Post Mortem - Unlit (C++/OpenGL)

Written on July 24, 2016

This year, for the first time I took part in a group project with 3 other programmers for 4 months. We have made a 2.5D platformer called Unlit where the player explores the mysterious cave Shushanna has fallen into with the help of the light being Nyx.

The project is ground up C++/OpenGL and all the art assets are created by either group members (models, environment) or other DigiPen students (audio) as we were not allowed to use free open source assets. There were 5 milestones set for the project by DigiPen, which were Engine, Prototype, Alpha, Beta and Release.

  • Title: Unlit
  • Developer: Teamstake of DigiPen
  • Programmer Count: 4
  • Duration: 4 Months
  • LOC: ~21k (Shaders excluded)

What Went Right

1 - Visuals

As this project was our first semester-long game project and first game project that would be built from scratch without using a commercial game engine, it was advised for students to go for a 2D game. We have chosen to build a 3D game with 2D gameplay. We knew it was going to be challenging. Only 2 of us had very limited experience with graphics before from the graphics course we have taken previous semester, which introduced the basics of graphics as well as some GLSL and OpenGL. With the help of two wonderfully prepared resources, Jaime King’s 3D Computer Graphics youtube list and learnopengl.com we were able to implement various effects to enhance our gameplay feel. I would definitely recommend both of the resources if you are new to graphics programming and want to learn OpenGL.

In-game screenshot

2 - Team Composition

4 of us were from 3 different countries, 2 different majors (3 CE & 1 CS), had various level of professional experience (2 had none) and had a variety of secondary skills. Despite the differences, we were able to communicate and contribute to the project in a productive manner. As 2 of us used modeling programs before, we have modeled the assets used in the game without help from an artist. One of us made the music for trailer. The professional experience of the team also helped in certain software design decisions. In the end, we had more fun working together than we had frustration.

Shushanna

Main character model close-up

What Went Wrong

1 - Lack of Communication

For various reasons - be it some of us having a full-time job or personal preferences - we decided to hold meetings 3 times per week and work from home almost all the time – well, until we realized how inefficient it was towards the end of the project. Working from home had some advantages and many disadvantages.

On the upside, having my home setup with multiple monitors and a mechanical keyboard rather than relying on DigiPen’s lab machines provided a really comfortable work environment. Working without any interruptions is probably preferrable by most of the programmers, –at least it is for me– and working at home provided that interrupt-free environment.

On the downside, however, fixing a bug would take way longer than it should take due to the communication overhead. This was quite crucial, as we could use the team spaces at the lab and work together to communicate in a more efficient manner and use the extra time we would get for polishing the game, introducing more features or fixing more bugs as the teams that worked physically together did. Due to our preference of working from home, we were losing time sharing screens on Skype when communicating about a bug or providing feedback about a newly-integrated feature. And Skype wasn’t particularly helping showing real-time footage over a network stream. This way we could inform each other about a bug, however, fixing an easy bug took a long time which, in turn, slowed the development and introduceed stress towards the milestone deadlines.

Debugging during beta phase

There were a few weeks when it got really busy with the midterms and some other homeworks for the full-time DigiPen students of the team and as a result we chose to drop our already low number of meetings per week. This essentially caused us to lose our shared vision as a team. At one point, one of us was working on a physically-based renderer, one of us on a 3D physics system, one of us on an editing tool and the other working on a messaging/event system. At that point, none of us was working on our game. The game project was basically turned into 4 different projects. After spending ~2 weeks like this, we realized we were about to show an alpha phase of our game in our beta presentation. We immediately increased the number of weekly meetings to establish the shared vision again and mitigate further damage to the project. This resulted in higher motivation for all of the team members for building Unlit making it fun after weeks of isolation and despair.

2 - Scoping & Game Design

We started the project as 4 programmers. We had very little idea how much engineering work it would take to put together a 3D game engine, thus we thought we wouldn’t need a game designer and rather try to learn designing while developing the game. The first two milestones, engine and alpha, went quite well. We made the progress we planned. When it came to iterate on our ideas and push the game into the beta phase, we weren’t making much progress due to spending most of our time implementing the missing engine features and fixing issues. Worst of all was, our ideas did not turn out to be fun at all from a game design point of view, or not as fun as we thought they would be since we couldn’t spend enough time on game designing. At DigiPen, a team can “hire” game designers, artists and sound designers from other majors from the entire university student base. After a rather poor beta milestone presentation, we immediately shot some mails to hire a designer and were lucky enough to meet our awesome game designer who helped a lot to turn our boring level design and gameplay into a rather fun one.

Another issues of ours was the scope of the project. We had this idea of a 2-phase game, where the player would go thorugh the platforming challenges. At the end of each level, the game would transition into a puzzle solving phase. And at the end of puzzle solving, the game would go back to platforming phase again, thus alternating between a fast- and slow-paced game style. We had no idea how much it would take to fine tune platforms, to make player controller feel responsive, smooth and fine and make one game mechanic feel right. At the end, we could barely put together a reasonable number of levels for only the platforming challenges. We basically had no time left to implement the puzzling phase. I’m pretty sure all of the team members will have more grounded design ideas in our next projects and go for an iterative approach.

3 - Handles & Premature Optimization

“Premature optimization is root of all evil.” -Donald Knuth

We were really excited about the project in the beginning, we wanted it to be something good. With all the good will, we started our research, we wanted to see how seasoned developers build their games in C++. We came across this fascinating idea of handles: they were providing cache coherency by using large arrays for components and game objects.

From previous semester, we already had a functioning component-based game object management system which used the naive approach with pointers. We spent almost two weeks to implement and integrate handles, replacing the pointers. Everything was set, we had optimized our update loop! So we thought. In the following weeks, when we started to put more objects into the scene and we realized the engine was performing rather poorly. When we ran the profiler for the first time (yes, we didn’t even profile before optimizing) we saw that the handles were the culprit. We probably made a huge mistake implementing handles, but this is irrelevant to this case. In the end, we had to throw them out, as well as the precious 2 weeks of worktime, hence we were back to pointers again.

Once I fired up the profiler, it was crystal clear where the bottleneck was. I'll never optimize before profiling again.

I want to clarify here. I understand cache coherency is a huge deal for real-time application optimization and should be considered where necessary. The biggest mistake in this scenario was trying to optimize something we did not even measure! I understood this very well when I started profiling regularly towards the end of the project as we hit some performance issues as the game developed. Our scenes were small enough, we did not even need the handle optimization to begin with.

The lesson was learnt: Donald Knuth is right.

Conclusion

Building a game from scratch, even though it was a small scale project, was an invaluable experience for us. It helped us to see the value of the commercial engines offer and better understad how things work under the hood a bit better when it comes to game engines. Our takeaways from this experience are the importance of team communication, value of prototyping as soon as possible and various programming practices when it comes to real-time applciations.