Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
0
Question by GlitteringStone · Jul 01, 2013 at 04:54 AM · enemytimespawnefficiency

More Efficient Enemy Spawning Than This?

I'm making a bullet hell game (shoot-em-up with a TON of bullets), and I'm wondering if there's a more efficient way of spawning enemies over time than what I'm doing right now.

Basically I want to spawn various waves of enemies, like enemyOne every second for five seconds, enemyTwo every second for ten seconds, stuff like that.

What I'm doing right now is having a bunch of if statements and executing the code when the current time is between two values, but is there a more efficient way of doing that? It's just really tedious to have to redo all these if statements every time I want a new wave of enemies.

 if (Time.time <= 5.0f) {
             if (Time.time > nextSpawn) {
                 nextSpawn = Time.time + 1.0f;
                 GameObject enemy = Instantiate(enemyOne, new Vector3(-1400f, 0, -1500f), Quaternion.identity) as GameObject;
                 enemy.GetComponent<enemyOne>().setID(enemyOneCounter);
                 enemyOneCounter++;
             }
         }
         if (Time.time <= 10.0f && Time.time >= 5.0f) {
             if (Time.time > nextSpawn) {
                 nextSpawn = Time.time + 1.0f;
                 GameObject enemy = Instantiate(enemyOne, new Vector3(1400f, 0, -1500f), Quaternion.identity) as GameObject;
                 enemy.GetComponent<enemyOne>().setID(enemyOneCounter);
                 enemyOneCounter++;
             }
         }
         if (Time.time <= 25.0f && Time.time >= 10.0f) {
             if (Time.time > nextSpawn) {
                 nextSpawn = Time.time + 5.0f;
                 GameObject enemy = Instantiate(enemyTwo, new Vector3(0, 0, -1950f), Quaternion.identity) as GameObject;
                 enemy.GetComponent<enemyTwo>().setID(enemyTwoCounter);
                 enemyTwoCounter++;
             }
         }
         if (Time.time <= 30.0f && Time.time >= 25.0f) {
             if (Time.time > nextSpawn) {
                 nextSpawn = Time.time + 5.0f;
                 GameObject enemy = Instantiate(enemyTwo, new Vector3(0, 0, -1950f), Quaternion.identity) as GameObject;
                 enemy.GetComponent<enemyTwo>().setID(enemyTwoCounter);
                 enemyTwoCounter++;
                 GameObject enemya = Instantiate(enemyTwo, new Vector3(0, 0, -1950f), Quaternion.identity) as GameObject;
                 enemya.GetComponent<enemyTwo>().setID(enemyTwoCounter);
                 enemyTwoCounter++;
                 GameObject enemyb = Instantiate(enemyTwo, new Vector3(0, 0, -1950f), Quaternion.identity) as GameObject;
                 enemyb.GetComponent<enemyTwo>().setID(enemyTwoCounter);
                 enemyTwoCounter++;
             }
         }
         if (Time.time <= 33.0f) {
             if (Time.time > nextSpawn) {
                 nextSpawn = Time.time + 3.0f;
                 GameObject enemy = Instantiate(enemyThree, new Vector3(-1400f, 0, -2000f), Quaternion.identity) as GameObject;
                 enemy.GetComponent<enemyThree>().setID(enemyThreeCounter);
                 enemyThreeCounter++;
             }
         }
         if (Time.time <= 36.0f && Time.time >= 33.0f) {
             if (Time.time > nextSpawn) {
                 nextSpawn = Time.time + 3.0f;
                 GameObject enemy = Instantiate(enemyThree, new Vector3(1400f, 0, -2000f), Quaternion.identity) as GameObject;
                 enemy.GetComponent<enemyThree>().setID(enemyThreeCounter);
                 enemyThreeCounter++;
             }
         }
         if (Time.time <= 39.0f && Time.time >= 36.0f) {
             if (Time.time > nextSpawn) {
                 nextSpawn = Time.time + 3.0f;
                 GameObject enemy = Instantiate(enemyThree, new Vector3(0, 0, -2000f), Quaternion.identity) as GameObject;
                 enemy.GetComponent<enemyThree>().setID(enemyThreeCounter);
                 enemyThreeCounter++;
             }
         }
         if (Time.time <= 45.0f && Time.time >= 39.0f) {
             if (Time.time > nextSpawn) {
                 nextSpawn = Time.time + 6.0f;
                 GameObject enemy = Instantiate(enemyThree, new Vector3(-1400f, 0, -2000f), Quaternion.identity) as GameObject;
                 enemy.GetComponent<enemyThree>().setID(enemyThreeCounter);
                 enemyThreeCounter++;
                 GameObject enemya = Instantiate(enemyThree, new Vector3(1400f, 0, -2000f), Quaternion.identity) as GameObject;
                 enemya.GetComponent<enemyThree>().setID(enemyThreeCounter);
                 enemyThreeCounter++;
                 GameObject enemyb = Instantiate(enemyThree, new Vector3(0, 0, -2000f), Quaternion.identity) as GameObject;
                 enemyb.GetComponent<enemyThree>().setID(enemyThreeCounter);
                 enemyThreeCounter++;
             }
         }
Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

2 Replies

· Add your reply
  • Sort: 
avatar image
0

Answer by iwaldrop · Jul 01, 2013 at 07:00 AM

From the perspectives of performance, and writing DRY code, we can certainly improve upon what you have here. DRY is an acronym for "Don't repeat yourself", and is a very good practice for novice programmers. Let's look at how you might be able to make this more efficient and maintainable.

First I'm assuming this is in an update loop. Bad idea. Every frame that this script is running it will check all of your conditionals. And because they're AND operations that's actually quite a lot of stuff to do every frame for (99.999% of the time) no reason. Sure, the computer can do it all in a fraction of a second, but why waste the time?

Secondly, you're hardcoding values that you should either declare as constants or expose in the inspector for tweaking.

I don't see what you're doing with your enemies, so I'm just providing a more efficient and maintainable version of the code you've posted, and kind of extrapolating out a couple of details. If you have questions related to my answer here, please update your question and leave a comment so I get an email letting me know.

 using UnityEngine;
 using System.Collections;
 using System.Collections.Generic;
 using System;
 
 public class EnemySpawnManager : MonoBehaviour
 {
     enum EnemyType { One, Two, Three }
     
     public class Enemy : MonoBehaviour { public virtual void KillNow(){} }
     public class EnemyOne : Enemy {}
     public class EnemyTwo : Enemy {}
     public class EnemyThree : Enemy {}
     
     #region Attributes
     public GameObject[] enemyPrefabs;
     public float[] waveTimers;
     #endregion
     
     #region Unity
     void Awake()
     {
         // initialize dictionary and lists
         activeEnemies = new Dictionary<EnemyType, List<GameObject>>();
         activeEnemies[EnemyType.One] = new List<GameObject>();
         activeEnemies[EnemyType.Two] = new List<GameObject>();
         activeEnemies[EnemyType.Three] = new List<GameObject>();
         
         // kick off spawning
         EnemySpawnTimer();
     }
     #endregion
     
     #region Public
     public int GetEnemyId(Enemy enemy)
     {
         int index = -1;
         GetEnemyTypeList(enemy, (list) =>
         {
             if (list != null)
                 index = list.IndexOf(enemy.gameObject);
         });
         return index;
     }
 
     public void KillEnemy(Enemy enemy)
     {
         GetEnemyTypeList(enemy, (list) => list.Remove(enemy.gameObject));
         enemy.KillNow();
     }
     #endregion
     
     #region Private
     private Dictionary<EnemyType, List<GameObject>> activeEnemies;
     private int waveCount;
     private Dictionary <Type, Func<EnemyType>> switchType = new Dictionary<Type, Func<EnemyType>>()
     {
         { typeof(EnemyOne), () => { return EnemyType.One; } },
         { typeof(EnemyTwo), () => { return EnemyType.Two; } },
         { typeof(EnemyThree), () => { return EnemyType.Three; } },
     };
     
     void EnemySpawnTimer()
     {
         EnemyType type = EnemyType.One;
         int numberToSpawn = 0;
         switch(waveCount)
         {
         case 0:
             numberToSpawn = 1;
             break;
         case 1:
             type = EnemyType.Two;
             numberToSpawn = 1;
             break;
         case 2:
             type = EnemyType.Two;
             numberToSpawn = 2;
             break;
         case 3:
             type = EnemyType.Three;
             numberToSpawn = 3;
             break;
         }
         
         for (int i = 0; i <= numberToSpawn; i++)
             activeEnemies[type].Add(SpawnEnemy(type));
         Invoke("EnemySpawnTimer", waveTimers[waveCount++]);
     }
 
     void GetEnemyTypeList(Enemy enemy, Action<List <GameObject>> action)
     {
         GameObject go = enemy.gameObject;
         EnemyType type = switchType[enemy.GetType()]();
         if (activeEnemies[type].Contains(go))
             action(activeEnemies[type]);
     }
     
     GameObject SpawnEnemy(EnemyType enemyType)
     {
         return Instantiate(enemyPrefabs[(int)enemyType], new Vector3(-1400f, 0, -1500f), Quaternion.identity) as GameObject;
     }
     #endregion
 }
 

This way you can just ask the EnemySpawnManager for an enemy's id, plus you can kill enemies indirectly as well (like if you're resetting a level or something). Even this could be more efficient, but at least we're not repeating ourselves any longer, and we've eliminated the update loop. I hate update loops, and you should too. :)

Comment
Add comment · Show 2 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image GlitteringStone · Jul 01, 2013 at 09:23 AM 0
Share

I actually just discovered coroutines, would that be an efficient solution too? Like just have one coroutine to generate the entire level, and start the coroutine in the Start() function?

avatar image iwaldrop · Jul 01, 2013 at 10:51 PM 0
Share

An Invoke works, essentially, the same way as a Coroutine, except it's a bit more flexible, I$$anonymous$$O. In this case a Coroutine would have to repeat a bunch of code, whereas an Invoke runs the same code each time it's called. Your call, really, but I wouldn't use one here.

avatar image
0

Answer by Immanuel-Scholz · Jul 01, 2013 at 07:20 AM

I don't share iwaldrop's hate for Update functions and recommend neither should you :P. IMHO, having lots of "global managers" around is definetely going to hurt you more.

Anyway, I'd also restructure stuff a bit to make it more friendly to tweak in the inspector. You can either assemble your data structures in Awake() (as iwaldrop did with the dictionary), but I'd just go with the original data, just to be carefull that you can serialize your data. If you aren't into Editor-writing, use the following rules of thumb:

  • Use arrays

  • Make simple classes (no structs!) with some plain public values

  • Mark your classes [System.Serializable]

What I mean is something like this (written without any testing, so fix my typos first ;)):

 [System.Serializable]
 class SpawnEntry
 {
     public float startSpawnTime, endSpawnTime, spawnInterval;
     public GameObject enemyToSpawn;
     public int amountPerSpawn;
     public Vector3 spawnPos;
     [HideInInspector]
     public float nextSpawn; // timer counting to 0
     [HideInInspector]
     public int counter;
 }
 public SpawnEntry[] spawns;
 void Update()
 {
     foreach (var e in spawns)
     {
         if (Time.time > e.startSpawnTime && Time.time < e.endSpawnTime)
         {
             if (e.nextSpawn > 0) e.nextSpawn -= Time.deltaTime;
             else
             {
                 for (int i = 0; i < e.amountPerSpawn; ++i)
                 {
                     e.nextSpawn += e.spawnInterval;
                    GameObject enemy = Instantiate(e.enemyToSpawn, e.spawnPos, Quaternion.identity) as GameObject;
                     enemy.GetComponent<enemyBase>().setID(e.counter);
                     e.counter++;
                 }
             }
         }
     }
 }

Be sure that you have a base class of enemyOne and enemyTwo that contains the setId() function so you can access it in a generic way..

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

17 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

random enemy spawn from day night cycle 2 Answers

c# Enemy's spawning over time, problem 2 Answers

More efficient way to spawn enemies in my game...? 2 Answers

Spawning game Object after a period of time 2 Answers

Help needed! 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges