More Lessons after Developing Academia: School Simulator

It’s been a couple of weeks since the release of Academia: School Simulator. A game that has been in development for 5 years – 4 of which was in Steam’s Early Access – and, as Marnel listed in his blog, there are a lot of lessons from those years of development.

The context of this blog is from my experience of having had part-time, freelance jobs in the game dev industry before joining Squeaky Wheel, my first permanent and regular job in the industry – and on programming a strategy game that has multiple systems running simultaneously and with hundreds of agents.

Below are shortcuts if you want to jump to a specific topic and as usual there are references at the end of the blog for further reading:

No-Garbage Programming

Well, not always but as much as possible. This is probably what I always oversee during my first years in the team (I still do every now and then) and you’ve probably seen these codes before. Garbage-generating code can look something like this:

public class GarbageExample : MonoBehaviour {

    public List<int> intList;

    public void Update(){
        if (Input.GetMouseButtonDown(0)){
            // Oops, garbage here every time the button is clicked
            this.intList = new List<int>();
        }
    }
}

Or a more common example with delegates:

public class DelegateExample : MonoBehaviour {

     // Applies different kind of damages to an entity
     private delegate void DamageEffect (int damage);

     public void Update() {
          // This generates garbage every frame
          ApplyDamage(PoisonDamage);
     }

     private void ApplyDamage (DamageEffect damageEffect){
          // Apply the damage here
     }

     private void PoisonDamage (int damageOverTime) {
          // Apply poison damage over time
     }
}

These blocks of code look harmless at first but; in the first one, a new object of type List is always being created every frame while the previous List is being released to be collected later by the Garbage Collector (GC); in the second example, what happens under the hood is a new delegate PoisonDamage is being created every frame and being passed to ApplyDamage(). Likewise with the first example, the previous delegate instance is being released to be collected later by the GC.

This is an issue because garbage collection is being done in the main thread and may look to the player as if the game was lagging if there are a lot of garbage to be collected in the current frame.

One of the ways of dealing with this is enabling Unity’s Incremental Garbage Collection. Another way is to improve your code. Here are a couple of guidelines that we follow to prevent garbage-generating codes:

  • Avoid these every frame or inside your Update method: GetComponent(), Find(), AddComponent(), Camera.main, transform, Debug.Log, and other built-in Unity methods
    • Reason being is that they are expensive and involves boxing. Solution here is to cache the return values of these methods
    • In the case of Debug.Log, these are still being called in the production build which may not be your intention. In our case, we remove these altogether after unit testing, or enclose these inside “#if UNITY_EDITOR” blocks
  • Pool your instances/objects and don’t instantiate/destroy objects every single frame. Cache the instances that you know you will reference multiple times in its lifetime
  • Move repeated computations that returns the same value outside of loops

These are just some of the examples and our ways of dealing with garbage-generating code and I’m sure there are a couple more. If you’re interested, Academia uses a signaling system to decouple different mechanics, and here’s one that Marnel made that doesn’t generate garbage.

Comment, Comment, Comment

This one I’ve realized midway into development when I was given bigger tasks that involve making systems from scratch. When I had to turn my attention to more urgent tasks such as bugs for hotfixes, then return to the system, I already forgot how half of the system works. Another benefit of adding comprehensive comments and summaries to your code, is this allows other programmers to understand your code without having to DM you about how a certain piece of code works. Sounds simple and will be beneficial in the long run.

Write your code as if it’s for the Asset Store

This is one of the first things that Marnel told me during our on-boarding period. This is related to the previous point of adding comments to your code. But not only that, your systems should work without a lot of dependencies with other systems. This is where design patterns come in. More specifically, the Component pattern or other decoupling patterns.

Writing your code in a way that they can be attached as components for Unity objects (or ECS entities) is a great way to decouple your codes and prevent spaghetti code and the infamous Gordian knot. This way of writing also allows programmers to work independently without worrying about breaking other programmer’s work.

Another way to separate systems from one another is creating a querying system and a signaling system for your game.

Read the code of existing systems

This one is crucial especially if you join the team in the middle of development – spend some time reading the backend codes of existing systems. I do this when I have extra time in-between tasks and when I’m curious on how a mechanic was implemented. This way, you will learn a lot about how your application works and thus, how best to implement and integrate new systems with existing ones.

In terms of communication, this also saves time when a question needs to be answered and your tech lead is not available.

Read and study new technologies

This might be daunting at first especially if you’ve been making the game for quite a while but, you don’t need to learn each and every new technology out there (you can if you want, though). You can skim over some of them, and study only those you are sure will help you improve your game. In our case, Unity’s new Data-Oriented Technology Stack or DOTS is definitely a need to speed up Academia’s simulations (Needs and Satisfaction, rendering, A* pathfinding, etc.).

Ask Questions – lots and lots of questions

So, you’ve been reading the backend of existing systems and reading about new technologies. That is all good and well. But, it’s sometimes better to start a discussion with your colleagues. Not only fellow programmers if you’re one, but, also with your designers and artists. This will open your mind to different disciplines in the industry and help you understand them a lot more.

By asking questions, I learned a lot from our designers which help me in designing code and tools that will help them create better content for Academia. Learning from our designers also helped me understand and visualize the mechanics in the game.

Be Clear and Specific in Communicating

Our CEO and artist, Ryan Sumo, wrote an article that pretty much sums up our daily communications. Basically, we avoid a lot of ambiguous and/or confusing definitions and terms that doesn’t translate well across different disciplines. When trying to explain programming pitfalls when we’re discussing new mechanics, I try not to use jargons that only programmers understand and explain implications in terms of actual in-game scenarios and examples. That’s just one way of making our conversations more streamlined, and there are more examples and tips in Ryan’s article.

Ask to be criticized every once in a while

This one is probably personal preference but, in order to further grow as a programmer and as a person in general, I ask my colleagues every once in a while how I’m doing in the company. Tangental to this, I take notes on common code review comments from our tech lead, usual terms and things that confuse our designers, and life advices I receive and observe from my colleagues.

This is not an exhaustive list of the lessons I learned from being part of Squeaky Wheel, developing Academia: School Simulator. But, I hope you picked up a thing or two from this list that may help you in your game dev journey.

That said, thank you and feel free to share this with your friends. Starting next week, I’ll start writing more programming-related blogs – See you in the next one!


References: