Z. Cornière
Z. Cornière Zacharie Cornière est étudiant de la promotion 2023 d'Epitech (Lyon). Il est passionné par les langages de programmation et le développement de jeux vidéos.

Developing a Descent FPS Game Using SFML

Developing a Descent FPS Game Using SFML

I found the YouTube channel of OneLoneCoder, especially a video about building a First Person Shooter (FPS1) from scratch. The video was really interesting, and at the end of it, I had a working FPS using SFML2. But I was lacking frame per seconds. So I improved it. And then, I realized that I was displaying on a really small window (200x200), so I extended it. And I added enemies. And so on and so forth…

In this post, I am sharing my experiment (and my code) for the curious.

Technologies used

I used the SFML to open a window, display the frames, and handle events. When it was time, I used it to load textures – but I finally used the libpng3 instead. I ended up trying to remove the SFML from the raycasting as much as possible, in order to allow bigger changes in the future (like OpenGL4 or Vulkan5 calls)

The Magic

In order to display something, we need to cast rays. Each screen column is the result of one ray. By doing so, we get the distance between the player and the walls, allowing us to split the column into three parts (sky, wall, ground), and texture then accordingly.

The DDA algorithm

You really should watch this guy :)

Parallelisation

Because the Raycasting is running on the CPU, if it is simple-threaded, it can be very slow, so I needed to add some parallel computations in the mix. Since I was in modern C++ (cpp206), I could use the std::async and std::future classes. It allows me to do asyncronous operations. But the standard library does not include an async executor (as of March 2021), so I made a ThreadPool class to execute async functions. This threadPool ended up being very useful even for non-rendering processes : like collision computation and entity updating.

Optimisation

The rendering code need to run quick, in order to maintain 60 frames per seconds. My first optimization attempts was therefore leaving g++7 for clang++8 (+ ~10fps) and enabling for speed focused compiler optimization (+ ~2fps). Then I wandered into the code, passing references as much as possible, exited loops early, reducing function calls, inlined lots of them, etc. This was really interesting, because it allowed me to dive into the C++ runtime behaviour and I learned new/better ways to do things, like:

  • Avoid using operator+ on complex type, and prefer the operator +=
  • Limit the amount of local variables to fit with cpu registers capacities
  • etc..

The subject is really complex and deep but very fascinating.

The Future

I want to use Vulkan to make use of graphics cards in order to do the rendering.

I also want to use BSP (Binary Space Partitioning) to render the space, just like the first Doon used.

I would like to improved my threadPool to allow having futures inside futures.

For now, a working code + building instructions are available on GitHub9.

play

References

Rating: