9
\$\begingroup\$

I'm currently making a defense game where enemies will spawn and attack the player's army. It's similar to a Tower Defense game, except there is only one level. The enemies will keep spawning until eventually the user dies or he amasses a large enough army that they wipe out any enemy that spawns instantly (hopefully it'll be challenging enough that this won't happen).

What I'm struggling with at the moment is how to make this game get progressively harder and the probability of an enemy spawning basically eventually reach 100%.

So far I've got something similar to the following

if(Math.random() < 1 - (1/elapsed_time) && spawnTimer <= 0 ){
    spawnEnemy()
    spawnTimer = rand(); // random number between 1 and 3
}

But it seems too uniform to begin with. At the moment I'm only spawning one type of enemy, the idea being that tougher enemies are spawned as time progresses as well.

I also think I need to randomise my spawnTimer at bit more and make that get quicker as time progresses, but I just can't seen to get my head around what my logic should really look like for this whole section. Is anyone able to help with a rough idea of this kind of formula?

I'm currently using javascript to write the game, but obviously it's more about the concept.

\$\endgroup\$
  • \$\begingroup\$ What is elapsed time? Is it the that passed until now? How often do you call this condition? \$\endgroup\$ – wolfdawn Jan 27 '14 at 9:38
  • \$\begingroup\$ Hi, yeah sorry elapsed time is the time passed since the game started and this is called in my games update() cycle which is based on window.requestAnimationFrame \$\endgroup\$ – TommyBs Jan 27 '14 at 9:59
3
\$\begingroup\$

The function you chose is not likely to be suitable for your purpose. First, if you go this route a failed random condition should result in a delay before the next time you roll the dice. Also, it is unlikely that keeping the spawn timer within a constant range good for your goal.

Let thresh be a difficulty threshold you think is high. We use that later to decide when to slow down the pace at which the game gets harder.

Let rate be the amount of monsters you want coming in every minute (or round).

Let GROWTH_RATE be the rate in which difficulty increases (for now it'll be 0.2).

Let's say you start with rate = 10. Now the player killed 5 monsters so you can increase rate by GROWTH_RATE * 5 = 1.0 and the new rate = 11.

How to implement a condition that will generate rate monsters every minute:

Set spawn timer to a number between 0.5 to 1.0 multiplied by 60 seconds orround-timeand divided byrate. Also leave a0.25` chance that no monster is spawned when the timer reaches 0 and the time is randomized again.

If rate ever reaches thresh you need to slow down. Now instead of increasing rate by GROWTH_RATE, you could increase it by 1 / math.sqrt(rate). That way the player won't be destroyed instantly in the harder setting.

You can reset thresh to be around 80% of the rate at which the player lost the game.

If you wish to randomize the strength of the monster be cautious about the timer. For instance, if you decide that player-score (determined by monsters killed so far) will be used to determine the max strength of a monster that could spawn, you can do something like this: max-monster-strength = player-score / 1000. Then randomize a float or double between 0.0 to 1.0 and multiply the result by itself.

float monster_power = Math.random();
monster_power *= monster_power; // Multiply result by itself
    // Makes hard monsters less frequent than weak ones
monster_power *= max_monster_strength;

Now when you randomize the timer you should probably take the power level into account. For instance you could multiply the timer result by the square root of the next monsters power.

spawn_timer *= math.sqrt(monster_power);
\$\endgroup\$
6
\$\begingroup\$

There's lots of options, two with your current setup:

  • Make enemies harder
  • Spawn more enemies (either more often or multiple at a time)

Then more with additional features:

  • Enemies start dealing different types of damage, and the player must defend differently against each. For example, ranged attacks, magic, etc.
  • Enemies start defending themselves against different types of damage. The player must build multiple types of damage dealing devices to counter. For example, player starts with arrow attacks and cannonball attacks. An enemy that has a very high resistance to arrows spawns, so the player must make sure they've balanced their attacks.

As for making a difficulty curve, there's really no correct answer. It's going to take a lot of playtesting and tweaking to get it right. It'll depend on how long the game is supposed to last and how difficult you want it to be. I suggest you use Excel or a site like WolframAlpha and see what kind of curve different functions create. Try exponential increases, linear increases, etc. Find one that works for you.

\$\endgroup\$
  • \$\begingroup\$ Thanks for the ideas but I think the selected answer is more what I need. But you've definitely given me more food for thought and I've upvoted your answer \$\endgroup\$ – TommyBs Jan 28 '14 at 9:12
2
\$\begingroup\$

Instead of having a fixed number of enemies (one) spawn with a variable probability, you could turn it around and have a variable number of enemies spawn with a fixed probability.

static const double SPAWN_CHANCE_ON_DIFFICULTY_1 = 0.01;

for (int i = 0; i < currentDifficulty; i++) {
   if(Math.random() < SPAWN_CHANCE_ON_DIFFICULTY_1 ){
       spawnEnemy()
   }
}

On difficulty level 1, this will have a 1% chance to spawn one enemy per tick.

On difficulty level 1000, it will spawn up to 1000 enemies, each one with a separate chance of 1%. This means that on average 10 will spawn per tick, but it could also be more or less. There is a possibility that a number very different from 10 will spawn at once, maybe even all 1000. but that's a very unlikely event due to how probability works (compare: Law of Large Numbers).

When you add more different enemies with different behaviors and stats, you maybe want some of the tougher enemies to only start spawning on higher difficulties and even then with a low density at first. To do this you could add a difficulty-modifier which is subtracted from the current difficulty. That way the enemy won't spawn before that difficulty-level is reached and even then only with a lower chance at first:

for (EnemyType enemyType: allEnemyTypes) {
    for (int i = 0; i < currentDifficulty - enemyType.getDifficulty(); i++) {
        if(Math.random() < enemyType.spawnChance() ){
            spawnEnemy(enemyType);
        }
    }
}
\$\endgroup\$
  • 1
    \$\begingroup\$ This is stimulating but it should be optimized before it's used. For instance, you could use Math.random() once to decide how many enemies to spawn. If you want to leave a smaller chance for a large amount of foes, you could implement the "Normal Distribution" more efficiently. stackoverflow.com/questions/2325472/… \$\endgroup\$ – wolfdawn Jan 27 '14 at 13:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.