I have problem spawning enemies in unity 2d. Enemies clones themselves too quickly and it lags. Here's my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemySpawner : MonoBehaviour
{
public Transform[] spawnPoints;
public GameObject enemy;
public float spawnTime = 5f;
public float spawnDelay = 3f;
// Use this for initialization
void Start () {
InvokeRepeating ("addEnemy", spawnDelay, spawnTime);
}
void addEnemy() {
int spawnPointIndex = Random.Range(0, spawnPoints.Length);
Instantiate (enemy, spawnPoints[spawnPointIndex].position,
spawnPoints[spawnPointIndex].rotation);
}
}
Oh, I am currently making a 2D game where I have to spawn enemies and here is what code I used, edited for you of course:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemySpawner : MonoBehaviour
{
public GameObject enemyPrefab;
public float interval = 100;
private float counter = 0;
// Update is called once per frame
void FixedUpdate()
{
counter += 1;
if(counter >= interval){
counter = 0;
Instantiate(enemyPrefab, transform.position,transform.rotation);
}
}
}
Simply create a new game object, put this script it, add the enemy prefab in the Game Object variable and you are set. You can edit the interval between each enemy with the interval variable. Good luck with your project :)
I'm assuming they are spawning more than every 3 seconds? Do you have multiple 'EnemySpawner' scripts attached to objects in the scene?
edit sounds like you have an ‘EnemySpawner’ script in your prefab, remove it from prefab and put it on a separate GameObject in your scene.
You should not as in the accepted answer use FixedUpdate for this! It uses 100 fixed update steps .. but who wants to calculate how long this actually takes?
By default fixed update step is 0.02s so 100 means 2 real-time seconds - but what if you change the fixed update step for any reason?
I would rather do it using a simple timer in Update:
public class EnemySpawner : MonoBehaviour
{
public GameObject enemyPrefab;
// In seconds
[SerializeField]private float interval = 2f;
private float timer = 0f;
// Update is called once per frame
void Update()
{
timer += Time.deltaTime;
if(timer >= interval){
timer = 0f;
Instantiate(enemyPrefab, transform.position,transform.rotation);
}
}
}
Actually what you had should also work! Depends of course also on the values you set via the Inspector.
BUT What I can see from the image you posted is: In enemy you referenced the object itself!
=> The next time there are 2 enemy instances including this spawner
=> The next time there are 4 enemy instances including this spawner
=> The next time there are 8 enemy instances ...
=> etc
Therefore they are called Enemy[Clone] then Enemy[Clone][Clone] then Enemy[Clone][Clone][Clone] etc so
Not sure if this was intended but I would rather put the spawner script on one single object in the scene and rather reference a prefab without an additional spawner script.
Related
I am trying to practice creating a clone of Galaga following the same ruleset as the original. I am currently stuck trying to attempt a limit on the amount of cloned prefabs that can be in the scene at any one time, in the same way that Galaga's projectiles are limited to 2 on screen at any time. I want to make it so the player can shoot up to two projectiles, which destroy after 2 seconds or when they collide (this part is functioning), followed by not being able to shoot if two projectile clones are active and not yet destroyed in the hierarchy (Not working as I can instantiate projectiles over the limit of 2).
I have combed through Google for about 3 hours with no solutions that have worked for me, at least in the ways that I had attempted to implement them.
Thank y'all so much for the help!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class playerController : MonoBehaviour
{
public float moveSpeed = 1.0f;
public playerProjectile projectile;
public Transform launchOffset;
public int maxBullets = 0;
private GameObject cloneProjectile;
public Rigidbody2D player;
// Start is called before the first frame update
void Start()
{
player = this.GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
MovePlayer();
PlayerShoot();
}
public void MovePlayer()
{
player.velocity = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical")) * moveSpeed;
}
public void PlayerShoot()
{
if (Input.GetKeyDown(KeyCode.Z))
{
var cloneProjectile = Instantiate(projectile, launchOffset.position, launchOffset.rotation);
maxBullets++;
if (maxBullets >= 3)
{
Destroy(cloneProjectile, 0.1f);
maxBullets --;
return;
}
}
}
}
You could change the logic up a bit. An instance of the playerController class is active as long as the game is active, so it will know and retain the value of 'maxBullets' until you die or exit the program.
So instead, every time you click "z", the first thing you should do is run the check. If the current amount of live projectiles equals the maximum, have the logic 'return' and exit out of the method.
This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 1 year ago.
So my game is a 2D top down movement game and my script does make my enemy attack but it constantly loops the attack animation because I obviously don't know what EXACTLY to put code wise to make the enemy attack when in range of the player to do damage instead of letting him constantly loop. Also i seem to be getting an error when i get close to my enemy as of right now it says
NullReferenceException: Object reference not set to an instance of an object
EnemyCombat.Attack () (at Assets/EnemyCombat.cs:36)
EnemyCombat.Update () (at Assets/EnemyCombat.cs:25)
Also, Here is the EnemyCombat script
enemy attausing System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyCombat : MonoBehaviour
{
public Animator animator;
public Transform AttackPoint;
public float attackRange = 0.5f;
public LayerMask enemyLayers;
public int attackDamage = 5;
public float attackRate = 2f;
float nextAttackTime = 0f;
// Update is called once per frame
void Update()
{
if (Time.time >= nextAttackTime)
{
Attack();
nextAttackTime = Time.time + 1f / attackRate;
}
}
void Attack()
{
animator.SetTrigger("Attack");
Collider2D[] hitEnemies = Physics2D.OverlapCircleAll(AttackPoint.position, attackRange, enemyLayers);
foreach (Collider2D enemy in hitEnemies)
{
enemy.GetComponent<Enemy>().TakeDamage(attackDamage);
}
}
void OnDrawGizmosSelected()
{
if (AttackPoint == null)
return;
Gizmos.DrawWireSphere(AttackPoint.position, attackRange);
}
}
To fix your endless attack loop:
// Update is called once per frame
void Update()
{
if (attackRate >= nextAttackTime) /* checks that nextAttackTime is less than or equal to the attackRate */
{
Attack();
nextAttackTime = Time.deltaTime * 5f; // adds 5 seconds to nextAttackTime
}
else
{
nextAttackTime -= Time.deltaTime; /* if nextAttackTime is greater than the attackRate, subtract one from nextAttackTime. this only happens once per second because you use Time.deltaTime */
}
}
From that NullReference Error it looks like the major problem you're having is that there is no actual point in the code or in the game hierarchy that you are telling your script which gameObject it is referring to.
Line 36 is trying to retrieve that information with .GetComponent<Enemy()>, so you need to provide that reference.
You could do this in the script fairly easily by creating a public variable that you can drag the enemy gameObject into in your hierarchy in Unity.
Try something like:
public GameObject enemyObject;
This will create a variable in the script visible in the hierarchy when you select the script which you can drag the appropriate gameObject into.
There might need to be some adjustments to it because I can't see the rest of your code, but this seems to be the issue.
Another option would be trying to manually adding it in:
void Start()
{
GameObject enemyObject = gameObject.GetComponent<Enemy>();
}
this is taken from Unity Scripting Documentation
I'm new, and I've got hard time to respawn my AI (right now he just a cube that follow my player) after he been destroy. I believe its because the script sits on the object that get destroyed. but what I need to do to respawn it?
(although I'm sure my respawn code is not good :\ (It's mobile-android project) )
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyTesting : MonoBehaviour
{
[SerializeField]
public GameObject player;
public GameObject enemy;
private Rigidbody body;
Vector3 accelerationDir;
// Use this for initialization
void Start()
{
body = GetComponent<Rigidbody>();
}
private void Update()
{
accelerationDir = Input.acceleration;
if (accelerationDir.sqrMagnitude>=5)
{
EnemyDead();
}
}
void EnemyDead()
{
Destroy(enemy);
Invoke("Respawn", 5);
}
void Respawn()
{
enemy = (GameObject)Instantiate(enemy);
enemy.transform.position = transform.position;
}
// Update is called once per frame
void FixedUpdate()
{
Vector3 toTarget = player.transform.position - transform.position;
float speed = 1.5f;
transform.Translate(toTarget * speed * Time.deltaTime);
}
}
Thanks very much!
I am supposing that your enemy GameObject is inside the scene holding your EnemyTesting MonoBehaviour instance (correct me if I'm wrong).
If this is the case, you cannot instantiate a gameObject that is destroyed.
As #derHugo pointed out, you should not use Destroy and Instantiate for your use case. It would be better to set inactive the enemy GameObject, move it to the position that you want, an (re)set it active. It will look like the enemy respawned.
If you want to dig the subject later, look at the object pooling game optimization pattern.
Otherwise, if you still want to use Instantiate for respawning, I would create a prefab of your enemy GameObject. The enemy GameObject referenced in the EnemyTesting field (in the Inspector view), would be your prefab from the project hierarchy instead of a GameObject inside the scene.
This way, you would be able to instantiate an enemy GameObject as many times as you want (and use it in other scenes!). Don't forget to hold a reference to the instantiated enemy GameObject so you can know which one you want to destroy. It would looke like this :
enemy = Instantiate(enemyPrefab, transform.position, transform.rotation);
You can replace transform with the transform of your choice, for example the transform of an empty enemyRespawnPoint GameObject in your Scene.
Do you see any error in the console ?
I almost have my game ready, I want to create an infinite number of 2D squares for my 2D game. However, the following code I used does not work to spawn a single square infinitely.
using UnityEngine;
using System.Collections;
public class Spawner : MonoBehaviour
{
private GameObject[] locationsToSpawn;
private float counter = 0;
[SerializeField]
string[] listOfPossibleTags;
[SerializeField]
GameObject[] objectToSpawn;
[SerializeField]
float timeBetweenSpawns = 3.0f;
void Start()
{
locationsToSpawn = GameObject.FindGameObjectsWithTag("SpawnLocation");
}
void Update()
{
counter += Time.deltaTime;
if (counter > timeBetweenSpawns)
{
GameObject spawnedObject;
spawnedObject = Instantiate(objectToSpawn[Random.Range(0, objectToSpawn.Length)], locationsToSpawn[Random.Range(0, locationsToSpawn.Length)].transform.position, Quaternion.identity) as GameObject;
spawnedObject.gameObject.tag = listOfPossibleTags[Random.Range(0, listOfPossibleTags.Length)];
counter = 0;
}
}
}
Also, my game looks like the following image
So, what can I do to create an infinite number of squares falling? I'm very close to finishing the game.
Based on the information you provided in your comments you are putting a script on a gameobject that doesn't exist in your scene. If there is no instance of your game object in the game your script will never run. Put the script on something else that makes sense and it will run.
When trying to figure out whats wrong with your code use the debugger. You will be able to find your problems much easier!
I'm working at an enemy spawn system. This is my code:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class EnemyManager : MonoBehaviour
{
public GameObject shark; // prefab von Shark
public GameObject instanceShark; // globale Instanzvariable von Shark
public Transform[] spawnPoints; // An array of the spawn points this enemy can spawn from.
public float spawnTime = 3f; // How long between each spawn.
public int maximumSharks = 2;
private int currentSharks;
public int healthShark; // current Health von Shark
public int startinghealthShark = 200;
public float sinkSpeed = 2.5f;
bool isDead;
void Start ()
{
healthShark = startinghealthShark;
currentSharks = 0;
}
void Update ()
{
if (currentSharks <= maximumSharks) {
InvokeRepeating ("Spawn", spawnTime, spawnTime);
}
Debug.Log (currentSharks);
}
void Spawn ()
{
// Find a random index between zero and one less than the number of spawn points.
int spawnPointIndex = Random.Range (0, spawnPoints.Length);
// Create an instance of the enemy prefab at the randomly selected spawn point's position and rotation.
instanceShark = Instantiate (shark, spawnPoints[spawnPointIndex].position, spawnPoints[spawnPointIndex].rotation) as GameObject;
currentSharks++;
if (currentSharks >= maximumSharks) {
CancelInvoke("Spawn");
}
}
public void AddDamageToShark (int neuDamageWert) // Addiere zu damage. Public function, können andre scripts auch aufrufen
{
// If the enemy is dead...
if(isDead)
// ... no need to take damage so exit the function.
return;
healthShark -= neuDamageWert;
if (healthShark <= 0) { //tot
Death ();
}
Debug.Log (healthShark);
}
void Death ()
{
// The enemy is dead.
isDead = true;
currentSharks--;
Destroy (instanceShark);
Debug.Log ("dead?");
return;
}
What I want: Spawn enemies as long as maximum amount isn't reached (this part works so far) and destroy the enemy that has been shot and respawn another one (doesn't work).
This codes creates 2 sharks as enemies at the moment. The problem is when I damage one, only the last created instance gets destroyed even though I shot at the first shark. Also the health of the other instance and new spawning instance isn't affected at all.
I'd appreciate any advices, I spent ages with this code and it seems like I need some help regarding the instances - health logic.
Thank you very much
Split your spawn manager behaviour and your enemy behaviour and use interfaces to organize your code in a more SOLID approach. Turn your objects responsible for just one scope (Now your SpawnManager is current responsible for Enemy behaviours/responsabilities)
SpawnManager should be a singleton object with just one responsibility "to manage enemy spawn" with a maximunEnemyCount and a currentEnemyCount. You can spawn always when your currentEnemyCount < maximunEnemyCount.
OnTakeDamage() and OnDeath() should be interfaces of your enemy behaviour (in a separated script from SpawnManager, lets assume EnemyBehaviour script) and your destroy should target just the instance of itself Destroy(this.gameObject)
Remember to notify your SpawnManager when an enemy is dead on your OnDeath method to adjust enemyCurrentCount.
I think this is a more elegant way to do this task and less susceptible to bugs on managing enemy instances.
edited:
linked singleton references that I forgot before
you'll need a unique gameObject for manager that should know how much enemies can live and how many are living, nothing more about the enemies (enemy's health is a responsibility of enemy object in this case).
each enemy need to know when he/she dies and so notify the manager to decrement its counter.