Project: Game in C - Tank APOcalypse

This article is about general concepts we’ve (me and my classmate Ivan Čermák - https://github.com/NotMyName2) used to make the Worms-like game, intended to be used on development board, in pure C with help from SDL2 library for graphics display while developing on PC.

I’ve divided this article into these parts:


In one of our final assignments for Computer Architecture class we had to create a control driven system such as game or some control capability demonstration like painting with buttons/dial controls for the board named MicroZed with which we worked a few weeks before the assignment.

Specification of MicroZed board:
Name: MICROZED EVALUATION KIT
Model: ADSAES-Z7MB-7Z010-G
Chip: Xilinx Zynq-7000 All Programmable SoC
Type: Z-7010, součástka XC7Z010
CPU: Dual ARM® Cortex™-A9 MPCore™ @ 866 MHz (NEON™ & Single / Double Precision Floating-point)
2x L1 32 kB data +32 kB instruction, L2 512 KB
FPGA: 28K Logic Cells (~430K ASIC logic gates, 35 kbit)
Compute power of FPGA: 100 GMACs
FPGA memory: 240 KB
Board memory MicroZed: 1GB
OS: GNU/Linux
GNU LIBC (libc6) 2.19-18+deb8u7
Linux 4.9.9-rt6-00002-ge6c7d1c
Distribution: Debian Jessie

As we had online learning during coronavirus closedown, we could use these boards at a distance since a couple of boards were streamed on YouTube and were accessible through ssh connection via lab computers. These boards were immediately and expectedly misused for image and gif memes.

Me and my friend decided to make it a little bit more challenging. We chose to create our assignment/our task to be showcased on the board. Other template-assignments provided by the professor were snake game or to mimic etch a sketch drawing machine.

We settled on making a clone of the Worms (series) game (one of our favorites).

The plan was to create a game for 2 players, each player with several tanks and each tank with several weapons. Each weapon would have its attributes (speed, reaction to wind, damage, a radius of the explosion, etc.) and could destroy/damage its surrounding environment.

Final exams and assignments from other classes started rushing in so we worked under time constraints.

The whole game is written in C without an existing game engine.

There are numerous ways to improve the code but at the time, the goal was not to make it perfect but to make it work and make the at least readable and commented so I can focus on my other exams asap.

If somebody would be interested and would want to take some ideas from the code etc. feel free to do anything.
https://github.com/PeterKillerio/Tank_APOcalypse_C_game

We decided to divide our work into two parts. As the game development was just a sprint I would take it under my shoulders (as only I had experience in making games), learning the board controls, creating a communication base with the board was, on the other hand, the responsibility of my friend. We didn’t have any library for controls, everything was memory accessed and self-coded. So it wasn’t as trivial as it might seem at the first glance.

I want to devote this article to explain the basic concepts you will most likely need to create your own basic “game engine”/game from scratch and these concepts are usable in a variety of programming languages. You don’t need classes (element of object-oriented programming languages) but at least you should have a struct datatype equivalence with C.

First of all, if you come from a non-technical background some concepts might be challenging. I know they were for me before I ever dove into programming.

To be honest, lots of these concepts and their implementation were spontaneous or found on the internet, I had prior experience with Unity game engine which helped immensely and can’t stress enough that for you to build you own game you should get used to the workflow of some existing game engine, also I had done some simulation in python HERE so that helped me with some conceptual issues to design a real-time process. Because in programming it goes a lot of times like this: “If you can interpret it, you can code it”.

Here’s my try to short-define a game: A game is a chunk of code running in loops trying to bi-directionally communicate with most likely but not necessarily a human with a primary objective of entertainment.

So, for a game we need two ways of communication human->computer (buttons, keyboard, etc. ), computer->human (screen, sound, etc.).

Before writing any code for the game these are the basic building blocks you have to take care of. When using existing game engines a lot of this dirty work is already done. (Dirty because you have to first prepare your supplies before you can start cooking).

The screen of the game is a rectangle of certain width and height composed of pixels. Each point (pixel) in this rectangle contains 3 numerical values (1 numerical value in case of black and white image) which encode color in (most likely) RGB spectrum. When you are programming displays you are always just working with numbers and you influence how these numbers change over time.

Communication with a keyboard connected to your PC or in your laptop is most likely already taken care of inside your programming language. When talking about buttons on development boards it depends. Sometimes buttons are mapped to board memory (to access information about the input you will have to read it from memory) and other times they are accessible through input/output pins as with Arduino.

I’m sure there are many ways to do this but basically what your goal is to have a certain amount of FPS (frames per second).

Calculations for your game get to be calculated certain times per second (not generally, don’t take it as a rule). Games nowadays run mostly in the range 0-144+ FPS (not counting FPS not displayed on the monitor, also modern games use surely more complex ideas than mentioned in this article). The maximum amount of FPS a computer can display depends on the specifications of the monitor. So, a monitor with a 60 Hz (hertz) refresh rate can show up to 60 frames (frame changes) per second.

Why do you want to limit frames?

One reason why you would want to do this is that some calculations and attributes of your game (flying projectiles) can be frame-dependent instead of time-dependent. So when things are frame dependent and you would be able to theoretically achieve hundreds of FPS and during heavy workload drop couple of times, your game’s speed would be inconsistent.

Why make something frame-dependent?

It’s a tradeoff and you have to decide for yourself and your application. Our decision making in this dilemma was that the board itself is not extremely capable and we wanted to make for random freezing, slowed down game, and things like that. Because when you make something time-dependent (real-time dependent) you introduce bounding to something external. Imagine a person (your game calculations) on a bicycle (your visible game) holding onto a truck (time). You cannot stop a truck and when your game freezes and unfreezes you might encounter unwanted things like teleportation of projectiles, in this case, a human frozen in time would teleport behind a truck even though it froze for a while and the truck is already a couple of meters away. Real-time doesn’t freeze/slows down (not taking into account the theory of relativity).

So one way to implement this is to before each calculation (iteration) start a timer and after each iteration check how long did it take to complete the calculations. If it took shorter that it should you introduce a pause and after this pause, you move on to the next iteration.

This is an implementation for serial processing (each part of the game is calculated one by one).

How big of a pause to introduce?

Well, if you want to have 30 FPS (and your system is capable of 30 FPS), then you can calculate how long should one iteration take. (30 FPS -> 30 frames per second, 1 iteration (with frame display) should take 1/30 seconds ~ 0.033 seconds).

After this is done you should have your game running at 30 FPS.

When you are creating something from scratch you also have to program physics. That’s the beautiful thing about existing game engines, the physics is already there.

In our game, physics composed of speed, gravity (vertical acceleration), wind force (sideways acceleration), collision (checking if your projectile hit either player, ground, or display walls).

When your game goes through the calculations it doesn’t have a concept of how fast is something moving, you have to store and update it in memory.

Let’s say you fire a projectile in one frame and it should have some initial velocity let’s say 1 pixel per frame (frame-dependent attribute) in the direction you are firing. When you arrive at the next frame you have to know by how much to move it and in which direction. The direction in a 2D game consists of 2D vectors (x and y coordinate pairs). Your initial velocity, if you are aiming let’s say 30 degrees of the ground, should be calculated using trigonometric functions and then converted into y-axis speed and x-axis speed.

Then after each iteration, you take this speed and add that value to a projectile position (x=5, y=10), in this case after each iteration you add (x + x-axis velocity, y + y-axis velocity). The same would go for negative speed which would just change the direction.

In the previous section, we talked about velocity and position change. We also want our projectile to behave more realistic so they should move along a parabolic path.

This parabolic path is created with help of gravity (downward acceleration). Acceleration is how much speed we gain per second. So if we fall to the ground at a current speed of 1 pixel/second and our downward acceleration is 2 pixels/second^2 (that means that after each “new” second we gain a downward speed of 2 pixels/second after 1 second we would be falling at rate 3 pixels/second, after 2 seconds at speed 5 pixels/second.

As you can see in this picture, we can already see a parabolic path thanks to gravity.

Wind works similarly to gravity, (in this example not in the real world). We introduce “sideways gravity” or sideways acceleration. If we add this option, our calculations will look like this.

So we can see that our parabolic path is getting slowly deformed and if the wind would blow even stronger (or after some time) we would see that our projectile would head to the left.

I implemented wind as an optional setting in the game and I had preset of 7 speeds for the wind, zero, then 3 power levels to the left and right.

I didn’t implement real-time trajectory calculations for my game. My flight path is created at the beginning of the movement right after a player shoots the projectile. The projectile then follows its path and computes collisions.

How does a game know if it collides with something? The game has to calculate each iteration its distance from its surroundings, other players, terrain, etc. Then it has to compare those distances to the threshold you as a programmer provide. You decide when you are writing your code how much distance there should be before something is considered a collision. It can be distance as small as 1 pixel but high speeds and smaller thresholds introduce many funky problems you will have to account for if you are aiming for high precision.

Possible problems you might encounter might be that when you are moving at such high speeds you will have to have far more time points. Time points are in my game points which together create flight trajectory (parabola). So, I will create a trajectory at a much denser rate (x coordinates move just a bit) beforehand, and then only a fixed small portion of these points are showed on display but all the points are used to calculate distances.

Since I wanted to have multiple weapons I knew I will have to have some template and all the weapons will have adjustable attributes. Each weapon had their own specific ID (0 for cannon, 1 for sniper, …), and their attributes were defined in an array the game had access to with that specific id (each tank instance had their own current weapon ID). After firing, the physics, rendering and everything else connected to shooting was based on that ID.

The way I created “lighter” projectiles (objects which accelerate slower and are more influenced by the wind) was that I introduced multipliers. Multipliers are numerical variables for each type of a weapon/projectile and as we discussed earlier after each iteration we move an object by its speed and add global downward acceleration, optionally a wind sideways acceleration. In my case, I would add speed to my position, and then I would change the speed by a formula like this speed = acceleration * multiplier. So if we want to have a projectile that is slowly accelerating downward we will use a gravitational_multiplier of 0.2 for 20% of downward acceleration and for strong wind acceleration we will use f.e. 3.0 for 300% wind acceleration (compared to the original).

We wanted to have destructible terrain and our terrain in the Worms-clone game is a 1D array which size equals to the width of the screen and each value in the array is encoding the height of the terrain at that particular column (screen can also be described as slices of columns and rows). This implementation was a sacrifice of future development for time (it will be much harder to change it to a more robust representation that can do even multiple layers of terrain and more complex and realistic destruction).

When it comes to destruction, after an explosion (collision of the projectile) you try to figure out if there is some terrain around you (check if you have a higher height than terrain as well) and If there is a terrain, you calculate the distance how far away it is (distance from a projectile position and a top column terrain position). If it’s very close, the damaging effect (subtracting height of the terrain at a particular column in its representative array) will be higher and if it's further, the damage will be smaller, (you can do it linearly) and this creates a crater.

How did I render my backgrounds, tanks, text and terrain ?

First of all for background and tanks I’m using PNG images. These images are located in my directory and to load them I use external library by lvandeve (lodepng - https://github.com/lvandeve/lodepng). This library helps me to decode PNG images into RGB arrays. Then I use simple function to copy contents of these arrays to my screen.

How come you have non-rectangle objects in your scene (tanks)?

All images come in form of a rectangular array. I needed to introduce transparency in some way. The way I did it is I chose 1 specific combination of RGB values, let’s say (1,1,1) and while copying contents of array onto my screen I check if any RGB pixel has the same color as my transparent combination (1,1,1), if so, I ignore this pixel and don’t overwrite the existing pixel. These colors have to be manually edited in some photo editing software.

Every rendering in the scene has its ordering. Rendering happens every frame. We start with an empty canvas and then we paint it with background, then we continue to add terrain, then add tanks, text, tank barrel, health indicators and lastly projectiles. If for example the background was the last one to render, the only thing you would see would be the background.

How do you render text?

For this we used files we received from professor. The files contain basic fonts of certain size in a text file coded in 1’s and 0’s. So number 1 would probably look similar to this.

Height:8 Width:5
0 0 0 0 0
0 0 1 0 0
0 1 1 0 0
1 0 1 0 0
0 0 1 0 0
0 0 1 0 0
0 0 1 0 0
0 0 0 0 0

You would just make a function which can read the text file and print appropriate words/sentences at a particular starting point. Some students even implemented font scaling.

For this, you should have some experience with existing game engines. Each entity (player tank, projectile) should be represented as a separate object with its attributes which corresponds to its purpose. However, it should have some common attributes as well, position, speed, etc. Then when you want to calculate distance from something or do physics it’s as easy as iterate through a list.

In my case player object was a struct datatype. You have to be able to distinguish between a player and its team. For that, I just had under team attribute some arbitrary number like 1,2,… for each team. Then each player object also has its health, available moves (at the start each player has f.e. 50 moves available which just means that he can move 50 pixels total, left or right), position, tank barrel angle, variable that indicates if it’s alive (can be figured from health though) and that’s mostly it.

I was inspired to introduce a game manager to my game as a result of my previous experience with the Unity game engine. A game manager is something that looks above and beyond everything in the sense that it’s a variable that doesn’t change over time much and consists of links to other variables and lists (players, tanks, projectiles), other scenes (main menu, game) and can hold all the objects, generally, at least in my game it’s a go-to variable if I want to get some information about the game status (it is over ? In what scene are we now ? …).

A game manager is just a variable that holds other variables, general settings, and some flags about the game status.

Speaking for myself. My logic for changing scenes, which was scene dependent, was executed in each iteration and it went something like this.

While in-game: Did all players from one team die? -> Show winning/losing screen -> wait few seconds -> go to the main menu.

While in the main menu: Did the user pressed the play button? -> Start a new game with settings adjusted in the main menu.

And that’s it.
The logic that manages different settings work like this:
Are we in the main menu? -> Did the player chose to change the number of tanks? -> Change the general settings in the current scene -> Did the player changed the wind? -> Change scene current settings (even though we are in the main menu) -> Did the player start a game? -> Start the game map and load all the customizable settings from the menu to the current (new) scene.

Making a game without a game engine is unnecessarily complicated in ways that it should not be, you should focus on making a product not making tools to make that product. So I would always advise to use existing engines and trying it out by yourself only for educational purposes and there is no way around (speaking to single/small size indie teams, I have no experience in making a bigger game besides small projects).

Either way, making games can get complex, especially after many features and additions. You have to have some kind of system, have a structured and easily changeable code, don’t repeat code as much as possible (although without classes it’s much harder), always be vary of decision like “I could make it quicker but it will be little messier or it would take me longer but in the end, it will be easily programmable” if you have a long way to go, chose easily programmable/changeable every time, especially when making foundations, it has a ripple effect. It will save you hours and hours down the line (it’s not time-efficient to program quickly even if it seems to be, one of the paradoxes I stumbled upon). You could make some quick quirks at the end. The fewer things that depend on these last changes the more time you will save. You don’t want to build a concrete home and halfway through adding sand blocks or straw, add these at the end if necessary, but those are general guidelines in programming which can be surely applied in other disciplines.

This school project took about 3-4 days of intense work during final exams (sleeping, eating, working all day) and it was surely a valuable experience. Even though the code itself is not in a stage to be presented I want to make it public regardless. I hope you enjoyed this article and it gave you some bird-view into the process.

Code: GitHub
License: Apache license, Version 2.0

Older article about making an android game in Unity can be found here.