Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

spawning a different type of enemy as the game progresses (asteroids type game)

Discussion in 'Scripting' started by lemDace, Apr 25, 2012.

  1. lemDace

    lemDace

    Joined:
    Feb 27, 2012
    Posts:
    13
    I currently have a ship that i can fly around the screen and i have a basicCometPrefab that i spawn a bunch of as soon as the game starts. I have a CometSpawner script that i attach to the main camera and this script creates an array of my basicCometPrefab game objects and instantiates them at random points along the edge of the gamescreen. The comets own scrip then controls its movement.

    This all works fine, but now i want to Spawn a new type of comet when the game timer reaches a certain time. I have another prefab called FollowerCometPrefab (with its own movement script) that i want to spawn some of while the other BasicComets are still flying around doing their thing.

    Im having trouble deciding how best to do this. Should i create another CometSpawner script specifically for each new tye of comet i want to spawn(followerCometSpawner), and attach these scripts all to the main camera as well and have them just handle themselves based on the current timer? Or is there a more elegant solution to this? it seems like im over complicating things by making new spawners for each type of comet , what i f i have 20 types, then i have 20 different spawner scripts attached to the main camera waiting to instantiate there special comets?

    Any help or guidance would be appreciated.

    Cheers
     
  2. Zethariel1

    Zethariel1

    Joined:
    Mar 21, 2012
    Posts:
    439
    Kepp the spawning in one script. Set a timer variable in the update, and if that timer reaches a certain value, you can turn a bool (spawnFollowers for example) to true. That bool would then work with an if statment that would spawn the next comets.

    For easy use, you could make an array of bool CometEnemies (or an integer that would represent which latest type of enemy can be spawned, like 0 is normal comets, 1 is follower + normal, 2 is explosive + follower + normal) and make the timer flip each one to true as time progresses (or increment the integer). Then just a few if statements and you're done (you can use a switch for the integer)
     
  3. lemDace

    lemDace

    Joined:
    Feb 27, 2012
    Posts:
    13
    Excellent, thanks for the input! so that mean that i should have a previously defined array in the cometSpawner for each enemy type?

    Like:
    Code (csharp):
    1.  
    2. public GameObject[] BasicCometPrefab = new GameObject[100];
    3. public GameObject[] FollowerCometPrefab = new Gameobject[50];
    4.  
    Is defining arrays that may or may not be used going to effect performance? or am i just thinking about optimization way too early?

    I suppose i could do the exact same method for powerups, and have them spawned from the main spawner script as well at random intervals.

    If i put the game timer in the main Spawner script, how can i reference that time frm another script? im unsure about referencing variables and methods from another outside script. If i had a script that handled the players score for example, then how would i get the time when it is a variable in the spawner script?

    Thanks for the help :)
     
  4. Glockenbeat

    Glockenbeat

    Joined:
    Apr 24, 2012
    Posts:
    669
    As for spawning different kind of enemies, like Zethariel already suggested, just put all your different types of enemies into an array or a list (assign them via the Inspector). While instantiating those, simply refer to enemyArray[0] to fetch the first type of enemies. After a while, when the timer has reached a certain threshold, just increase the chosen enemy counter.

    So you will end up with something like this as variables to be set (all in JS, not so familiar with C#):

    Code (csharp):
    1.  
    2. var thresholdValue : float = 50;
    3. var enemyTypeSelector : int = 0;
    4. var enemyTypeArray : GameObject[];
    5.  
    and something like this to increase the chosen enemyType:

    Code (csharp):
    1.  
    2. if (Time.time > thresholdValue) {
    3.     enemyTypeSelector++;
    4. }
    5.  
    and something like this to Instantiate the enemies:

    Code (csharp):
    1.  
    2. var newEnemy : GameObject = Instantiate(enemyTypeArray[enemyTypeSelector], position, rotation);
    3.  

    You should however consider to Instantiate all your enemies on startup, pool and deactivate them, and once you need them grab them from the pool. This way you won't run into expensive Instantiate calls during runtime.

    As for getting variables from other components / types, you would do e.g. something like this for setting those.

    Code (csharp):
    1.  
    2. var newEnemy : GameObject = Instantiate(enemyTypeArray[enemyTypeSelector], position, rotation);
    3. newEnemy.GetComponent(AIController).attackDamage = 100;
    4.  
    Reading values should be no problem from then.
     
  5. Zethariel1

    Zethariel1

    Joined:
    Mar 21, 2012
    Posts:
    439
    IMO, it is good practice to keep variables in one script, so you don't have to communicate unless you have to (like, keep all game controlling variables in one script, all player related scripts in another etc).

    I think you misunderstood me with the arrays :) My intention was to simply create an array or an integer that would govern which enemies can be spawned. While your idea of indexing every comet can be useable for your game, does it have a purpose? (does the controller or w/e really need all the references to those objects? Or can those objects just work autonomously?)

    So, in order to decide what is spawned, you can use the pseudo-code below:

    Code (csharp):
    1.  
    2. GameObject SomeComet, SomeOtherComet, etc...;
    3. int cometTypeAllowed;
    4. float timer;
    5. // eventually bool[] cometTypeAllowed = new Array{false, false, false, etc} for as many comet types you have;
    6.  
    7. void Update
    8. {
    9. timer -=Time.deltaTime;
    10.  
    11. if ( cometTypeAllowed >= 0 )
    12.     SpawnSomeComet()
    13. if ( cometTypeAllowed >= 1 )
    14.     SpawnSomeOtherComet()
    15.  
    16. /* You can also just reference the index of the bool array
    17. if ( cometTypeAllowed[0] )
    18.    SpawnSomeComet();
    19. etc.... */
    20.  
    21. // every 60 seconds, another type of comet can be spawned!
    22. if ( timer >=60 )
    23. {
    24. timer -=60;
    25. cometTypeAllowed ++;
    26. // or change a variable in the array;
    27. }
    28.    
    29. }
    30.  
    EDIT: Glockenbeat beat me to it :) And his idea with having the spawnable enemies in an array makes things even simpler, as you just spawn with a for loop
    Code (csharp):
    1.  
    2. for ( int i = 0 ; i <= cometTypeAllowed ; i++)
    3.     SpawnCometTypeOf( CometTypes[i-1] )
    4. // CometTypes would be your GameObject array of comet types, and the function SpawnCometTypeOf would instantiate the i-1 indexed object.
    5.  
     
    Last edited: Apr 25, 2012
  6. lemDace

    lemDace

    Joined:
    Feb 27, 2012
    Posts:
    13
    Okay, yes i think i did misunderstand what you meant with the arrays :p If i had a predefined arrays with 100 of each comet type that would mean i could reference each individual comet if i needed to and change them if i needed, but i dont really need to be able to do that because each come just has its own logic to control how it moves and thats it, kind of fire and forget.

    How can i put all my enemy/comet types into an array as Glockenbeat says? do i need each comet to inherit some base class of comet and make an array or baseComets? because i didnt think i could have an array that holds multiple different class types in it? how do i assign this via the inspector without having an array for each comet class?

    It seems like your both suggesting an array with one of each comettype in it, and then i use this array to instantiate copies of each type of comet depending on what the timer is at?

    I think i am understanding what you mean :p sorry im not at my computer so i will try it out shortly, im just trying to get my head around the logic while i have some spare time :)

    EDIT: i just saw your edit Zetharial, and imstill a bit confused about the array of comet types, how can i have an array of different classes, i though an array must contain all the same type?
     
  7. Glockenbeat

    Glockenbeat

    Joined:
    Apr 24, 2012
    Posts:
    669
    Yepp, exactly like this. So this means you create a prefab item of each of your comet types and assign it to the array via the inspector. This means in conclusion that if you have 10 different types of comets, the length of the array would be 10 as well. The timer and threshold will then decide which to spawn.

    That's true, sure. However the type of the array's contents is GameObject, which is the prefab you assign.
     
  8. novashot

    novashot

    Joined:
    Dec 12, 2009
    Posts:
    373
    Of you make the array of type whateverclass then yeah your stuck.... Make your array of type game object and have it of all enemy types.
     
  9. lemDace

    lemDace

    Joined:
    Feb 27, 2012
    Posts:
    13
    ahhhhh okay! so i have an array of gameObjects:
    Code (csharp):
    1.  
    2. public GameObject[] CometTypes = new Gameobject[2];
    3.  
    In inspector i set CometType array position 1 to BasicCometPrefab, and position 2 to SuperCometPrefab, then when i want to instatiate one or the other i would use the prefabs name instead of GameObject, like:
    Code (csharp):
    1.  
    2. Instantiate(BasicCometPrefab, atLocation, atRotation);
    3. //or
    4. Instantiate(SuperCometPrefab, atLocation, atRotation);
    5.  
     
  10. Zethariel1

    Zethariel1

    Joined:
    Mar 21, 2012
    Posts:
    439
    You can define the lenght of the array like you did before:

    Code (csharp):
    1.  
    2. // instead of this
    3. public GameObject[] BasicCometPrefab = new GameObject[100];
    4. // use this
    5. public GameObject[] cometPrefabs = new GameObject[10]; // given you have 10 comet types
    6.  
    Then you just populate the array via the inspector :)

    EDIT: Dammit, I write so slow xD You can't write
    Code (csharp):
    1.  
    2. Instantiate(BasicCometPrefab, atLocation, atRotation);
    3. //or
    4. Instantiate(SuperCometPrefab, atLocation, atRotation);
    5.  
    As they are not defined. Instead you would write:
    Code (csharp):
    1.  
    2. Instantiate(CometTypes[0], atLocation, atRotation); // where the item indexed 0 would be equal to your Basic Comet Prefab
    3. //or
    4. Instantiate(CometTypes[1], atLocation, atRotation); // where the item indexed 1 would be equal to your Super Comet Prefab
    5.  
     
    Last edited: Apr 25, 2012
  11. lemDace

    lemDace

    Joined:
    Feb 27, 2012
    Posts:
    13
    Thank you so much for the help, i am getting my head around the logic better now thanks to you guys help, so thanks :)

    I have used an array of cometTypes like suggested and it works great, and i understand what is happening, i made a method within my spawner class to spawn whatever comet i want and a certain amount:

    Code (csharp):
    1.  
    2. void SpawnCometsOfType(GameObject cometType, int amount)
    3.     {
    4.  
    5.         int temp;
    6.         Vector3 spawnLocation = Vector3.one;
    7.         int count = 0;
    8.  
    9.         while (count < amount)
    10.         {
    11.             temp = Random.Range(1, 5);
    12.  
    13.             if (temp == 1)
    14.             {
    15.                 spawnLocation = new Vector3(Random.Range(-screenWidth, screenWidth), screenHeight, 0.0f);
    16.             }
    17.  
    18.             if (temp == 2)
    19.             {
    20.                 spawnLocation = new Vector3(screenWidth, Random.Range(-screenHeight, screenHeight), 0.0f);
    21.             }
    22.  
    23.             if (temp == 3)
    24.             {
    25.                 spawnLocation = new Vector3(Random.Range(-screenWidth, screenWidth), -screenHeight, 0.0f);
    26.             }
    27.  
    28.             if (temp == 4)
    29.             {
    30.                 spawnLocation = new Vector3(-screenWidth, Random.Range(-screenHeight, screenHeight), 0.0f);
    31.             }
    32.  
    33.             Debug.Log("comet " + count + " about to spawn.");
    34.             Instantiate(cometType, spawnLocation, Quaternion.identity);
    35.             Debug.Log("comet " + count + " spawned.");
    36.  
    37.             count++;
    38.  
    39.         }
    40.  
    I then call this method everytime i want to spawn a new wave of comets of a certain type, so at 5 seconds into the game i call:

    Code (csharp):
    1.  
    2. if ((gameTimer > 5.0f)  (gameTimer < 5.05f))
    3.         {
    4.             SpawnCometsOfType(CometTypesArray[1], SecondWaveCount);
    5.         }
    6.  
    (thats the first way i thought of to spawn a new wave at a certain time of the game, i cant say 'if gametimer == 5.0f' because of the way the timer increments, it doesnt seem to increment the same everytime, so sometimes it misses a spawn if i am being exact.)

    This works great though and i am now working on tweaking the behavior of my different comet types so they all act differently!

    One little performance hit i have noticed, when i create a new wave everything in the game pauses for a half a second or so before continuing on with the new comets on the play field. Is this pause due to me instantiating the new wave of comets in the SpawnComets method? how would i get rid of this pause, would i need to instantiate all the comets i am going to need at the very beginning of the game? because this could possibly result in hundreds, maybe over a thousand comets being preloaded, and a lot of them may not even get used if the player dies in the first 10 seconds.

    Anyways, once ive made more progress i will try and work out how to put a demo web player up online. Thanks again :)
     
  12. Zethariel1

    Zethariel1

    Joined:
    Mar 21, 2012
    Posts:
    439
    If you put a great number into the amount integer, it may take some time to iterate through them. Bear in mind that the while loop will try and execute itself in 1 frame, meaning that it won't go ahead until it's done. So the Debug.Log will just spit out a lot of info at the same time -- if you want it to spawn comets not at the same time, try not using the while loop and calling SpawnCometsOfType each frame until their count is suited to your needs. This may not spawn all the comets immediatelly, but you are guaranteed not to encounter any stutters or performance slow down as the code generates just one meteor instead of a lot per 1 frame :)

    EDIT: Also
    Code (csharp):
    1.  
    2. if ((gameTimer > 5.0f)  (gameTimer < 5.05f))
    3.         {
    4.             SpawnCometsOfType(CometTypesArray[1], SecondWaveCount);
    5.         }
    6.  
    I would advise against this, as there is no guarantee this code will execute only once -- depends on how fast the machine is (how much gameTimer is advanced. If you use Time.deltaTime, it could so happen that the increments would be like 0.01 instead of the 0.05 that you might expect). It would be best if you held some kind of other variable (bool or int) that would make 100% sure that the code isn't done over and over again should the game glitch/freeze for some reason.
     
    Last edited: Apr 26, 2012