PC Games
• Orb Tutorials
• 2D shoot 'em up Latest Updates
SDL2 Versus game tutorial
Download keys for SDL2 tutorials on itch.io
The Legend of Edgar 1.37
SDL2 Santa game tutorial 🎅
SDL2 Shooter 3 tutorial
Tags • android (3) • battle-for-the-solar-system (10) • blob-wars (10) • brexit (1) • code (6) • edgar (9) • games (43) • lasagne-monsters (1) • making-of (5) • match3 (1) • numberblocksonline (1) • orb (2) • site (1) • tanx (4) • three-guys (3) • three-guys-apocalypse (3) • tutorials (17) • water-closet (4) Books The Honour of the Knights (First Edition) (The Battle for the Solar System) When starfighter pilot Simon Dodds is enrolled in a top secret military project, he and his wingmates begin to suspect that there is a lot more to the theft of a legendary battleship and an Imperial nation's civil war than either the Confederation Stellar Navy or the government are willing to let on. |
— Making a 2D split screen game — Note: this tutorial assumes knowledge of C, as well as prior tutorials.
Introduction It's time to make our game a little nicer to look at. We have a lot of the core gameplay elements in place, but there are things missing, such as explosions, impact effects, and other things. In our game, these will all be displayed using a particle system, created from triangles. Once again, there won't be an textures in our game. Extract the archive, run cmake CMakeLists.txt, followed by make, and then use ./versus09 to run the code. You will see a window open like the one above, with each player on either side of our zone. Use the default controls (or, at your option, copy the config.json file from a previous tutorial, to use that - remember to exit the game before copying the replacement file). Play the game as normal. There should now be a load of particle effects visible on the screen, while flying your ship, firing rockets, dying / respawning, and when bullets impact the environment. Once you're finished, close the window to exit. Inspecting the code Adding in a Particle system is something we've done many times, in many of our tutorials. The process here won't differ very much from those. Once again, the major difference is that we'll be rendering our Particles using triangles, instead of textures. Starting first with structs.h:
We've added in a Particle struct. This struct, unsurprisingly, represents a particle. We're storing the `position`, `dir` (velocity), `size`, `color`, `health`, adn `angle` of the particle. rotationSpeed is the speed at which the partcle spins. Our Particles are part of a linked list. With that setup, we can move over to particles.c, where the bulk of this update has taken place. Starting first with initParticles:
Our Particles are stored in a linked list, so we start by setting up the `head` and `tail`. Next, we set two variables to 0. playerEngineTimer is used to control how often we'll produce a particle for the players' engines. The function addPlayerEngineParticles is called every frame by a Player's `tick` function, but we don't want the trail to be bound to our frame rate. Therefore, this timer will help us to control how often the trail is produced. A second variable, rocketEngineTimer, controls how often we'll produce the trail for the rockets. We'll see these two variables in action in a little while. Next up, we have the doParticles function:
Nothing out of the ordinary here; it's like many particle processing functions that we've seen before. The particles are moved by their `dir`, and their `health` is reduced. Particles are deleted once their `health` hits 0 or less. Of note here is that we're updating the particle's `angle` by its rotationSpeed. This is to make the particle rotate, if the rotationSpeed is not 0. Also of note is that we're processing our playerEngineTimer and rocketEngineTimer variables here. We're decreasing their values upon each call to doParticles, and if they fall below 0, we're resetting them to 2 and 0.25, respectively. Note that the order in which we're decreasing and resetting these variables is important, as we'll see in a bit. Again, nothing special. Now let's look at drawParticles:
Once again, there's nothing difficult here to understand, as our particles are just a bunch of triangles, of variable sizes. Our particles support additive alpha blending, as in our other games, so we're first setting SDL_SetRenderDrawBlendMode to render that way. We're then looping through all our particles, creating an equilateral triangle of the required size (stored in 3 SDL_Points called `points`), and then using those points to create 3 vertices. The vertices are rotated by the Particle's `angle`, and its `color` set. Since we want our Particles to fade as they age, we're adjusting the alpha value of the colour, according to the Particle's `health`. As with the other things in the game we render, we take the camera position into account. Finally, we pass the SDL_Vertex (`v`) to drawVertex, before resetting our blending mode at the end of the loop. So far, pretty standard. The remaining functions won't pose much of a challenge, either, so we'll only take a high level look at them. Starting first with addPlayerEngineParticles:
Here, we can see the playerEngineTimer put to use. This function won't do anything if the value is greater than 0. This is why in our doParticles function we need to reset the value BEFORE decreasing it. If we do it the other way around (as one would expect), this function won't work, since playerEngineTimer will never be 0 when it is called. As for the rest of the function, we're using a for-loop to create 5 particles. When it comes to calculating the direction of the Particle, we're taking the Player's (`e`) current `angle`, and issuing the Particles in the opposite direction; we want our Particles to appear at the back of the ship, after all. With that done, we add some randomness to the `dir` vector, and set the `color` to a light blue. So, our particles will be created at the back of the player's ships, with a velocity moving away from it. Just as you would expect an engine to behave. Over next to addRocketEngineParticles:
This is very much like the function for adding our player engine particles. Here, we're making use of rocketEngineTimer, and giving the Particles a random red-yellow hue. As stated before, the next few functions are all very similar in nature, so we won't linger on them. First, we have addBulletHitParticles:
This creates a handful of Particles about the bullet's (`b`) `position` (where it would've hit the environment / entity). Their various attributes are randomly set. addRocketHitParticles comes next:
Here, we're creating a large number of Particles, about the point of the impact of a rocket. The `dir` of the rocket is much greater than the other functions, to emphasise the blast. The `size` of the Particles is also much larger (between 4 and 11). Once again, they are given a random red-yellow hue. addDeathParticles comes next:
This creates a large number of Particles at the given `position`, with the Particles being coloured using the RGB values passed into the function. The colour is randomly adjusted by a small amount, to add some variation. The last creation function is addRespawnParticles:
Once again, this creates some random Particles at the given `position`. The final function is spawnParticle:
This doesn't need much explaining - it just creates a Particle and add it to our linked list. So, particles.c is pretty much par for the course! We've seen it all before, and other than the rendering of the Particles, there isn't much else that needs detailing. Let's move on to see how all this is used in the rest of the code. I'm certain it won't bring up any surprises. First, over to zone.c, where we've first updated initZone:
Here, we're calling initParticles. Next up, we have `logic`:
We've added in a call to doParticles. Finally, we have `draw`:
As expected, we're calling drawParticles here, passing over the camera (`c`) we wish to work with. That's zone.c done. We can now briefly look at where we've added in our various Particle function calls. First, over to player.c, where we've updated `steer`:
We're calling addPlayerEngineParticles here, whenever the player is flying. Notice that we only call the function when the player is applying the thrust control, rather than all the time. We want to give the impression that the engines are active. Next up, we have the `die` function updates:
As we saw earlier, the addDeathParticles function takes RGB values. We're making use of that here, producing some light blue particles when player 1 dies, and light green ones when player 2 dies. This reflects the colour of their ships. That's player.c done, so we can move over to entities.c, where we've made a tweak to doDead:
Whenever an entity respawns, we're calling addRespawnParticles, passing over the `position` of the entity. Right now, this will only affect the two players. The changes to bullets.c comes next. First, we have doBullets:
While processing our bullets, we'll test if the current bullet is a rocket (its `type` is BT_ROCKET). If so, we'll call addRocketEngineParticles. Again, the rocketEngineTimer will control how often these particles are produced, so we are not bound to our frame rate. bulletDie comes next:
Here, we're now calling addBulletHitParticles, in addition to setting the bullet's `health` to 0. Finally, we have rocketDie:
We're calling the addRocketHitParticles function. Our particle system is done. There wasn't anything here that taxed the mind; the more interesting parts were the timers for the engines and rockets, and also the rendering. Everything else was very much as we've seen before. It has made our game a lot prettier, though, and adds in some missing impact. You're likely feeling there is something else missing our game, though. How about some power ups or items to collect? Perhaps some points pods, that the players can hunt down. I agree, this would be a good thing. So, in our next part we'll be introducing items and other collectables. This will expand the gameplay of our game, and add in some interesting elements. Purchase The source code for all parts of this tutorial (including assets) is available for purchase, as part of the SDL2 tutorials bundle: From itch.io |