NB: English is my second language. There is Russian language in videos and screenshots.
There are a lot of “teams” that have existed for years. Their list of developers consists of dozens of people, plus, “everyone needed!”. But they don’t have any adequate demo planned even for the nearest future. Looking at all of this, I feel like saying: “What are you doing, damn it?”. But it’s destructive. It’s not enough to criticize. I should provide positive examples.
These considerations led me to an interesting experiment. I came across a flight simulator competition. It involved two months of development, but I thought that it was too much. That’s why I decided to take part in it. And… Forgot about the competition for a month and a half.
The basic idea of the experiment is to develop a complete project within two weeks. The completion date of the competition is a good deadline. Besides, the last week of the competition was at the end of the New Year holidays, so I had more time to spend on it. Thus, there’s a competition, a project from scratch, and two weeks.
If you don’t feel like reading the entire article, just scroll down. You’ll find a review of projects and a brief summary of the entire experiment.
So, we have two weeks. The main question is what to do. To tell the truth, I planned to spend just one week on it.
One week is not a lot of time. That’s why I chose the genre of “launchers”.
Launcher is a game, where all the actions happen in a single level only. In fact, the process of passing the game is a series of tries to fly further/higher. Each try (except for the last one) is doomed to failure, but it gives you the points to level up the character, so that it could fly better. The game ends when the character is leveled enough to reach the end of the level.
Butibo Bison is a great example of this genre.
I was planning to repeat the gameplay of this game. However, when I decided to clarify the details of the competition, it turned out that I had not 7, but 12 days. Great! We’ll make a 3D Action!
We’ll use some of my favorite games as a basis: Vangers, A.I.M., Ex Machine and Renegade Ops. It would be nice to make an RPG, but we’re short of time for this. Therefore, we’ll take the gameplay of RenegadeOps/Vangers as the basis, and a set of quests by the analogy with Ex Machine/A.I.M. Not to waste time on inventing the world, the action of the game will take place in the world of A.I.M.
What do we have here? My own framework, which have been used to build several 2D games. As for the 3D features, we can only load the mesh from a file. There’s also a landscape editor. We use it to design the game landscape. The task for day 1 is as follows:
- Create a test landscape and display it in the game.
We’re going to need a 3D camera for this purpose. It should be able to set the view and projection matrices. I didn’t have to write the camera class, as it had already been implemented (during the years of development). Just drag the camera to the framework.
The next task is to move shaders from the editor to the game. Shaders are easily accessible. All we have to do is remove the editor’s tool processing: the backlight of tile edges, the dynamic calculation of normals and other stuff.
The main task that took most of the day was the implementation of a class responsible for loading the file with the landscape and passing geometry to the renderer. The task was not too difficult, but it took half a day.
So, what do we see here? The landscape has some artifacts. The reason is very simple: while converting textures, I forgot to add the generation of MipMaps. But it was already 11 p.m. and I really wanted to sleep.
There are two tasks for today:
- Work on Shadow maps.
- Draw models (without models, we cannot test the work of shadows).
The first thing I do is enable MipMaps, which I forgot to do yesterday. Turns out, something generates an OpenGL error in the code. There are not many checks in the code, and the error pops up during the load of shaders. I think it’s due to the shaders, improperly transferred from the editor. It took me an hour to figure out that the error was not in shaders… Turns out, it’s the wrong setting of textures, and the GL_LINEAR_MIPMAP_LINEAR value was passed to MAG_FILTER, and it should not be there. I change it to GL_LINEAR and everything works now. But I spent two hours on this, and it’s quite a lot of time, as I only have 12 days left.
Now, let’s take care of models. Actually, I have a simple model loader, but it’s not enough to know how to draw a model on the screen. The game should draw only models that are in the camera frustum now. The easiest way to do it in our case is to divide the playing area into squares. A square is approximately equal to the area visible on the screen.
The landscape is already divided into such squares. Therefore, we create an object manager that divides the playing area into squares according to landscape settings. When drawing, it processes only objects being not further than a square away from the camera. That is to say, we draw a 3х3 square area, in which the central square is the one containing the camera. Plus, the object manager should trace the movement of objects, and in case an object goes beyond the borders of its square, we should move it to the relevant square. It took me 5 hours to write this manager.
The next task is to implement the Shadow Map. But, the Render to Texture support in the framework is required for this purpose. I did not have it, as my previous projects didn’t require post-effects, and RTT in 2D is not used anywhere, except for post-effects.
I spent an hour on the implementation of a wrapper for FBO. It wasn’t difficult.
Let’s get down to the last task of Day 2: Shadow Maps. But the thing is, I prefer Stencil Shadows… I always avoided using Shadow Maps. I just don’t like them. But the time goes by. Actually, there is not a single reason to use Stencil Shadows nowadays, as performance falls significantly with the increasing complexity of geometry and screen resolutions. Thus, there’s no alternative and we should implement Shadow Maps. I definitely know the theory of their operation, but never had a chance to implement them. Therefore, I do not expect much difficulty. I took the manual and implemented shadow mapping.
By the way, I used this tutorial. Would recommend it. Everything is written clearly and in sufficient detail. The basic problems we face when implementing Shadow Maps are also described in there.
The landscape looks better with MipMaps, and shadows look nice as well.
Let’s continue to add the necessary functionality. There are two tasks planned for today:
- Integrate the Lua script language with the help of the LuaJIT project. Not so long ago, an updated version of the JIT compiler for this language has been released, and our experiment is a great opportunity to use it.
- Integrate the particle system. I was planning to use Magic Particles by Astralax.
Let’s start with LuaJIT. Download the source code and easily build the dll for Windows. Write a small wrapper class (I don’t like luabind and its analogues and prefer to work with lua via a minimum number of layers) and I guess that’s it.
I tried to build it for Android, but could not do it in an hour and decided not to waste time, as it’s not so important for the competition.
The second part of the plan for today is the integration of the particle system. I worked a lot with Magic Particles by Astralax, as it’s really hard to find something more powerful and easy in this area. Plus, the developer is adequate, which is important for development.
But one unimportant for the competition, but important for the framework, thing stopped me. The thing is that the framework is created for projects under Windows CE. Unfortunately, it’s almost the only common OS that is not supported by MP. This is understandable, as the gaming market on WinCE does not exist as a class. But it’s important for me. Therefore, I decided not to use MP for now…
Being upset, I decided to play Burito Bison and spent one and a half hours on this.
However, the day was far from over, and it was barely afternoon. So, I decided to make the start menu. Thus, I created a simple landscape and placed a few buildings and two helicopters on it. That’s when I got a bug with Shadow Maps. It is clearly seen on the corner wall, at the left, under the “Exit” button («Exit» is the last button in the menu). I decided not to fix this bug, as it will not be noticeable in the game. But the process of fixing the aliasing of shadows is likely to kill few precious hours.
I made a menu that slides up and down, which is quite interesting. I added «Settings» and «Continue» buttons. However, I perfectly understand that I will not be able to implement their functionality within the competition due to the lack of time.
One might think that I wasted time on such a useless thing as menu animations. But it did not take much time, as the framework can make animations for UI. It took me 10 minutes to tell it what and when to animate. I think animations made the menu much more interesting.
Plan for today:
- Helicopter flight control
- Weapons: lasers, shotguns, missiles
I started to work on the planned tasks in the afternoon, something at around 4pm. Before this, I solved problems that are not directly related to the plan, but I couldn’t perform further tasks without doing so. One of them is about spawning a unit. For instance, a helicopter. Spawning requires working scripts that require level loading and writing appropriate functions, using which we can control the units.
When I finished with the helicopter control, it turned out that when I click on the button and start moving, the Qt mouse becomes slow as hell.
Like any sensible developer, I thought it was my fault. I rewrote the internal messaging system, and this did not help.
Ok. I decided not to use the regular messaging system and read the keyboard and mouse states instead. This did not help either.
I completely disabled the input system in the engine, and still had freezes. I made the WH_GETMESSAGE hook for all the messages related to the input, handling and canceling all of them… It did not help. I had nothing left but to examine the system outside my development environment.
The answer was simple… I spent three hours fixing the bug that did not exist… Synergy was slowing it down when I was holding multiple keys down at the same time. I removed Synergy, and everything became smooth and neat.
As a result, I didn’t have time to implement helicopter’s leaning to the direction of a flight. It seems a trifle, but still, it’s important. However, not too important to spend time on it now. Moreover, it’s been 4 days, and I only made the helicopter fly. Thus, I need to focus on shooting.
At 2 a.m., I recorded the video. Since I didn’t have time to make missiles, I decided that they were not as important as sleep.
The task for today is to make game locations.
I’ve got 10 buildings, which I bought on 3drt.com for another project. First, I wrote the gameplay, and then made a list and thought of the role each building could perform in the game. Some building looked like a mine, and another one like the main base, etc.
I sketched the approximate playing area in the landscape editor:
Note: UI is in my native language
I wrote the name and the number of the model used in each part of the playing area,
After that, I drew boundaries of the playing area using mountains. I also drew some impassable mountains in the middle of the map, so that the player could not fly directly.
The next step is to add inhomogeneity to the landscape and build the road surface. Being short of time, I spent only three hours on it. This time was only enough to create a minimum playable landscape. I really didn’t have time for artistic details.
The next task is to set up objects and define terrain areas. I don’t have a suitable 3D editor for the framework, as it has always been 2D and all the projects have been 2D as well. Fortunately, I have an editor that has been created much earlier for 3D projects. It’s quite complicated and has a complicated saving format. Besides, I wrote it for a totally different engine, so I can’t use it directly. Anyway, it does not really matter today. I created a location in that editor.
I also set up objects and drew areas:
- Flight areas
- Safety areas (located close to buildings, you cannot shoot within such areas).
Moments in the video, when the landscape is colored red and green, — is area editing.
- Load the location created in the editor
- Provide functioning to all of the buildings (turret functioning and determining the “entrance” into the buildings)
We have the location in the editor, which is not designed to work with our game engine.
I decided not to bother with loading its complex format and made a simple thing: added an option to the editor to save a scene as a text file. Then, I saved the location to the text file, and got something like this:
The example of a text file with the level:
FILENAME TLevelStaticObject.Model.FileName \\buildings\bld10.sxm STRING TLevelBaseObject.Name CLIMAT_1 FLOAT TLevelBaseObject.Position.x 2248.16 FLOAT TLevelBaseObject.Position.y 2874.08 FLOAT TLevelBaseObject.Position.z -0.355 FLOAT TLevelBaseObject.Rotation.angle 0 FLOAT TLevelBaseObject.Quaternion.x 0 FLOAT TLevelBaseObject.Quaternion.y 0 FLOAT TLevelBaseObject.Quaternion.z -0.732 FLOAT TLevelBaseObject.Quaternion.w -0.68 BOOL TLevelBaseObject.ShadowCaster false
Now, we should write a simple utility, which takes only the necessary features for the game from the complex text file and saves them in an appropriate format.
Then, we should load zones and objects. Decoration objects are simply added to the object manager written before, and the game forgets about them. As for other objects, we create them in the usual way.
In general, the process of converting and loading locations took a small part of the day. I spent more time on the implementation of turrets’ functionality. Their work is simple: if the player shoots in the safety area, the timer is activated for 15 seconds. During this time, all turrets open fire on a player, which is in their radius.
- It’s time to implement the artificial intelligence.
I already have the artificial intelligence able to perform various functions. I made it several years ago, having an idea to implement the AI competition. Actually, I implemented a 2D model of the A.I.M. world with its rules. Merchants got points for the delivery of goods, guards – for the support of merchants, hunters – for the brought heads, looters – for the stolen (not bought) and sold goods.
Let’s see how this AI worked:
Red letters are dead units.
Circles are living units.
There are two teams: green and red. They’re actively trying to represent the living world.
We won’t be able to transfer the AI directly. The thing is, a unit in the A.I.M. world is something hovering and shooting in front of it. Thus, there’s no problem with the turning radius, as there’s a way to move sideways. In this project, the situation is different: a unit is a land vehicle with wheels and all the attendant problems.
I spent the whole day creating the AI.
There’s no video for this day, as I had nothing to show. However, I added an option to create units with the help of text files (I made the editor for this after the competition).
Config of a regular unit looks something like this:
FILENAME VERSION 1 TYPE CAR NAME GUN_JEEP SCALE 1.0 BODY vehicles/jeep_1/body.sxm WHEELS_COUNT 4 MODEL vehicles/wheels/wheel3_left.sxm POSITION 3.581 -4.362 2.024 MODEL vehicles/wheels/wheel3_right.sxm POSITION -3.537 -4.362 2.024 MODEL vehicles/wheels/wheel3_left.sxm POSITION 3.581 3.387 2.024 MODEL vehicles/wheels/wheel3_right.sxm POSITION -3.537 3.387 2.024 SPEED 50 ROTATION_PER_METER 1 STORAGE_SIZE 2 HEALTH 4000 SHIELD 4000 SHIELD_REGENERATION 50 WEAPONS_COUNT 1 TYPE GUN_TURRET_1 POSITION 0.013 3.251 6.755
During this day, the game learnt how to parse all of this, and also load data. Actually, there are two such configs. The second one is for describing turrets that are placed on units. In this case, we place the GUN_TURRET_1 turret.
- Continue to work with the AI.
So, our units can now move and attack, as well as pursue the enemy in sight. Most of the work was devoted to movement.
I decided not to add shock absorbers, as I simply did not have time for that. Thus, I won’t work on obstacle avoidance, physical interaction between units, and suspension. The latter upsets me most of all. Without the suspension calculation, units repeat the landscape and it doesn’t look nice. However, they perform their functionality, so we’ll leave it this way.
I planned four types of AI for the game:
- Merchants: buy goods and sell them in another building.
- Guards: follow the merchants and attack the enemy when they see him.
- Hunters: walk around the map and attack enemies; sometimes take heads of the killed enemies and friends to the base.
- Looters: walk around the map and attack hunters; sometimes take the found goods to the base.
I made three types of AI. I didn’t bother with looters, as there are no goods. Actually, there are goods: merchants buy and sell them. But I decided not to throw the goods out onto location if a unit dies.
Thus, there’s no sense in looters who would collect the goods next to the corpses. As a result, I created the AI of merchants, guards and hunters. Also, I fixed a few bugs and made the attitude of clans towards the player changeable, depending on his actions.
I also designed the HUD and created the inventory to collect the killed units to, and to throw them out from it, if necessary.
The final is close!
I think it makes sense to finalize the image. As can be seen from the result of the previous day, battles look fine… until someone dies. Units simply disappear, which doesn’t look well. There are two variants. The first one is to break the units into separate parts. This task is quite complicated in terms of the routine work on cutting units into parts and converting them into an appropriate format. I really don’t want to spend time on adding animations to the engine. The second variant is to use the particle system. But Magic Particles cannot work with WinCE. Should I integrate it to be used on other platforms? But I really want to avoid solutions in the cross platform framework, as they will not work everywhere…
In the end, I settled on the following solution: use Magic Particles, but completely abandon its native API.
This was done as follows. I implemented a converter that played the effects I created in MP. During each frame, it saved the state of all visible particles to a file. At the output, I got a file describing the state of the effect at every moment of time, but it wasn’t tied to the Magic Particles API. It’s a file I can play on any platform.
This variant does not cover the whole range of MP features, but it can deal with explosions and other simple effects. This is just what I need.
Somehow, the explosion turned out to be whitish, but I decided not to waste time on dealing with it. It’s far from «A+», I would give it a «C», but I have so little time left. Actually, I only have 36 hours left.
I decided to spend the remaining time on quests. The game definitely has some gameplay, but quests will give more sense to it, and maybe will make the player stay a bit longer.
Actually, there’s nothing much to write about.
Quests are hardcoded in scripts. There are no tools to edit relations or to set the script without coding. The code is pretty simple.
I made a dialog box to communicate with NPC (buildings perform their role). The principle of operation of the dialog box is identical to the quest dialog box in Space rangers. I think I could write a simple converter that can make text quests from Space rangers suitable to run in this game.
I decided not to create a mini map, as limitations of the world would become obvious, while I want to create the impression that the world is alive and big. But I also can’t leave the player wandering like this. Of course, some players may like this, but the most part will get lost at the first intersection and won’t return to the game.
Therefore, decided to implement the compass without the map. The compass indicates the direction of the current target. At the start base, we can add a mark to any building.
The video shows the process of implementing one of the tasks. Not to make it too long, I increased the speed of the helicopter.
I’ve finished the quests. One can pass the game to the end now. By the way, it turns out that the competition has been extended for two weeks, as most of participants voted for this.
Actually, it is contrary to my idea of developing a project in a short time.
Anyway, I decide to go to bed and sleep off. Plus, I will add two more days, so that I would have time to fix some bugs.
I did the following:
- Went through all the quests one more time.
- Added the respawn effect to a player.
- At the point of the player’s death, I created the effect of teleportation. There’s also an effect at the point of respawn.
- In addition, I improved pathfinding, so that units rarely ran into obstacles. However, they still ran into them with some probability.
- Added a few more types of units, or rather not types, as units remained the same. But before this, all the roles in all clans (hunter, looter) were performed by units that looked the same. I made each unit of each clan look unique.
- Fixed the AI a little bit.
The Last Two Days
There’s no plan. I do everything that comes to my head or catches my eye, everything I want to fix. There’s no video for these days, I made plenty of small changes, and the video would be too long.
Here’s the list of everything I did (I didn’t mention the smallest changes):
— Enabled the gamepad support. Actually, the framework for the interface can provide the gamepad out of the box, so I don’t have to invent anything new. I just added the direct helicopter control.
— Fixed a bug of the wrong calculation of the unit slope with regard to the surface. There is this moment in the video, when vehicles dig their front part into the ground. This is due to the incorrect application of the quaternion that describes the slope of the surface under the unit wheels.
— Made a simple calculation of the motion of units, so that they would not run into each other, I added simple collisions between the units.
— Fixed a bug of crashing when we work with the dialog box. It’s a bad idea to recreate elements of the box in the element handler.
— Added the marks of clans over the clan units.
— Shadows of the clouds. Seems a trifle, but it helps to change the flat world.
— The goods do not just appear on the ground, next to the place they have been thrown out, but scatter from the point of discharge.
— Added the teleportation of goods. When some unit picks the goods up, they do not disappear instantly, but smoothly, with the appropriate effect.
— Tested and fixed some minor bugs.
Thus, I made this game from scratch within 14 days, using the framework that could not provide 3D at the beginning.
Frankly speaking, I spent half of the time not on the game, but on implementing the missing functionality in the framework itself.
If I had built it all with some complete game development engine (like UE4 that has recently become free), I could have made the project even in 7 days, and the quality would have been better. This applies to almost any genre. In today’s world, you can build a playable prototype of any genre within a month of unhurried work.
If you meet people who will offer you to join a project based on the team spirit, and develop a Super-Mega-Killer-Crysis-WOW, just because they have an idea, but no real demo, you’d better run away. They are simply lazybones who did not bother to spend a week and $200 on asserts to create a prototype that would implement their idea.
P.S. I decided to debug and improve the project after the competition. Plus, I have decided to move everything to UE4 and focus on the development of the game, not on the engine for it. If you’re interested, I could tell you about how I carried out the transition to UE4 and what difficulties I faced with.
After reading this article, some people might think that it’s really simple to develop a game in a few weeks, and the ones who can’t are just lazybones. But it’s not what I meant.
Unfortunately, the 80-20 rule is still relevant. In a very short period of time, you can only develop a playable prototype that will reflect your ideas.
But it’s really far from the game itself. The speed of development falls with each new feature.
The first days of development are not the same:
On the first day, we have nothing. The next day, there’s a landscape. The difference between “nothing” and a “landscape on the screen” is huge. But still, it takes half of the day!
Now, we should add some trees. Well, a small forest.
“Seriously? The landscape takes 5 hours, while a forest on it takes a month?” Well, that’s how it goes. When we implement a forest, a number of questions arise. For instance, optimizations (we cannot just draw a forest, as we’ll face the lack of performance. So, we need optimizations, quite frequent and often nontrivial) and visual artifacts (suddenly, deferred shading does not want to work with blending, while the optimized tree leaves are implemented by blending). Not to mention path finding that has not taken into account the presence of separately standing small objects, which AI should avoid.
The development of a good and high-quality game is a complicated, long and meticulous process, the bigger part of which is not seen from outside.
I told you how to develop a demo version of your idea in a short time, but definitely not a finished product.
P.S. My game took 2nd place out of 10 projects.