How can I generate enemies in random time (same position) in Unity 3D using C#? - c#

I generate repeatedly enemies each 1.75f. But I don't know how can I use a Random function. My prototype game is like the game in Chrome Browser,It appears when the page is not found.
Thank you for helping me.
Here is my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyGeneratorController : MonoBehaviour
{
public GameObject enemyPrefeb;
public float generatorTimer = 1.75f;
void Start ()
{
}
void Update ()
{
}
void CreateEnemy()
{
Instantiate (enemyPrefeb, transform.position, Quaternion.identity);
}
public void StartGenerator()
{
InvokeRepeating ("CreateEnemy", 0f, generatorTimer);
}
public void CancelGenerator(bool clean = false)
{
CancelInvoke ("CreateEnemy");
if (clean)
{
Object[] allEnemies = GameObject.FindGameObjectsWithTag ("Enemy");
foreach (GameObject enemy in allEnemies)
{
Destroy(enemy);
}
}
}
}

You can use StartCoroutine for simple enemy instantiating:
using System.Collections;
...
private IEnumerator EnemyGenerator()
{
while (true)
{
Vector3 randPosition = transform.position + (Vector3.up * Random.value); //Example of randomizing
Instantiate (enemyPrefeb, randPosition, Quaternion.identity);
yield return new WaitForSeconds(generatorTimer);
}
}
public void StartGenerator()
{
StartCoroutine(EnemyGenerator());
}
public void StopGenerator()
{
StopAllCoroutines();
}
And, as Andrew Meservy said, if you wanna add the randomness to the timer (for example to make spawn delay random from 0.5 sec to 2.0 sec) then you can just replace yield return to this one:
yield return new WaitForSeconds(Mathf.Lerp(0.5f, 2.0f, Random.value));

Modified version to generate the enemies
Use StartCoroutine with Random Time
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyGeneratorController : MonoBehaviour
{
public GameObject enemyPrefeb;
public float generatorTimer { set; get; }
void Start ()
{
generatorTimer = 1.75f;
}
void Update ()
{
}
void IEnumerator CreateEnemy()
{
Instantiate (enemyPrefeb, transform.position, Quaternion.identity);
yield return new WaitForSeconds(generatorTimer);
generatorTimer = Random.Range(1f, 5f);
}
public void StartGenerator()
{
StartCoroutine(CreateEnemy());
}
public void CancelGenerator(bool clean = false)
{
CancelInvoke ("CreateEnemy");
if (clean)
{
Object[] allEnemies = GameObject.FindGameObjectsWithTag ("Enemy");
foreach (GameObject enemy in allEnemies)
{
Destroy(enemy);
}
}
}
}

Related

Find and return nearest gameObject with tag, Unity

im trying to make a game where you have to merge 2 gameObjects to get the next one and so one. First i tried so that when the gameobject is released and it's touching another gameobject it deletes both of those and then instantiates the next one. But this didn't quite work bc of the triggers "covering" each other or some other wierd but. So i thought i could detect the collision so that every gameobject that touches the trigger gets added to a list and then removed on exit. What i'm not trying to figure out is how to calculate the distance between the gameObjects and if they are close enough (mergingthreshold) then they both get destryed and the next gameobject gets instantiated, but i can't quite figure out how to get this distance calculating and returning of what gameObject that is. So all help would be appreciated! (don't leave out any details, cause I'm quite the beginner)
Thanks!
Here's the code I've gotten so far:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Merging : MonoBehaviour
{
List<GameObject> NearGameobjects = new List<GameObject>();
void Start()
{
}
void Update()
{
}
void OnTriggerEnter2D(Collider2D col)
{
if (col.gameObject.tag == gameObject.tag)
{
Debug.Log("Enter!");
NearGameobjects.Add(col.gameObject);
}
}
void OnTriggerExit2D(Collider2D col)
{
if (col.gameObject.tag == gameObject.tag)
{
Debug.Log("Exit!");
NearGameobjects.Remove(col.gameObject);
}
}
}
If you're going to use the list and have added all objects into this list you could use a for each loop and check the distance between your main object and all the other objects in the list if its closer add that to the closets object
public class NewBehaviourScript : MonoBehaviour {
List<GameObject> NearGameobjects = new List<GameObject>();
GameObject closetsObject;
private float oldDistance = 9999;
private void Something()
{
foreach (GameObject g in NearGameobjects)
{
float dist = Vector3.Distance(this.gameObject.transform.position, g.transform.position);
if (dist < oldDistance)
{
closetsObject = g;
oldDistance = dist;
}
}
}
}
You could just order your list by the distance using Linq OrderBy:
using SystemLinq;
...
public void CheckNearest()
{
if(NearGameobjects.Count == 0) return;
// This orders the list so the closest object will be the very first entry
var sorted = NearGameobjects.OrderBy(obj => (col.transform.position - transform.position).sqrMagnitude);
// currently closest
var closest = sorted.First();
if (Vector3.Distance(this.gameObject.transform.position, closest.transform.position) < mergingThreshold)
{
Destroy(gameObject);
Destroy(closetsObject);
Instantiate(NextPF, transform.position, Quaternion.identity);
}
}
private void OnTriggerEnter2D(Collider2D col)
{
// Rather use CompareTag
if (col.CompareTag(tag))
{
Debug.Log("Enter!");
NearGameobjects.Add(col.gameObject);
}
}
private void OnTriggerExit2D(Collider2D col)
{
if (col.CompareTag(tag))
{
Debug.Log("Exit!");
NearGameobjects.Remove(col.gameObject);
}
}
For anyone wondering, this was the total code that worked perfectly thanks to CubeCrafter360!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Timeline;
public class Merging : MonoBehaviour
{
List<GameObject> NearGameobjects = new List<GameObject>();
GameObject closetsObject;
private float oldDistance = 9999;
public GameObject NextPF;
public float mergingThreshold = 0.3f;
void OnTriggerEnter2D(Collider2D col)
{
if (col.gameObject.tag == gameObject.tag)
{
Debug.Log("Enter!");
NearGameobjects.Add(col.gameObject);
}
}
void OnTriggerExit2D(Collider2D col)
{
if (col.gameObject.tag == gameObject.tag)
{
Debug.Log("Exit!");
NearGameobjects.Remove(col.gameObject);
}
}
public void CheckNearest()
{
foreach (GameObject g in NearGameobjects)
{
float dist = Vector3.Distance(this.gameObject.transform.position, g.transform.position);
if (dist < oldDistance)
{
closetsObject = g;
oldDistance = dist;
}
}
if (Vector3.Distance(this.gameObject.transform.position, closetsObject.transform.position) < mergingThreshold)
{
Destroy(gameObject);
Destroy(closetsObject);
Instantiate(NextPF, transform.position, Quaternion.identity);
}
}
}
And then i just called the function in my movement script:
private void OnMouseUp()
{
GetComponent<Merging>().CheckNearest();
}

When I used portal in My Game why Destroyed "Camera Fallow"?

I made a portal to the 2D game. Normally the camera needs to follow the character. But after the portal scripts I wrote, "CameraFallowScript" does not work. The character is passing through the portal. but after passing "CameraFallowScript" disappears. I'm a little new and my English is bad.
thanks for helping.
Camera Fallow Script :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraFallow : MonoBehaviour
{
public GameObject target;
void Start()
{
}
// Update is called once per frame
void Update()
{
transform.position = new Vector3(target.transform.position.x, target.transform.position.y, transform.position.z);
}
}
Portal Script here :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Portal : MonoBehaviour
{
private Rigidbody2D enteredRigidbody;
private float enterVelocity, exitVelocity;
private void OnTriggerEnter2D(Collider2D collision)
{
enteredRigidbody = collision.gameObject.GetComponent<Rigidbody2D>();
enterVelocity = enteredRigidbody.velocity.x;
if (gameObject.name == "BluePortal")
{
PortalControl.portalControlInstance.DisableCollider("orange");
PortalControl.portalControlInstance.CreateClone("atOrange");
}
else if (gameObject.name == "OrangePortal")
{
{
PortalControl.portalControlInstance.DisableCollider("blue");
PortalControl.portalControlInstance.CreateClone("atBlue");
}
}
}
private void OnTriggerExit2D(Collider2D collision)
{
exitVelocity = enteredRigidbody.velocity.x;
if (enterVelocity != exitVelocity)
{
Destroy(GameObject.Find("Clone"));
}
Destroy(collision.gameObject);
PortalControl.portalControlInstance.EnableColliders();
GameObject.Find("Clone").name = "Character";
CameraFallow.DontDestroyOnLoad(transform.gameObject);
}
}
PortalControl Script Here :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PortalControl : MonoBehaviour
{
public static PortalControl portalControlInstance;
[SerializeField]
private GameObject bluePortal, orangePortal;
[SerializeField]
private Transform bluePortalSpawnPoint, orangePortalSpawnPoint;
private Collider2D bluePortalCollider, orangePortalCollider;
[SerializeField]
private GameObject clone;
void Start()
{
portalControlInstance = this;
bluePortalCollider = bluePortal.GetComponent<Collider2D>();
orangePortalCollider = orangePortal.GetComponent<Collider2D>();
}
// Update is called once per frame
public void CreateClone(string whereToCreate)
{
if (whereToCreate == "atBlue")
{
var instantiatedClone = Instantiate(clone, bluePortalSpawnPoint.position, Quaternion.identity);
instantiatedClone.gameObject.name = "clone";
}
if (whereToCreate == "atOrange")
{
var instantiatedClone = Instantiate(clone, orangePortalSpawnPoint.position, Quaternion.identity);
instantiatedClone.gameObject.name = "clone";
}
}
public void DisableCollider(string ColliderToDisable)
{
if (ColliderToDisable == "orange")
{
orangePortalCollider.enabled = false;
}
else if (ColliderToDisable == "blue")
{
bluePortalCollider.enabled = false;
}
}
public void EnableColliders()
{
orangePortalCollider.enabled = true;
bluePortalCollider.enabled = true;
}
}
In general I wouldn't create clone I guess ... why instantiate a new player? Why not simply move it to the new position?
What happens here is that after Destroy(collision.gameobject) the FollowCamera loses the reference to its target so you would need to reassign it after the cloning e.g. in
private void OnTriggerExit2D(Collider2D collision)
{
exitVelocity = enteredRigidbody.velocity.x;
if (enterVelocity != exitVelocity)
{
Destroy(GameObject.Find("Clone"));
}
Destroy(collision.gameObject);
PortalControl.portalControlInstance.EnableColliders();
var clone = GameObject.Find("Clone");
clone.name = "Character";
DontDestroyOnLoad(clone);
// It would ofcourse be way more efficient
// if you would store this reference somewhere
FindObjectOfType<CameraFollow>().target = clone;
}
Note that in general usage of Find is expensive and you should avoid it wherever possible! Rather pass on the clone reference between all required scripts.
Regarding coding style: passing around string parameters is not really good code.
I would suggest e.g. an enum like
public enum PortalSide
{
orange,
blue
}
and then use
private Dictionary<PortalSide, Transform> portalSpawns;
private Dictionary<PortalSide, Collider> portalColliders;
private void Awake()
{
portalSpawns = new Dictionary<PortalSide, Transform> { {PortalSide.blue, bluePortalSpawnPoint} , {PortalSide.orange, orangePortalSpawnPoint}};
portalColliders = new Dictionary<PortalSide, Collider> { {PortalSide.blue, bluePortalCollider}, {PortalSide.orange, orangePortalCollider} };
}
public void CreateClone(PortalSide whereToCreate)
{
var spawnPoint = PortalSides[whereToCreate];
var instantiatedClone = Instantiate(clone, spawnPoint.position, Quaternion.identity);
instantiatedClone.gameObject.name = "clone";
}
public void DisableCollider(PortalSide ColliderToDisable)
{
var colliderToDisable = portalColliders[ColliderToDisable];
colliderToDisable.enabled = false;
}

Unity2D: Fixing colliding for inventory

My goal was to make my character pickup item on collider (2D) didn't work.
So here is what I've tried:
Player Controller Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public float moveSpeed = 2f;
public Inventory inventory;
void Start()
{
}
public bool isGrounded;
public LayerMask groundLayers;
void Update()
{
// isgrounded?
isGrounded = Physics2D.OverlapArea(new Vector2(transform.position.x -
0.2f, transform.position.y - 0.2f),
new Vector2(transform.position.x + 0.2f, transform.position.y -
0.21f), groundLayers);
Jump();
Vector3 movement = new Vector3(Input.GetAxis("Horizontal"), 0f, 0f);
transform.position += movement * Time.deltaTime * moveSpeed;
}
void Jump()
{
if(Input.GetButtonDown("Jump") && isGrounded)
{
gameObject.GetComponent<Rigidbody2D>().AddForce(new Vector2(0f,
2.5f), ForceMode2D.Impulse);
}
}
private void OnCollisionEnter2D(ControllerColliderHit hit)
{
IInventoryItem item = hit.collider.GetComponent<IInventoryItem>();
if (item != null)
{
inventory.AddItem(item);
}
}
}
HUD SCRIPT:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class HUD : MonoBehaviour
{
public Inventory Inventory;
void Start ()
{
Inventory.ItemAdded += InventoryScript_ItemAdded;
}
private void InventoryScript_ItemAdded(object sender, InventoryEventArgs
e)
{
Transform inventoryPanel = transform.Find("InventoryPanel");
foreach(Transform slot in inventoryPanel)
{
// Border... Image
Image image = slot.GetChild(0).GetChild(0).GetComponent<Image>();
// We found empty slot!
if (!image.enabled)
{
image.enabled = true;
image.sprite = e.Item.Image;
// Todo store a reference;
break;
}
}
}
}
Inventory Script:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Inventory : MonoBehaviour
{
private const int SLOTS = 7;
private List<IInventoryItem> mItems = new List<IInventoryItem>();
public event EventHandler<InventoryEventArgs> ItemAdded;
public void AddItem(IInventoryItem item)
{
if(mItems.Count < SLOTS)
{
Collider collider = (item as MonoBehaviour).GetComponent<Collider>
();
if (collider.enabled)
{
collider.enabled = false;
mItems.Add(item);
item.OnPickup();
if (ItemAdded != null)
{
ItemAdded(this, new InventoryEventArgs(item));
}
}
}
}
}
Inventory Item Script:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface IInventoryItem
{
string Name { get; }
Sprite Image { get; }
void OnPickup();
}
public class InventoryEventArgs : EventArgs
{
public InventoryEventArgs(IInventoryItem item)
{
Item = item;
}
public IInventoryItem Item;
}
Rock Script (The object):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Rock : MonoBehaviour, IInventoryItem
{
public string Name
{
get
{
return "Rock";
}
}
public Sprite _Image = null;
public Sprite Image
{
get
{
return _Image;
}
}
public void OnPickup()
{
// TODO: ADD LOGIC THAT WILL MAKE THE ROCK A 'WEAPON' TO CUT DOWN THE
TREE
gameObject.SetActive(false);
}
}
All of those scripts work, but whenever I join my game and Collide the player with the object (all 2d, 2D Box colliders, etc.) the character wont pick the item up and put it in it's inventory?
The scripts are referenced to each other.
What did I do wrong?
Physics 2D Screenshot:
Player inspector screenshot:
Rock (Object that needs to join his inventory)
One thing I noticed is that you are mixing the syntax of two events to create one that doesn't exist. void OnCollisionEnter2D(ControllerColliderHit hit) is not a built-in event in Unity. You probably mean to use void OnCollisionEnter2D(Collision2D hit):
private void OnCollisionEnter2D(Collision2D hit)
{
IInventoryItem item = hit.collider.GetComponent<IInventoryItem>();
if (item != null)
{
inventory.AddItem(item);
}
}
Another thing is that BoxCollider2D does not inherit from Collider. So, in AddItem, you should look for a Collider2D component instead:
public void AddItem(IInventoryItem item)
{
if(mItems.Count < SLOTS)
{
Collider2D collider = (item as MonoBehaviour).GetComponent<Collider2D>();
if (collider.enabled)
{
collider.enabled = false;
mItems.Add(item);
item.OnPickup();
if (ItemAdded != null)
{
ItemAdded(this, new InventoryEventArgs(item));
}
}
}
}
Consider this to be a partial solution because this may not capture all the changes needed... Let me know if this alone doesn't fix the problem in the comments below.

Character does not "see" the player (3D Game)

I am following this unity 3D course. I followed every single step of the part called "Enemies Part 1: Static Observers", and after re-checking the code and doing researches for a day, I still did not find the problem. The scope of this part of the tutorial is to make that when the "Gargoyle" sees the player, when passing in front of him, should restart the game.
These are the two scripts that should make this work, but don't.
Observer (Gargoyle):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Observer : MonoBehaviour
{
public Transform player;
public GameEnding gameEnding;
bool m_IsPlayerInRange;
void OnTriggerEvent(Collider other)
{
if (other.transform == player)
{
m_IsPlayerInRange = true;
}
}
void OnTriggerExit(Collider other)
{
if (other.transform == player)
{
m_IsPlayerInRange = false;
}
}
void Update()
{
if (m_IsPlayerInRange)
{
Vector3 direction = player.position - transform.position + Vector3.up;
Ray ray = new Ray(transform.position, direction);
RaycastHit raycastHit;
if (Physics.Raycast(ray, out raycastHit))
{
if (raycastHit.collider.transform == player)
{
gameEnding.CaughtPlayer();
}
}
}
}
}
And this is the GameEnding script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class GameEnding : MonoBehaviour
{
public float fadeDuration = 1f;
public float displayImageDuration = 1f;
public GameObject player;
public CanvasGroup exitBackgroundImageCanvasGroup;
public CanvasGroup caughtBackgroundImageCanvasGroup;
bool m_IsPlayerAtExit;
bool m_IsPlayerCaught;
float m_Timer;
void OnTriggerEnter(Collider other)
{
if (other.gameObject == player)
{
m_IsPlayerAtExit = true;
}
}
public void CaughtPlayer()
{
m_IsPlayerCaught = true;
}
void Update()
{
if (m_IsPlayerAtExit)
{
EndLevel(exitBackgroundImageCanvasGroup, false);
}
else if (m_IsPlayerCaught)
{
EndLevel(caughtBackgroundImageCanvasGroup, true);
}
}
void EndLevel(CanvasGroup imageCanvasGroup, bool doRestart)
{
m_Timer += Time.deltaTime;
imageCanvasGroup.alpha = m_Timer / fadeDuration;
if (m_Timer > fadeDuration + displayImageDuration)
{
if (doRestart)
{
SceneManager.LoadScene(0);
}
else
{
Application.Quit();
}
}
}
}
Back to the unity editor, I set the variables (player, gameending, exitimagebackground and caught imagebackground.
Does anybody know what the problem is and could help me out?
Thank you!
Edit:
these are the components of the Player Character:
and these of the Gargoyle:
Which has these as children:
which have these other components:
On your Observer class,
void OnTriggerEvent(Collider other)
{
if (other.transform == player)
{
m_IsPlayerInRange = true;
}
}
The function name is OnTriggerEvent(Collider), it should be OnTrigger**Enter**(Collider) instead.
Otherwise, it should work as intended.

Particle System not working on multiple enemies

I have a particle system for when the enemy is destroyed. I have multiple enemies coming from the same path (using the same prefab) and the particle system is only working on the first enemy. Can someone tell me why it is not working on the others as well? Thank you.
EnemyShooting scrip (this is where the piece of code is for the explosion):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyShooting : MonoBehaviour {
[SerializeField] float EnemyLaserSpeed = 10f;
[SerializeField] float EnemyLaserFireTime;
[SerializeField] GameObject LaserBulletEnemyPreFab;
[SerializeField] int MaxNumberOfHits = 1;
public Transform explosion;
int CurrentNumberOfHits = 0;
Coroutine FireCoroutine;
void OnTriggerEnter2D(Collider2D collider)
{
if(collider.gameObject.tag == "PlayerLaser")
{
if (CurrentNumberOfHits < MaxNumberOfHits)
{
CurrentNumberOfHits++;
Destroy(collider.gameObject);
Score.ScoreValue += 2;//The user will be rewarded 1 point
}
if (explosion)//EXPLOSION CODE
{
GameObject exploder = ((Transform)Instantiate(explosion, this.transform.position, this.transform.rotation)).gameObject;
Destroy(exploder, 2.0f);
}
}
}
void DestroyEnemy()
{
if(CurrentNumberOfHits >= MaxNumberOfHits)
{
Destroy(gameObject);
EnemySpawner.Instance.OnEnemyDeath(); // Tell the EnemySpawner that someone died
}
}
private void Fire()
{
FireCoroutine = StartCoroutine(ShootContinuously());
}
void BecomeVisible()
{
Fire();
}
IEnumerator ShootContinuously()
{
while (true)
{
GameObject LaserBulletEnemy = Instantiate(LaserBulletEnemyPreFab, this.transform.position, Quaternion.identity) as GameObject;
LaserBulletEnemy.GetComponent<Rigidbody2D>().velocity = new Vector2(0, EnemyLaserSpeed);
EnemyLaserFireTime = Random.Range(0.5f, 0.9f);
yield return new WaitForSeconds(EnemyLaserFireTime);
}
}
// Use this for initialization
void Start () {
BecomeVisible();
}
// Update is called once per frame
void Update () {
DestroyEnemy();
}
}
EnemySpawner : (I thought this script might help in a way so I attached it)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class EnemySpawner : MonoBehaviour
{
[SerializeField] GameObject EnemyPreFab;
[SerializeField] int MaxEnemies = 30;
[SerializeField] float EnemySpawnTime = 1.00001f;
[SerializeField] GameObject FirstWaypoint;
int CurrentNumOfEnemies = 0;
public int EnemiesToNextLevel = 7;
public int KilledEnemies = 0;
public LevelManager myLevelManager;
public static EnemySpawner Instance = null;
int timesEnemyHit;
IEnumerator SpawningEnemies()
{
while (CurrentNumOfEnemies <= MaxEnemies)
{
GameObject Enemy = Instantiate(EnemyPreFab, this.transform.position, Quaternion.identity);
CurrentNumOfEnemies++;
yield return new WaitForSeconds(EnemySpawnTime);
}
}
void Start()
{
if (Instance == null)
Instance = this;
StartCoroutine(SpawningEnemies());
timesEnemyHit = 0;
if (this.gameObject.tag == "EnemyHit")
{
CurrentNumOfEnemies++;
}
}
public void OnEnemyDeath()
{
CurrentNumOfEnemies--;
/*
if (CurrentNumOfEnemies < 5)
{
// You killed everyone, change scene:
LaserLevelManager.LoadLevel("NextLevelMenu");
}
*/
KilledEnemies++;
if (KilledEnemies >= EnemiesToNextLevel)
{
LaserLevelManager.LoadLevel("NextLevelMenu");
}
}
}

Categories