The Horde - A Top-Down Zombie Survival Game

arduboy_the_horde.hex (47.3 KB)

This is my first Arduboy game. I made this months ago but I have finally decided to share it with the Arduboy community.

The game is available here.

Twitch WillStunForFood

The Horde



Instagram - YouTube

A Top-down zombie survival game for Arduboy.

Table of Incontinence

About

The Horde is a top down zombie survival game. All you youngins’ out there might associate this game with Dead Ops Arcade from the first Call of Duty Black Ops. You play as a single character on the screen represented by a hollow circle. You can move in any direction. When you fire your weapon, the shot travels in the same direction as the current direction you are facing your character.
Zombies are presented by a semi-solid circle. They spawn off screen and will immediately start chasing you. The number of zomies spawned is equivalent to the current wave. Zombies can spawn with variable speeds but never with speed greater than your character. If any part of a zombie touches your character, you die.
Zombies drop powerups and it would behoove you to pick up those powerups. More on this later. Your session statistics are shown on screen upon death.

Controls

Button Function
UP Move character up
DOWN Move character Down
LEFT Move character Left
RIGHT Move character Right
B Shoot
A N/A

Powerups

When you kill a zombie, there is a 10% chance the zombie will drop a powerup. The powerup can be any weapon greater than or equal to your current weapon, or an aid powerup.
Dropped powerups are represented by a stationary hollow circle (like your character). They can be picked up by running into them.

Weapons

The following weapons can be obtained through powerups.

Pistol

  • semi-automatic
  • One bullet kills one zombie
  • This is the weapon you start with.


Machine Gun

  • Fully automatic
  • One bullet kills one zombie
  • Bullet speed is the same as the pistol


RPG

  • Semi-automatic
  • One rocket can kill all zombies within its blast radius of 18px
  • Rocket speed is half of the pistol bullet speed
  • Yes this guy is OP


Aid

Nuke

  • Single instance after pickup
  • Kills all zombies within a 100px blast radius


I want to give a special thanks to @Pharap for helping me saves a great deal of space from strings and lists. I wouldn’t have been able to get this far without the help.

To Do

  • I would like to add songs to the game. I have made test arcade covers of classic Call of Duty zombies songs (Beauty of Annihilation) that I would like to include but I am having trouble converting the song into something usable by arduboy.
  • I want to add grenades which can be picked up, stashed, and dropped using the A button
  • More weapon ideas
9 Likes

Thanks for deciding to share, this is great!

5 Likes

Pretty easy to pick up and play… I think you should have added in diagonal shots!

1 Like

I like the idea. How would you propose I implement diagonal shots? Currently the shot direction is determined by the last direction the player traveled.

Would you settle for something as simple as just hit two directional buttons at the same time to achieve diagonal shots?

1 Like

maybe change control a bit. up - move forward. left/right - rotate

That would probably make sense.

You also wouldn’t have to change much because matchShot already handles veritcal and horizontal velocity separately.

True this would be very easy to implement with the current design. I will move forward on this feature then.

One small thing to be aware of: if you use BULLET_SPEED and -BULLET_SPEED unaltered to produce your diagonal speed, your diagonal shots will technically be travelling faster than your horizontal and vertical shots.

(If you’re not sure why, imagine a 1x1 square and think about the diagonal length between the corners being longer than the edges of the square.)

To make the diagonal shots travel at the same speed as the vertical and horizontal shots you’d have to multiply them by ~0.7071067811 (i.e. sin(45°)).

Unfortunately you’re using ints at the moment, so you’ll have to make sure BULLET_SPEED isn’t 1, otherwise you won’t get any diagonal movement because static_cast<int>(1 * 0.707) is 0, anything greater than 1 should be fine.

I actually just had a colleague stop by my desk to chat and we were talking about this so the timing was pretty great. Shall I use floats instead? I can’t remember how close I was to meeting the end of the memory constraints.

Edit: Just went in a noticed my shot class already uses floats but matchShot uses ints. This will need to be fixed indeed.

If you have room then yes.

If you don’t have room, you could trial fixed points instead.

If you still need more room, I don’t think you ever actually merged the changes that got rid of String onto master (they’re on a different branch), so if that branch is working you could give that a go.

I plan on working with the avoid-using-string branch. I will merge that into master after I get this change working since it is certainly time. I should have enough room to do this. I am already using floats for the shot class. I just misconfigured matchShot to take int arguments instead of floats.

1 Like

@crait @Pharap Added the diagonal shots feature in this commit. I also merged the strings branch into master.

2 Likes

While I think of it, I notice there’s still some String being used here:

It ought to be a lot cheaper to split that into:

Serial.print(F("Player x: "));
Serial.print(player.x);
Serial.print(F(" y: "));
Serial.println(player.y);

One little hack I sometimes use if I’m writing an Arduboy program that does a lot of printing is to define an operator << like std::ostream does in the C++ standard library:

template<typename Type>
inline Print & operator <<(Print & printer, const Type & value)
{
	printer.print(value);
	return printer;
}

template<typename Type>
inline Print & operator <<(Print & printer, Type && value)
{
	printer.print(value);
	return printer;
}

Which can then be chained like so:

Serial << F("Player x: ") << player.x << F(" y: ") << player.y << '\n';

And the magic of good compiler optimisation should mean that it ends up being exactly as if you’d just written this in the first place:

Serial.print(F("Player x: "));
Serial.print(player.x);
Serial.print(F(" y: "));
Serial.print(player.y);
Serial.print('\n');
2 Likes

Nice hack … I like it!

1 Like