Best way to contain all enemies in a 2D game?

I’ve tried doing this myself, and the solution I have is fairly decent, but I’m sure there’s a better way.

I’m making a 2D space shooter as a University project. At the moment, when a new level is loaded, my EnemyController script grabs all the enemies in that level, and places them into an array/list. (Haven’t decided which to go for, I prefer lists personally but each has their own pros and cons ofc)

So, for example, here’s the code it runs on awake.

	private GameObject[] heavyCruisers;

	private List<GameObject> lightCruisers = new List<GameObject>();

	void Awake()
	{
		lightCruisers.AddRange(GameObject.FindGameObjectsWithTag ("LightCruiser"));
		heavyCruisers = GameObject.FindGameObjectsWithTag ("HeavyCruiser");
			
		foreach(GameObject enemy in lightCruisers)
		{
			enemy.AddComponent<LightCruiserModel> ();
			enemy.AddComponent<EnemyView> ();
		}

		foreach(GameObject enemy in heavyCruisers)
		{
			enemy.AddComponent<HeavyCruiserModel> ();
			enemy.AddComponent<EnemyView> ();
		}
	}

Is there a way anyone can recommend, so that I can have them all loaded into one list at the start of each level and then I can simply check for something unique on each type of enemy? The reason i want them all in one list, is that it would be far easier to work with when checking for stuff such as all enemies being killed etc.

Another issue is, I’m trying to avoid using “GameObject.Find” as I was told it’s really slow. I don’t know if “FindObjectWithTag” is the best alternative or not.

This container approach is very similar to what I am doing in my current project. The container with the GOs sitting out in the project basically serves both as the pooling and defining the positioning of objects in some cases.

I have a WorldQueryManager that provides methods to gather up lists of GameObjects of a certain type. I pass in the ContainerName such as PlayerProjectiles and get a list of PlayerProjectile objects back containing references to each of the GameObjects in the project beneath the ContainerName GO just as you are describing.

If you want to add them to the same list it should be simple as just doing that. Based on a tag or interface you could figure out which type each is.

Or you can go one more step and set up containers for the objects themselves. That is what I am currently doing [using container objects to work with GameObjects in Unity].

However, I am using a very simplified (streamlined) approach where I have a manager for each different object type (well class of object types). For example, the ProjectileManager manages both Player and Enemy projectiles. The ProjectileObject (a lightweight container I made) stores references to the Unity specific stuff such as GameObject, Transform, etc as well as the properties that are game related (such as velocity, damage, etc). Others such as the Enemies also have the Unity InstanceID for the transform stored. This is just to allow communication.

In your case you could have a type property in your object class. This is what I do for the Enemies. The EnemyObject container class has an enum EnemyType property so I know exactly what kind of enemy I am dealing with.

It’s a unique way as far as Unity dev goes and am sure many folks would think GASP! That is not the Unity Way. But it is a way that actually works very well for me. I find it much more productive than the Component based approach having used that on my previous projects (although always moving more and more to this point… finally just took the big plunge). Probably because for the first time I feel like I am actually managing the system instead of it magically running itself and it produces a simplified architecture. Although I am not here to argue for or against the Component-Based approach.

Some of that may be helpful to you. Possibly. I definitely won’t say it is the best way. I think that really comes down to what works best for you.

The important question is:

Why do you need the list?

You can set enemy counter somewhere. By default it will be zero.
When enemy spawns for the first time, it’ll increase counter by one.
When it despawns/dies, it will decrease it by one.

No list, enemy count is known at all times.

Basically, I can’t think of many scenarios where you would need a list. I would attempt to exterminate this kind of structure from the project, unless I can’t live without it.

I wouldn’t say I NEED the list but they are handy ways to store objects etc to be fair.

I have an enemy view, enemy model, and enemy controller script. At the moment, my Controller spawns the enemies as shown above (The enemies are objects in the editor right now but I’ll possibly switch to spawning via code, which isn’t exactly difficult tbf)

The reason I have lists, is because I have different types of enemies. Lightcruisers, Heavycruisers, and I have plans to add more. I have one EnemyModel script, that has the classes for each type of enemy also, since they inherit from the base EnemyModel. I’m aware I can split them into other scripts but they really don’t contain much data so it would be a pointless thing to do really.

Maybe I’m over-complicating this due to the restrictions of the assignment and so forth, I had planned on having an enemy counter, that’s why I’m asking for advice on here about how to “collect” my enemies for that counter.

I’ve been programming this game non-stop for days and I’m probably over-looking something really simple here, it’s always the same. Thanks for the replies so far though

Roll a dictionary of <int, gameobject>

Store the object ids as the ints and refs to the go’s on instantiation, this prevents duplicate adds and just saves a ton of work when it comes to counting enemies and stuff.

It’s efficient too if you make a static method in a manager class that takes an object id (int) and returns the gameobject reference… its a hash table, there’s just no down side.

For bonus points make it a dictionary of <int, EnemyControllerScript> and now you still have access to the go’s transform, etc. but now you can also call methods and stuff.

Make a dictionary for each type of object and you won’t be sorry.

EnemyManager.GetEnemyById(other.getInstanceId()).KillYourself();

So easy its like cheating.

*fair warning: When you delete, if you want to destroy the objects you don’t want to use a foreach loop otherwise you destroy the collection. Just make an array of int the size of the dictionary keys and iterate through that and do it that way, and gc will take care of the rest.

This is your EnemyManager.cs:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class EnemyManager : MonoBehaviour {
    public static EnemyManager inst;

    Dictionary<int,EnemyController> Enemies;

    void Awake () {
        inst = this;
        Enemies = new Dictionary<int, EnemyController>();
    }
  
    public static void Register(int id, EnemyController enemy) {
        if (inst.Enemies.ContainsKey(id)) {
            Debug.Log("Duplicate registration attempted... " + id.ToString());
            return;
        }

        inst.Enemies.Add(id, enemy);
        Debug.Log(inst.Enemies.Count);
    }
}

This is the code for each enemy to register:

using UnityEngine;
using System.Collections;

public class EnemyController : MonoBehaviour {
    void Start () {
        EnemyManager.Register(gameObject.GetInstanceID(), this);
    }
}

That should get you started. It’s pretty easy to extend this into having god like control over your scene objects and you should be able to figure out how to make this work with different objects easily.

The best part is that there is no “order” to the objects, so if you just want to destroy one out of the blue… have at it. You aren’t going to break your collection’s integrity.

I made a boo-boo. If you want to Foreach through the collection you can destroy all the objects, actually, without any problem. Just remember to clear your collection when you’re done.

I haven’t come up with a more concise way than this nor have I seen one.

1 Like

I have never used dictionaries, but I just gave them a quick try and it’s so much easier. Even with my terrible programming.

I now have my enemies being assigned to one collection as shown below

        enemies.Add("LightCruiser", GameObject.FindGameObjectsWithTag("LightCruiser"));
        enemies.Add("HeavyCruiser", GameObject.FindGameObjectsWithTag("HeavyCruiser"));

        foreach (KeyValuePair<string, GameObject[]> enemy in Enemies)
        {
            for (int i = 0; i < enemy.Value.Length; i++)
            {
                if (enemy.Key.Equals ("LightCruiser") && enemy.Value != null)
                {
                    enemy.Value[i].AddComponent<LightCruiserModel> ();
                    enemy.Value[i].AddComponent<EnemyView> ();
                }
                else if (enemy.Key.Equals ("HeavyCruiser") && enemy.Value != null)
                {
                    enemy.Value[i].AddComponent<HeavyCruiserModel> ();
                    enemy.Value[i].AddComponent<EnemyView> ();
                }
            }
        }

It may be messy (Not sure if it is, or if it’s a fairly passable piece of coding tbh) but it’s far more readable than before, which was FOREACH loops for every type of enemy. Instead it’s simply one foreach loop with a nested FOR loop, much better imo.

One small problem though, it “broke” my counter. I tested it by loading up level one and it does collect all the enemies, and gives them all their correct models etc. In my EnemyController though, I had something like

        if (app.model.enemyModel.Enemies.Count == 0) 
        {
            app.controller.gameController.ChangeLevel("Level_02");
        }

So here’s what happens, it collects all the enemies and assigns them to the correct models as I mentioned BUT it also seems to think the count is empty, I tried changing the script execution order but same issue. Any idea why?

This belongs in scripting. :wink:

Your approach looks quite odd to me. If you are going to add components procedurally, why not make the GameObjects procedurally as well?

Alternatively if you are going to have GameObjects already in the scene, why not make them prefabs and have the components pre attatched?

Go procedural or go prefab. The half way approach is pointless.

Hi there, 2D space shooters are great. Where are the pics ?
You could consider using events for the purposes you are currently using the list.
Here is an intro[ https://unity3d.com/learn/tutorials/modules/intermediate/live-training-archive/events-creating-simple-messaging-system](http:// https://unity3d.com/learn/tutorials/modules/intermediate/live-training-archive/events-creating-simple-messaging-system)
You get more flexible, maintainable code using this approach. Lists can create major headaches after a while.
Kind regards,

Oh God, this started in General and got moved to Scripting. Okay.

@Ippokratis - Events are nice when all you want certain objects to respond to something that is happening elsewhere without needing to keep track of all of them in one central location. It’s sort of a “decentralized” solution. But if you need to be able to quickly gather information about a collection of objects, retrieve a count of the objects and–most importantly–if you plan on serializing some things you want to use one of the built-in collection classes since their purpose is to store collections of things. Tell me, what does “contain all enemies” sound like to you? Sounds like someone wants to put a bunch of things inside of a container, doesn’t it? So tell me, how does that call for an event-subscriber relationship? Especially when there is actually an explicit relationship between all of the objects in the collection, they need to have spatial awareness of one another and you want a higher-level view of them. Also, if you’re doing things intelligently, you really don’t want to be running a bunch of rogue logic in each one of your instances of something. The more you can centralize logic and processing, the fewer times your objects are going to have to analyze data and the better everything will run.

Take the simple problem of needing to find out which one of a certain type of object is nearest to a given point in the three-dimensional space.

Think about how you would achieve that using a event system?

Now think about how you could do that using a collection with a foreach loop?

I think it is clear which is preferable.

While I like the idea of “flexible, maintainable” code I also like to use the right tool for the job.

[quote=Master Frog, post: 2610679, member: 867970]
While I like the idea of “flexible, maintainable” code I also like to use the right tool for the job.
[/quote] I agree.

Many tasks in a 2d space shooter, like keeping track of player health, score, enemy count, triggering game over, next level, next enemy wave spawn, power-up appearance can be better implemented using events.

I also believe that most people who start out making their first game have no idea about how useful this method can be and use huge lists or dictionaries for this task, only to find out how inflexible this approach is after they try to build the second level.

It is a nice thing to find out early.

Thanks for all the replies/advice. I went with the approach Master Frog had demonstrated, an interesting way to keep track of enemies and definitely something I’ll be using again/tinkering with, so thanks a lot!

Also, sorry this was originally in the wrong section, my bad.