Introduction
I've been the lead programmer of this project, and my work was focused on organizing the team of 16 programmers, distribute work, verify and ensure its quality and talk with the other departments to clarify doubts, contribute to decision making and help with questions about the functionality of the engine. Within the programming department I had spent most of the time talking with the groups of programmers of each scrum or talking with individual programmers to be sure that they were doing the assigned work, solve problems, help in the communication between different departments, help with errors and understanding of some code, and I tried to be available most hours of each day to answer questions or help in whatever happened. All of this occupied more time than I expected, but I have managed to continue programming during the project, I made the event system and the screenshot and gif system, I explain these tasks in the last section of this website.
In the following sections I explain the basic functionality of the engine that we have done for the project, the engine subsystems, quality processes followed, lessons learned and my work as a programmer.
Engine basic functionality
Culverin is the engine that we had created to make the game Unthrone. It uses SDL for the window and input system for keyboard and controller, the UI of the engine is imgui with docking, the maths are done with MathGeoLib and all drawing is done with the OpenGl graphics API.
When you open the engine, you can see a basic layout with the hierarchy of game objects of the loaded scene, a window to see the 3D representation of the loaded scene, an inspector window to see the editable variables and components of the game object selected, the time manager and finally, the resource manager and the console.
At the top menu bar you can see the following menus:
- File: There are options to create, load and save scenes and the option to create a build, which lets you select the initial scene and some options, and then it creates a folder with the build at the desktop.
- Edit: Option to center the selected object in the view of the camera scene window.
- Game Object: Access to creation of some built-in primitives, empty game objects, basic UI elements, scripts, shaders and materials.
- Windows: Option to show and hide the windows of the original layout, load audiobanks of Wwise, menu to view the loaded resources, window to see the ms that are taking each module to update, configuration window and information of the different modules. In this menu there is also the window of the Tile Map Editor, tool that we used to create the tiled maps of the game, both the artistic and logic parts.
- Help: Direct links to documentation, by clicking on them opens the browser to the appropriate website.
If you right click on a game object, a menu with basic options is shown, these options are copy, paste, delete, create game object (the same as the one at the menu bar at the top of the window) and component creation, this last action can be also done with the button at the bottom of the inspector.
Culverin imports geometry with the library Assimp and the models are stored in an own file format to speed up loading times of the asset, textures are imported with the library DevIL and stored as dds, and JSON files are read with parson library.
The scenes were serialized with JSON, but big scenes took 20-30+ seconds to load, so we changed it to a binary saving system to speed up loading times, now it goes about 55% faster. Another optimization is the culling system, at the beginning we had an octree, but because out game features tiled maps, we removed the octree and implemented an occlusion culling system, this sped up about 95% the culling search and drawing.
Many actions and the drawing are made with the event system, this helps to organize the code, sort the events and adds the possibility to run actions with delay. With this system, drawing can be compacted and speed up to get a better performance.
Libraries used by base engine:
- SDL 2.0.7
- imgui 1.53
- MathGeoLib 1.5
- OpenGl 2.1
- Assimp 3.3.0
- DevIL 1.7.8
- parson
- mmgr
Engine subsystems
The base particle system is the one I made with Sergio Alvarez, and for this project, Carlos Cabreira took and improve it, he adapted the system to the engine and to shaders, added options and fixed some errors.
The skeletal and skinning animation system of Culverin imports animations from dae files and from the engine you can add an animation component and an animation state machine editor to create the characters' locomotion, the skinning is accelerated with a shader. There is also the option of displaying sounds and prefabs activated by the animations themselves. For transitions between animations, there is a controllable system from scripting of blending clips, where you define two animations and the characteristics of the blending.
The scripting is provided by Mono, so the scripting language is C#. The system supports all the AI scripts and adds the links between the subsystems and scripts for their editing. All scripts are compiled in one dll before being used in the game, and there’s also the functionality to avoid a crash if any access to a script is nullptr.
The Culverin shaders system allows you to create and edit shaders and materials from the editor. The PBR shaders support lighting, dynamic shadows, glow effect and prebaked reflection with cube maps.
The Culverin UI can be controlled with keyboard, mouse and controller. Features image, text, button, checkbox, canvas and slider components. It also has an input System to link names and inputs to use in scripts, and it also features key binding from scripting.
The physics system is provided by PhysX of Nvidia and is integrated with scripting to be able to have OnTriggerEnter, OnTriggerLost and OnContact functions, along with a system to filter the game object by tags. There are collider, rigid body components that can produce triggers, colliders, rigid bodies and joints.
The audio of the game is provided by Wwise, from the engine different Wwise audiobanks can be loaded, also features reverberation areas and audio components to define emitters and listeners. It is also implemented that scripts can call Wwise events to be able to play sounds.
Libraries used by subsystems:
- Mono 4.0.30319
- PhysX 3.4.1
- Wwise 2017.2.4.6590
Internal quality processes
At the beginning of the project, there was a meeting with all the programmers and we decided the code style to have a more uniform and legible code.
A minimum of one or two times a week I reviewed the tasks pending review of Trello or Hack’n Plan. This was to verify that the work was actually done and check the code implemented, on occasions I had spoken with the programmer who had done the task to comment on optimizations, but due to little time between deliveries, we were not been able to improve and clean the code as much as we wanted.
Sometimes some programmers commented me possible optimizations, then I talked with the requesting programmer, the programmers who could do the task and perhaps some other programmers, and all together we studied the viability and the possible milliseconds decrease for each frame. If we found that the optimization could be done with the available time and had a significant impact, we proceeded to realize it.
We have also used GitFlow, so if a feature is not tested and approved is not passed to the develop branch, so we get another layer of error prevention to the develop branch, our semi stable branch.
To report issues, I made a GitHub issue template of with this sections: Status, Category, Description, Steps to reproduce, Priority, Screenshot/Gif and an annex with a link to a Google Drive doc with the explanation of the issue sections and the management and life cycle of issues:
Link to the Google Drive doc.The reported issues, were classified by the leads and the producer, then the leads assignee the issues to the people responsible of the area that could lead to the error or if that person was already occupied, we looked for someone with less work and assigned the issue to him.
Things we learned
During the course of this project, we have noticed several things that we did wrong or had not thought about it, and that must be borne in mind for future projects. Below there is a compilation of the most important observations:
- You have to go carefully with the art and design that you want to put in the game, a bad decision triggers a snow ball of problems that lead to fail a delivery or the workload is excessive. An example is the puzzle of barrels, it seemed easy, but delayed us too much, we had work overload and along with other errors, we did not reach the vertical slice 2 delivery well, if it had been an easier puzzle, programmers could program it faster, iterate and improve more and maybe had time to help other scrums or solve more issues. Thankfully, we learned from it, and the other puzzles followed that rule.
- The artificial intelligence of the NPCs may seem easy to implement, but during the project has always brought problems and whenever you tried to fix a bug, a multitude of other things broke, another problem is that AI depended on others subsystems as transformations and animation, therefore the problems started popping out everywhere. It has to be said that in this case, we did not have a way to debug the scripts, you could only put logs.
- Human like AI seem to be more difficult to make it feel more real than a standard animal enemy of dungeon crawler games. For example, an animal behaves in a simpler way, it does not speak, does not notify with gestures, etc and if it does some strange movements, it can be accepted since it is an animal. Maybe the typical enemy’s snail, turtle or spider, might have been easier to do more realistic, but this are only thoughts of the team while we programmed it, it could be false.
- You should not implement a subsystem or tool, if it is not of immediate use or 100% confirmed that it will be used. We have dedicated many hours to a system of perception of AI, which is implemented, it works, but you cannot tell if it is really working, was designed to be used on another level where there would be elements of stealth, but because of time, that was not done. Also, there is a tool to make the patrol paths of enemies from the Tile map editor inside the engine, but in the end they were already half done from code and did not use the tool. There are also the shadows, which have been a long and difficult implementation, but are only used in static shadows of the final boss room, this could be textures and have saved time and resources.
- The communication seemed like a fact that had to happen because we all know each other and if someone has a question, he goes to the indicated person to fix it, but it hasn't happened, from leads and producer, we had to force and to encourage communication to get the project forward. With this we realized that communication is crucial, you notice so much when there is lack of it.
- At the beginning of the project I did a survey to find out if we use a single master branch or use GitFlow for the repository, the result was a master branch, but after the first delivery, we saw the problems that we had and I came back to do another survey, this time GitFlow was the elected option. Because at the beginning we used a master branch and then we used GitFlow, we all understood the utility and learned to use GitFlow. The image below is full of involuntary merges we did between us with the single master branch.
- From the experience i had with this project, i find extremely important in projects like this to take one or two days every two weeks, stop the development and start playtestings, bug hunting and bug fixing to stabilize and improve the code and improve the overall game.
Programming tasks that I have done
During the project, I have managed to find the time in which I was able to program for a while, and I had managed to make the event system and the screenshot and gif system.
The base engine with what we started had no event system, and we saw that it would be very useful, so as it looked very interesting and I already had ideas of how to implement it, I picked up the task.
The event system is responsible to communicate the modules and process all of the object drawing, the most important part of the system compacts and sort the drawing instructions, this speed up the drawing phase, in fact, it accelerated more than we expected.
The system has had two iterations. Both have a struct to define each event and there is also an union that stores one instance of each event struct and a variable to store the type of event in order to know which of the structs read, for the management and creation of the events, I created the event system module that keeps four multimaps with events of opaque drawing, transparent drawing, glow drawing and not draw events.
The first implementation was a test of the event concept and functionality, which fell short of functionality very quickly, because the programmers were missing actions that I didn’t even think of, so I started another system of events from scratch and I asked the programmers to fill a list with desired functionality, as a result I ended up with the actual event system, that compacts the draw events and accepts the execution of events with a delay of time and frames, alongside with new functionality I fixed many bugs and I took advantage of the property of the unions to share memory with all of its members with bitwise operators, this is my first time investigating and using them, in this case was very useful.
When I finished the event system and i saw that the system was well integrated and stable, some team members asked me to implement an in-engine system to take screenshots and if possible to make animated gifs, so I searched information on how to do this. Finally, I have done two classes that implement full screen and screen portion screenshots and gifs.
For screen shots I used glReadPixels function of OpenGl and DevIL to save them. For the gif, I have used the archive of public domain gif-h. I have had to change a little bit the code to make it work fine with our engine. Although it works, it lowers the framerate a lot, I tried to speed it up, but I cannot do so, another problem is that the resulting gifs are huge so very expensive to upload or use in websites, I searched information, but I didn’t find what I wanted, and everybody was using third party tools for gif recording so alongside with this problems and the little time left, there is no point on improving this part of the system, so I stopped the development of this part of this system.
Most of the scrrenshots of this website were taken with this system.
Link to Gif-h.