Does anyone know an efficient way to handle waves in a TD? Single enemy type waves are easy, but I’m talking about multiple enemy types that can go something like Ax10, Bx20, Ax10, Cx30 (spawn 10 A’s, then 20 B’s, etc…).
Right now I’m manually programming each wave, and while that works very well, it’s extremely slow and even slower when there are multiple levels. I have 75 waves per level and like 8 levels. That’s an ass load of lines of code lol.
So I’m trying to think of a wave to utilize the editor to create a wave of enemies (like “+” in a new wave, then “+” in a new enemy, add how many, then “+” in a new enemy, add how many, etc…), but I have no idea where to start.
If it were a traditional game engine I would create a text file or XML that specifies the enemy type and number of enemies to spawn for each wave and level. As it is Unity, I’d make a custom wave editor to tie in to the game. If I was feeling lazy, just stick the info in a string and then use a regexp to pull out what I needed.
For a quick test I would first hand code the data in to a hard-coded array of structs or an array of tuples, which is what I would turn the text/XML/regexp in to anyway after I have done my initial interpretation of the data. The custom Unity editor would poke the values directly in to the array of structures for me.
I’ve already done the quick test you suggested. I tried using a public array before, but eventually I have massive wave sizes and manually dragging in a unit to each slot in an array is no faster than my current method.
I like the idea of using regex to determine how many go into the array. I could use one regex for unit type of a block of units (so like A/B/A/C, but I would use 1/2/1/3) and one regex for how many go into the array (10/20/10/30). It might be a little inefficient to do this, but I would rather take the easy route than have to do a bunch of XML tie ins, and I have the framerate to spare (plus lots of static moments in the game where a hiccup is allowed to occur), so it should be ok.
If anyone has more efficient, easier ideas, I’m still open to suggestion. Thanks for replying so quickly though JL, I appreciate it.
EDIT: After looking into regex a bit more, I realize it isn’t the exact route. It’s best to use String.split() instead since regex includes the delineating characters and we wouldn’t want to ParseInt a “/” would we?
I won’t give away the trick we are using, but I can share that we originally were going to make a wave script and put that on a game object, then make a waves (with an ‘s’) script which we drag and drop waves on to. This would allow us to make prefabs of individual waves and then adjust them in a list per level. In the end, we found moving text around a lot easier than drag and drop for this amount of data, but we have shortcuts so a wave may only take a single line of code if all the enemies are the same, and the waves themselves have options we can set as well. If you get clever with Lists you can write wrapper functions which make it easier to add more than one item with a single method call.
PoolManager helps us a lot with this because once we make an enemies pool we essentially have a really a easy-to-access dictionary of static references we can grab from any script. So in a level script, we can just grab the references and start sequencing them using lists. When the level runs, we just, essentially, run through the lists and pop off items to be spawned.
Another big factor is how many different enemy prefabs you have and how you manage their attributes, etc. Do you repeat patterns but increase the level of difficulty? Is there a randomness to the game…it is design dependent.
I hope this rambling helps in some way.
EDIT: For inspiration, look at how you explained it “Ax10, Bx20, Ax10, Cx30”…that should be 4 lines of code.
It sounds like you are a reasonably advanced programmer rather the tongue-lolling, knuckle-dragging, mouth-breathing MMORPG creators that usually ask these questions, I’ll be happy to help you work through this problem.
With regexp you could literally use something like “(Ax10)(Bx10)(Ax20)(Cx10)(Bx20)(Dx5)” and have it split apart into types and numbers precisely what you want. If you don’t come up with the regexp in 24 hours hit me up and I’ll see if I can find five minutes to walk through it with you.
For the editor control, I wouldn’t imagine you would drag and drop a whole bunch of objects. I would create a “Wave” behaviour that strores a two-dimensional array or array of tuples or structs to include an enum for enemy type and an integer for number of enemies and then write an editor for that, e.g.
// either use a tuple or a dedicated struct if you can, otherwise you will live to regret it
public struct WaveInfo
{
public AttackingWave.EnemyType enemyType;
public int count;
}
public class AttackingWave : MonoBehaviour
{
public enum EnemyType
{
Enemy01,
Enemy02,
Enemy03,
Enemy04,
}
protected int m_currentWaveIndex;
public List<WaveInfo> m_waves;
public void Start()
{
// hard-coded set up for now, really bad way of doing this
m_waves = new List<WaveInfo>();
AddWave(EnemyType.Enemy01, 10);
AddWave(EnemyType.Enemy02, 10);
AddWave(EnemyType.Enemy01, 20);
AddWave(EnemyType.Enemy02, 20);
AddWave(EnemyType.Enemy03, 10);
AddWave(EnemyType.Enemy04, 5);
}
public void AddWave(EnemyType enemy, int enemyCount)
{
WaveInfo info = new WaveInfo() { enemyType = enemy, count = enemyCount };
m_waves.Add(info);
}
public void ResetLevel()
{
m_currentWaveIndex = 0;
}
public void NextWave()
{
m_currentWaveIndex = Mathf.Min(m_currentWaveIndex + 1, m_waves.Count - 1);
}
public WaveInfo CurrentWave
{
get
{
return m_waves[m_currentWaveIndex];
}
}
public int CurrentWaveIndex
{
get
{
return m_currentWaveIndex;
}
}
}
I think doing something like JustinLloyd is doing in Awake is better than a regular expression (just don’t do it there, write a static interface or something and call it from a level script…or something). Typing code will allow your IDE to catch errors while you type (and auto-complete, etc) whereas a string won’t cause problems until run-time. i just think it makes for less errors and quicker work. Besides, moving lines up and down is far easier than visually parsing a crazy string to find that one enemy you want to change.
I have a question which is kinda related to this one.
Replacing this:
public enum EnemyType
with:
public GameObject[] potentialPrefabs;
And then having the calling code passing a prefab instance instead of a enumType
Then doing a comparison on the passed instance against all known instances.
InstanciateEnemy(Vector3 pos, Quaterionen rot, GameObject enemyType)
{
bool knownPrefab = false;
for(int i = 0; i < potentialPrefabs.lengt; i++ )
{
// Is this doing a Reference Compare or do I need to implement IComparer?
if(enemyType == potentialPrefabs[i])
{
// Add to existing list for that prefab type.
knownPrefab = true;
break;
}
}
if(knownPrefab == false)
{
// Create a new list for that Prefab Type
}
}
I am acctually using Code like this for a ProjectilePool, it’s not completly dynamic yet since it’s mainly for prototyping and learning.
But it allows a Non Programmer to add new Projectile Types with ease, just by adding a new Prefab To the potentialPrefab array.
All the Code that I typed was in the moment in the Browser, so there will be errors
What do you guys think? (Pro/Cons) Weird? (Bad/Good) ? Thanks
The question just popped up while I was reading your EnemyType enum (only programmers can add new types instead of designers)
and I could not resist the urge to ask.
My appologize for hijacking your thread @darknuke
Goes hiding behind a rock now