How TDD helps me make RTS / Habr

How TDD helps me make RTS / Habr

Not my RTS 🙂

Hello, Habre! My name is Igor and I am a Unity Developer. In this article, I would like to share a case study of how Test Driven Development helps me develop an RTS game.

Usually, in the development of mobile projects, I have always done without unit testing and thought that writing tests is a waste of time and effort. Well, like, why? “Mechanics in mobile games are therefore primitive – what is there to test? I’d better go and make the next feature. So the deadlines are burning!”

So for the past three years, my approach to game development has been pretty simple: “Follow the principles of SOLID-KISS and everything will be OK!” In general, it was like that until I started doing a big project…

When I started doing the mechanics for the strategy, I suddenly realized what a huge amount of work needed to be done just to implement the mechanics of moving units around the map. It turned out that the foundation of these mechanics is built on various mathematical and logical algorithms, which are combined together by game logic. Thus, to write a good pathfinding algorithm, you need to write at least an A* algorithm and a linear tracing algorithm, and those will be based on the implementation of the cell field that the units will walk on. In addition to this, it will be necessary to do optimization.

In general, I understood that I will not be able to write such a solution from scratch, so it will be necessary to decompose it into simpler tasks and solve them step by step, checking the operability of each. It was not very convenient to test the functionality by running PlayMode in Unity every time, and then I remembered that there is TDD to save me!

For those who don’t know, Test-Driven Development (TDD) is a software development methodology that involves creating tests for code before writing the code itself. The TDD process is an iterative cycle that includes the following steps: Write the test -> Write the code -> Refactor the code.

  1. First, the developer writes a test that defines the expected behavior of the function or module that he is about to write. This test usually starts by showing how the code will be used from the perspective of another part of the system.

  2. The developer then runs that test, and it should fail because the corresponding code doesn’t exist yet.

  3. Then the developer writes the minimum amount of code that will be necessary to pass this test. The goal is to make the test pass so that the code performs the expected function.

  4. After writing the code, the developer runs the test again. The test should now pass, confirming that the code is working correctly.

  5. After a successful test, the developer can perform refactoring, improving the structure and readability of the code, but without changing its behavior. Tests ensure that changes do not break existing functionality.

This process is repeated cyclically for each new feature or module added to the project.

As a result, the algorithm was implemented. Defined a list of tasks and began to solve them one by one according to TDD:

  • Task #1. Implementation of the cell field. Under the hood it is a bool matrix[x,z]where the value of each cell preserves the permeability of that cell. Each cell is 1×1 in size, so the cell index indicates its position in space on the XZ axes.

  • Task #2. Implementation of algorithms for working with the cellular field: checking for cell patency, obtaining neighboring cells and corner points for constructing a path graph, etc.

  • Task #3. Implementation of Algorithm A for path finding. Here I wanted to make sure that the array-array idea worked and that the units moved correctly through the cells.

  • Task #4. Implementation of the linear tracing algorithm by the cellular field. This is necessary so that units do not walk in cells, in those situations where it is possible to walk directly.

  • Task #5. The final implementation of the cell field-based pathfinding algorithm combines A* and linear tracing.

After implementing each method, I wrote several tests that check its performance. Thus, small iterations resulted in a solution of two classes: GroundArea (cell field) and GroundPathFinder (path finding algorithm):

Tests for the GroundArea class

Tests for the GroundPathFinder class

GroundArea tests were required to test the performance of the various methods. Therefore, I made a separate class GroundAreaSubstitutions, in which I noticed different variations of the cell fields:

Plugs of cell fields (Substitutions)

The pathfinding tests themselves looked like this:

Wayfinding tests

After successful tests, he created a new Unity project where he tested the performance of the system on 80 units. After seeing spikes in the profiler window, I optimized it.

Test project for testing the wayfinding system

When optimizing the algorithm, I made several mistakes that I immediately saw in the tests:

The pathfinding algorithm tests failed

After eliminating all the errors, I got a separate GroundSystem assembly that can be used in a Unity project together with the Mathematics module:

Ground System module files and dependencies


Thus, I realized that TDD is absolutely necessary for the development of complex systems and algorithms. Here are the advantages I felt:

  1. Confidence in the functionality of the program. Thanks to the tests, you can quickly find the error and eliminate it. This is especially useful when the algorithms are closely related to each other and changing the code in one may cause errors in the other.

  2. Writing TDD code makes the system simple and understandable. As a result of the development of the wayfinding system, I got only 3 classes, which can be seen above on the chest.

  3. Feature modularity using Assembly Definition. Such a system can be easily used in other projects. At the same time, when the scripts are in a separate assembly, it is very disciplining to use only the necessary dependencies.

  4. Sense of progress and feedback in development. This psychological moment that allows you to feel well done 🙂

That’s all, thanks for your attention 🙂

It is possible to improve the skills of a game development developer, and in particular, to learn practically the TDD methodology on courses in Otus.

Related posts