Dec 182017
 

Hey guys!

This post is about how to avoid Unity’s coding pitfalls you will inevitably fall into if you follow the official tutorials.

Don’t take me wrong. Unity is an exceptional tool for creating games, and I love most of what it does for you! Especially now that I know what I know, after 4 years of using it in making Vaporum, I wouldn’t want to change my precious little engine for anything! But that’s exactly the issue — you have no way of knowing in advance that you’ll get slapped plenty of times going the “Unity” way of doing things.

The Unity Way

So, Unity teaches you to create game objects, slap your components on them, have your components listen for the “magic methods” like Awake, Start, Update, and the like, and the meadows are green and roses are red. Yeah, that surely works for the mini-projects their tutorials take place in. Issues start to pop out the moment your game grows in size and complexity though. Especially if you implement some kind of full-state save system with multiple ways of loading scenes (more on that later).

In classical game architectures, you write your main loop where you poll for input, update objects, components, systems, and render audiovisuals in the order of your choosing. In Unity, this is all set in stone and you have almost no control over the flow. It’s not clear and you can never be 100% sure of what gets called when and in what order. Unity gives you some degree of control via the script execution order tool, but even that cannot solve all use-cases. Not to mention that it’s weird from a coder’s standpoint.

Another issue is the performance. When your component is initialized for the first time, Unity checks if it contains a magic method, and if yes, it adds the component to an internal list of objects that need to be notified when certain engine events happen (Awake, Update, etc.). The problem is that these notifications are done via calls from C++ to C#, which is costly in itself, and the fact that Unity does a lot of safeguarding behind the scenes. In my opinion, that safeguarding should be your responsibility — you should make sure you are calling something that exists, that you don’t remove an object from an array you’re currently iterating over, that the object has ever been initialized, or whatever your specific game requires. You are making your own piece of software so you know what you’re doing. You should have full control.

And again, in small games with a handful of components, you will hardly ever notice any perf issues. In games with 120+ total components and a few hundred game objects in a scene, this can get noticeable.

Read this excellent official Unity blog post on the specifics! As you can see, they themselves recommend creating your own way of managing the game loop.

The Classical Way

What I’m going to recommend here may or may not fit your project, but this is what we learned the hard way when working on Vaporum. Since we realized problems piling up too late in the development, we couldn’t rework the whole game from ground up, but at least some changes towards the suggested system made it in.

There is no way you can properly implement your own main loop in Unity. You have to rely on the callbacks; they are the only entry points into anything you can do code-wise. So let’s work with that!

You should have a GameObject in every scene (let’s call it GameManager) with a single script on it, and you should make this object survive scene changes. This is one of the typical simple implementations (in Unity waters also known as singleton):

public class GameManager : MonoBehaviour
{
    public static GameManager Instance { get; private set; }

    public void Start()
    {
        // If this instance is the first one...
        if (GameManager.Instance == null)
        {
            // Set it as static instance.
            GameManager.Instance = this;

            // We need this GameObject to survive scene change.
            DontDestroyOnLoad(gameObject);

            // Initialize whatever you need here...
            ...
        }

        // If this is not the first instance ever...
        else
        {
            // Destroy this GameObject.
            Destroy(gameObject);
        }
    }

    public void Update()
    {
        // Update your stuff...
    }
}

The GameManager is THE ONLY class in your project that has the magic methods (with an exception, later on that), so it’s the only entry point from Unity into your game. This in itself is a HUGE boost already! Not only do you drastically cut the C++ to C# calls, you also gain full control over what gets called when. The Update method becomes your main loop from which you rule your kingdom with absolute authority!

Sure, this puts the burden on your shoulders to come up with a sound architecture for adding, removing, sorting, and updating your game objects & components. As none of your components have any of the magic methods, you have to spawn, initialize, update, and destroy them yourself.

To be clear here, you still use GameObjects with your custom components on them, but you just don’t use the magic methods on those components. That’s the crux.

There are countless ways to implement this, but I’ll describe our own here in a few words, just to give you an idea. I’m sure your game will need a specific implementation anyway.

  • When the game starts for the first time (GameManager.Start()), you scour the current scene for any GameObjects that have your own components on them, and add them to the system.
  • Have arrays and managing functions to add, remove, and update objects & components in these arrays. Use simple arrays, not lists or dictionaries. Arrays are fast!
  • Use integer ids in your systems and components to quickly access other objects & components in this global array(s).
  • Have your own functions to spawn and destroy objects that automatically properly handle addition / removal to and from the arrays.
  • Have a way of defining the order of when components or component groups should update.

Even if there is a unique special case where normally all ComponentAs update before all ComponentBs, but one specific ComponentB needs to update even before all ComponentAs, you can do it with this system. You can do whatever your game needs. That’s the point.

Now, one of the very few exceptions where you may need some magic methods in classes other than GameManager is collision handling. Unless you write your own physics, you will need OnTrigger... and OnCollision... methods on specific objects to handle this. Still, this can be wrapped into a single generic collision-detection component that can communicate with GameManager directly, which in turn can decide on when your game logic will get to handle the collisions (right when Unity detects them, or later on in the main loop, or ignore them in some cases — your call).

Note on Singletons

Some game systems (PrefabManager, SoundManager, SaveManager, LocalizationManager, …) are just a bunch of structures and functions. It doesn’t make much sense for them to live inside a Unity scene on a GameObject. Yet, you can find heaps of tutorials that teach you just that — create a bunch of singleton classes like our GameManager, put them on a few GameObjects, and everything is dandy!

Yeah, but why… I see zero benefit in this approach. My recommendation is to simply implement these as static classes. Handling the GameObject & Component thing is completely unnecessary in this case. Waste of your time.

Full-State Save System Hell

Here are a few issues you’ll likely run into when implementing a full-state save system without any kind of main-loop manager. By “full-state”, I mean creating a save point that is exactly reconstructed upon load, byte by byte, so you end up with the exact same game state as when you pressed the save button. For games with checkpoints, this gets much easier.

Components (classes derived from MonoBehaviour) cannot have constructors. The arcane Awake method is a sort-of replacement for that. So you mostly use that to initialize some other objects, states, and references. But there is a nice little caveat! If you load a scene with a disabled GameObject, the Awake methods on its components will never run! Not even when you enable the GameObject. Which leaves your components uninitialized for good. So for those you need to be disabled on start, you must create your own initialization method anyway. Sorry, this is not true. I mixed it up with something else. The issue we faced is that GOs that are disabled in design time will not get their Awake called until you enable them. But because the Awake is like a quasi-constructor, and you use it mostly to initialize references and other basic stuff, this leaves the object in a weird state. It exists, but its “constructor” has not been called. So it’s mostly unusable before you enable them, which you don’t always want, yada yada… Just something you need to be aware of.

The sneaky Start method has another beautiful perk. It does not adhere to the script execution order. We found out the hard way, when things were breaking every other day. Because of this, we had to stitch together a Start manager that would be able to call the method on objects in our desired order, and also be able to tell if the method has ever been called in the lifetime of that object (persisting through save files).

When you load a saved game in Vaporum and you are in the same level as where you saved the game, we do not reload the whole level (Unity scene). In this case, we just match those objects that are both present in the save data and in the scene, and load data into them instead of removing everything and re-instantiating it again. This is to make loads very quick, and damn, it does speed things up! But, there is a problem, of course. The object’s Start will not get called, obviously, and if there is something in there that need initialization, we got issues, Houston. So, again, your own layer of creation, removal, and initialization begs to exist here.

Objects that don’t exist in the scene, but do exist in the save data, or the other way around, have to be created or removed. But because you do the loading within an Update callback, the Starts don’t get called on those objects until the next frame. So we had to hack this too (also forcing them to be called in our desired order).

All this shabang exists because there are multiple concepts of “loading a scene”:

  • You’re entering a scene for the first time ever.
  • You’re entering a scene you’ve already entered before.
  • You’re entering a scene for the first time in this session, by loading a save file.
  • You’re entering a scene you’ve already entered before, but it’s the same scene as in the save file, by loading a save file.
  • You’re entering a scene you’ve already entered before, and it’s a different scene from that in the save file, by loading a save file.

Now, to be honest, some of the issues we ran into could have been mitigated by a smarter approach, or more research, or perhaps more testing, etc.

In any case, I’m not making another Unity game without a manager I have full control over!

Dec 152017
 

Hey guys!

As promised, we are rolling out a new patch on this important date in the dungeon crawl community — the 30th anniversary of Dungeon Master release. And it’s packed with new features!

TLDR summary:

  • New exoskeleton rig.
  • 3 new circuits & 9 modules.
  • Level cap increased from 16 to 17.
  • Enemy Showcase added.

New Exoskeleton Rig

We are introducing a new, 4th rig that you can choose in the first level. The Assault Rig is all about a risk-reward dynamic, increasing all damage you deal based on your missing integrity. In short, the closer you are to death, the more overall damage you deal (weapons, gadgets, shield reflects, etc.).

To take advantage of this mechanic, you will have to stay on the brink of death, only repairing your rig when the situation becomes critical. The constant danger of being one-shot to death is nerve-wrecking, but at the same time very rewarding, as your damage goes through the roof!

Because it increases your overall damage, the Assault Rig pairs well with both weapon builds and gadget builds. In any case, it’s all about offense, so you can go ham on them pesky creatures!

3 New Circuits & 9 Modules

The new rig is fun to play with on its own, but why not throw a few new skills into the mix, right? You can now invest your circuit points into three more circuits (each having 3 modules to unlock):

  • Servo Support: If you like weapons, this is for you. Enhances your precision and evasion with some strong bonuses on critical hits and evading attacks.
  • Complex Capacitor: Allows you to drastically increase either integrity or energy, with hefty bonuses and trade-offs.
  • Elemental Conductor: If you like DOTs, you will like this. Gives you a lot more ways to wreck enemies with elemental damage over time (even those who are immune to certain types!) while keeping yourself safe from such effects.

During playtesting, we found many interesting & fun builds that felt quite refreshing compared to the vanilla game. To open up more build options, we’re also increasing the level cap from 16 to 17 (it’s not easy to achieve though!). Can’t wait to see what builds you guys come up with.

Thank you all who contributed with their ideas for the new rig & circuits in this discussion!

Enemy Showcase

The third major addition is the Enemy Showcase. Whenever you defeat an enemy, an entry for it will unlock in the Showcase. This is not retroactive so you will have to defeat every enemy once again even if you’ve already beaten the game. Once unlocked, you can go see detailed information on their combat capabilities, hints, and lore via Main Menu -> Arx Pedia. You can also see some of the enemy animations in peaceful conditions.

Unfortunately, the Showcase texts are not localized. We wanted to provide solid chunks of lore and info, which resulted in lots of strings to translate, and in our current situation, it would be just too expensive. In any case, you guys can always take your part and localize the content yourselves (read this for more info).

Patch notes

  • Added a new, 4th rig. You’ll find it where the original three are.
  • Added 3 new circuits.
  • Increased max level cap: 16 -> 17.
  • Added an enemy showcase where you can read detailed info on enemies you beat.
  • Fixed: Total damage multiplier sometimes applies twice.
  • Fixed: Weapon damage shown in the inventory panel does not account for dual-wielding damage penalty.
  • Fixed: Difficulty change prompt sometimes shows up even if you aren’t dying much.
  • Fixed: Certain interactions with Puller Golem’s pull may crash the game.

Enjoy and happy holidays! πŸ™‚

Dec 122017
 

Hey guys!

We recently announced a giveaway of keys for Vaporum on Steam and GOG. We’ve received 157 answers, and what’s interesting is that the vast majority of you (89%) was right! You guys obviously know your stuff! πŸ™‚

The correct answer is 15th of December, Dungeon Master!

We added all of you with the correct answer to a pool and used a simple program to choose 10 from the pool, at random. We are currently contacting the winners to see what kind of key they wish to get.

Winners, enjoy this early Christmas present! The rest, thank you for taking part!

Dec 062017
 

Saint Nicholas Day Giveaway!

Do you want to get a free Steam or GOG key for Vaporum? Then find the right answer to our riddle!

We have a juicy update coming up — and you have to guess the date it’s going online! A little hint: it is the 30th release anniversary of a very important game in the dungeon crawl genre!

Email us the correct date here giveaway@fatbot-games.com until 9AM CET of the 8th of December and we will pick 10 of you by lot. These fortunate individuals will get a free Steam or GOG Key for Vaporum!

Thank you for your support, and enjoy!

Dec 042017
 

Hey guys!

We have a major update for you today! Yesterday (December 3rd) was the International Day of Persons with Disabilities, and we wish to address some of the issues people with disabilities may have while playing Vaporum and other games. One of the reasons why we took this stance is this article written by Olivia Falk at GameSpew. Olivia pointed out several in-game situations where Vaporum fails to fully allow people with various forms of disabilities a full gaming experience.

In the past patches, we were adding quality of life improvements and some changes to make the game more accessible to people with motion sickness and hearing deficiencies. Today, we’re introducing a Stop Time Mode.

You can turn this mode on and off at any time by pressing CapsLock (default key). When the mode is active, it automatically stops time whenever you are taking no action. This can be very useful in difficult combat situations where you need time to plan the best attack, or retreat for that matter. While you’re taking an action, time goes by as normal. But the moment you stop doing anything, time stops again, giving you the opportunity to plan the next move. Sometimes, the best course of action is to wait. You can hold Shift (default key) to advance time. You can make time tick while holding a weapon attack button too (LMB or RMB).

The Stop Time Mode can also be used in puzzles and hazards that require perfect timing, obstacles you may find too difficult to beat in real time. Using the Shift key, you can time your movement down to the millisecond. Please note that this is not meant as some sort of turn-based combat mode, but rather a more tactical approach to combat that is available in the game. We thought it would be interesting for people who like to play games at a slower pace, or people with physical difficulties, to give them plenty of time to reason about their moves.

We’re also adding an on-screen directional keypad for people who can only play with a mouse or only via touch. Or for those who love the old-school feel! πŸ™‚

As usual, here are patch notes:

  • Added a Stop Time Mode.
  • Added an on-screen directional keypad.
  • Added item comparison tooltips.

Enjoy!

Nov 212017
 

Hey guys!

Got a small patch with some fixes and tweaks you’ve been asking for. A bigger patch with some cool stuff is on the way, but we thought we’d put up this smaller update in the meantime.

Patch notes:

  • Added a direction damage indicator so you can see where the damage is coming from.
  • Increased the max FOV to 150. Note that values above 135 will likely cause visual glitches (this also depends on the screen ratio).
  • Fixed missing names for some keyboard keys. NOTE: Please make sure your controls work after the patch, as some of the hotkeys might get unassigned. You just need to reassign them once more.
  • Other small fixes.

Enjoy! πŸ™‚

Note: You can always find all the patch notes in your Vaporum folder in a text form. In case we fail to make an announcement like yesterday πŸ™‚

Nov 022017
 

Hey guys!

We know it took a lot more time than it should have, for which we are sorry, but it’s finally here! In today’s patch (Patch #2), we bring you 9 new languages: Chinese (simplified), Czech, French, German, Japanese, Polish, Russian, Slovak, and Spanish.

Apart from these official languages, we also bring you the ability to write your own localization! Go to Vaporum/Localization and read the ReadMe.txt for info on how to start. We appreciate any feedback on the localization so if you find a typo, incorrect wording, or anything, feel free to tell us, and we will fix it for the next patch. In the coming days, we will also enable Steam Workshop so you will be able to upload and download localization files easily.

Along with the languages, we also updated the game. Here are the patch notes:

  • Added 9 official languages: Slovak, Czech, Russian, Polish, German, Spanish, French, Japanese, and Chinese (simplified).
  • Added the ability to write community localizations. Head to the Localization folder and read the help file for more info.
  • Improved organic hit sounds to sound more meaty and impactful.
  • Added difficulty change prompt when you die a lot in a short time.
  • Tweaked: Emergency Braces module will now also activate when you’re below the threshold when it comes off cooldown. Previously, this only activated when taking damage.
  • Tweaked: Drainer gadgets now heal you for a portion of your max integrity, with a hard cap, instead of flat heal. Previously, you could abuse the system to get a lot more healing. Check this post for more info. Thanks motorbit!
  • Fixed: In some cases, the automap may remain disabled even after returning from the teleport chambers in the Arx Vaporum level.
  • Fixed: In rare cases, in the last room of Creepy Little Things, when you teleport back, the door remains closed and you cannot go back.
  • Fixed: The chest with coal on platform in Boiling Point sometimes glitches, falling down.
  • Lots of other small fixes.

Thank you!