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.
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.
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.
I've created some boxes to recreate a 2D level where the dungeon is being created in a procedural generation. Altho when I run my script it creates the right levels, but it does not align the Spawnpoints to each other.
This causes an unlimited level generation (since the spawn points don't touch) but it also makes sure that there is no way for the character to go from 1 way to the other. I've made sure that every level is 10 x 10 and the spawn points are 10 pixels away from the middle of the rooms. I wanted to ensure collision between the spawn points but unfortunately, this is not the case.
I have created multiple rooms where each has their own exit (Spawn Point) depending on where the spawn point is, it can connect with another room that has the same spawn point (Room with a left exit is connected by a room with at least a right exit).
I put all of this in an int, declaring the number of rooms that are available.
Roomspawner code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RoomSpawner : MonoBehaviour
{
public int openingDirection;
// 1 --> need bottom door
// 2 --> need top door
// 3 --> need left door
// 4 --> need right door
private RoomTemplates templates;
private int rand;
private bool spawned = false;
void Start(){
templates = GameObject.FindGameObjectWithTag("Rooms").GetComponent<RoomTemplates>();
Invoke("Spawn", 0.5f);
}
void Spawn(){
if(spawned == false){
if(openingDirection == 1){
// Need to spawn a room with a BOTTOM door.
rand = Random.Range(0, templates.bottomRooms.Length);
Instantiate(templates.bottomRooms[rand], transform.position, templates.bottomRooms[rand].transform.rotation);
} else if(openingDirection == 2){
// Need to spawn a room with a TOP door.
rand = Random.Range(0, templates.topRooms.Length);
Instantiate(templates.topRooms[rand], transform.position, templates.topRooms[rand].transform.rotation);
} else if(openingDirection == 3){
// Need to spawn a room with a LEFT door.
rand = Random.Range(0, templates.leftRooms.Length);
Instantiate(templates.leftRooms[rand], transform.position, templates.leftRooms[rand].transform.rotation);
} else if(openingDirection == 4){
// Need to spawn a room with a RIGHT door.
rand = Random.Range(0, templates.rightRooms.Length);
Instantiate(templates.rightRooms[rand], transform.position, templates.rightRooms[rand].transform.rotation);
}
spawned = true;
}
void OnTriggerEnter2D(Collider2D other){
if(other.CompareTag("SpawnPoint")){
if(other.GetComponent<RoomSpawner>().spawned == false && spawned == false){
// spawns walls blocking off any opening !
Instantiate(templates.closedRoom, transform.position, Quaternion.identity);
Destroy(gameObject);
}
spawned = true;
}
}
}
}
Room templates
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RoomTemplates : MonoBehaviour
{
public GameObject[] bottomRooms;
public GameObject[] topRooms;
public GameObject[] leftRooms;
public GameObject[] rightRooms;
public GameObject closedRoom;
}
I did a new test with a room where it has only 1 spawn point. I Paused the game the second the 2nd spawn point was created so you can see that the 2nd level is created a with a different height than the starting one.
Underneath you can see the connecting room spawning just above the starting one. By doing this it messes up all the rest of the rooms.
Instead I want it to be like this
It seems to me like the problem arises due to the way you instantiate the copies of the rooms.
They are all instantiated at transform.position but given that they appear to spawn at all sorts of places, I'd suggest that you make sure that the pivot point/center point of the prefabs/meshes of your Room Templates are aligned correctly.
I'm writting a small 2D game in Unity with C#. I have built two obstaclespawner which spawn vertical lines. After the lines have been spawned, they move down. One of the spawner is located at the upper left edge and the other one at the upper right edge. Currently, new objects are spawned after a certain time. But my goal is, for example, to spawn a new object on the upper left edge when the object which is spawned on the top right has traveled a certain distance.
Could this possibly be done via the coordinates of the objects?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObstacleSpawner : MonoBehaviour
{
public GameObject[] obstacles;
public List<GameObject> obstaclesToSpawn = new List <GameObject>();
int index;
void Awake()
{
InitObstacles();
}
// Start is called before the first frame update
void Start()
{
StartCoroutine (SpawnRandomObstacle ());
}
// Initialize obstacles
void InitObstacles()
{
index=0;
for(int i =0; i<obstacles.Length*3;i++){
GameObject obj = Instantiate(obstacles[index], transform.position, Quaternion.identity);
obstaclesToSpawn.Add(obj);
obstaclesToSpawn [i].SetActive (false);
index++;
if (index == obstacles.Length)
{
index= 0;
}
}
}
IEnumerator SpawnRandomObstacle()
{
//Wait a certain time
yield return new WaitForSeconds(3f);
}
//I want something like this
(if gameObject.x == -0.99){
//activate obstacles
int index = Random.Range(0, obstaclesToSpawn.Count);
while(true){
if (!obstaclesToSpawn[index].activeInHierarchy){
obstaclesToSpawn[index].SetActive(true);
obstaclesToSpawn [index].transform.position = transform.position;
break;
}else{
index = Random.Range (0, obstaclesToSpawn.Count);
}
}
StartCoroutine (SpawnRandomObstacle ());
}
}
As far as I understand, you need to save in each spawner the reference to other spawner.
public class ObstacleSpawner : MonoBehaviour
{
public ObstacleSpawner otherSpawner;
...
Then in spawner check the position of obstacle in the second spawner. Something like this:
...
if (otherSpawner.obstaclesToSpawn[someIndex].transform.position.x <= -0.99)
{
// Spawn new obstacle in this spawner...
...
}
Comparing position of an object to a certain position in the world is something that might work for you right now but may cause problems in the future if you ever try to change anything in the way your scene is set up.
You're looking for distance travelled by an object and you have everything necessary to calculate said distance.
The starting point for all your obstacles spawned by an ObstacleSpawner is the position of the ObstacleSpawner object so you don't need to cache the spawn position which makes things a lot easier.
You need a variable defining the distance after which you want to spawn another obstacle something like public float distBeforeNextObstacle = 1f, then you can compare this distance against the obstacle's distance from its spawn position (use Vector3 or Vector2, both have a Distance method, you should choose whatever fits your game best):
if(Vector3.Distance(obstaclesToSpawn[index].transform.position, transform.position)>=distBeforeNextObstacle)
{
//Spawn next obstacle
}
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!