Development Blog
Unity
Lore
Unity

And what is custom here and was premade by our virtual team? This is good to show, because this way it's easy to see how all the parts entangle together. I took the free model from the Unity Asset Store, and since it already comes with the models (several of them, with several different clothes) and animations, and that can be applied to any model, it is just to integrate the logic, and we're good to go. Not even customization is needed, as I will abstract the fact that the scene looks like space and we're not supposed to breath there.

The "logic" is not really code. It's a cool system that Unity calls Mecanim, that Unreal already had before, even in UDK, under the name of Animtree. This is an image of it taken from the internet:

Instantiating Modules at Runtime

As in autorunners we have to instantiate the modules the player will run over in runtime, randomly, this will be my next step here. The editor scene before play doesn't have much, just a skybox in an empty scene:

When I hit play, this is the code that instantiates it:

Not only the modules are random, but I can control what are the chances of they changing once I instantiate a new module. This avoids a scattered visual effect, and provides some uniformity.

When I hit play, the once empty scene changes, now with all the forty modules instantiated in runtime:

Later I will add curves, but before that we need the code that, as the player runs forward, new modules are instantiated ahead, and deinstantiated behind.

Here we have two options: or make the character go forward, or make the whole scene go backwards, like a treadmill. I will go with the first option, because, in the scope here, it can be more easily integrated with other scenes or maps. Here is it at runtime:

Instantiated Prefabs and Baked Lighting

We have an early challenge if we want to use Global Illumination in runtime instantiated prefabs. The thing is: Unity doesn't do that. It makes sense from a common sense point of view: if, in GI, lights on each part of the scene affects the whole scene, there will be lighting seams, inconsistencies and contrasts all across the scene. And, because of that, Unity doesn't store baked lighting in prefabs. Even so, I think there should be an option to do that, if the developer wants it that way. Just a warning message and a click of Yes would do.

And, as I want GI on instantiated prefabs, it will have to be done manually, through the use of pooling, instead of prefab instantiation.

Pooling is when you activate and deactivate objects inside a scene, instead of creating and destroying. A drawback is that we are limited to a maximum number of simultaneous instances of an object in the scene, and an advantage is that it is faster than prefab instantiation at runtime. It is a lot used for bullets in rapid firing.

Here I won't use it because of performance, but just to not lose the beautiful lightmapping.

There were some other solutions on the internet, but it didn't work, so that will be the solution here. As a test, I baked the lighting in an instantiated prefab, disabled, moved and enabled it, and it didn't lose the baked lighting:

GI works only on static objects, but, as here I want things that look better but that still can be moved around the scene, I went with some unusual configuration: I marked lightmaps and reflection probes as static, and left the rest dynamic. So no optimization like integrating textures in batch processing, but still with all the more complex illumination:

The player itself is illuminated by a single direction light, using a culling mask that affects only him, to not saturate the already lighted scene:

Now there will be the next step: the pooling code to move, enable and disable prefab instances at runtime.

(4 hours later...)

Pooling

This is the pool at runtime:

From the API client side, there is just a new class called PrefabPool with Get() and PutBack() methods:

And the actual PrefabPool code listing:

It has a nested class PrefabType, like a prefab that can be instantiated, but with a limited number of instances in it:

Later I will show the final implementation of the pool, adapted for the particular case of an autorunner, with additional methods to facilitate it.

At this moment, in the lighting part I alternated modules with and without ceiling to minimize global illumination inconsistencies, so that effects like radiosity are almost the same in any module:

Running Along the Path

Autorunner are a type of on-rails game, so now I will make an implementation of these rails. Before I show the technique I ended with, there were two others that didn't bring good results.

The first one was that the actual Transform of the character, i.e., it's position and rotation, would follow a very simple path of straight lines between different modules, with a trigger collider at the beginning of each module to inform the beginning of it:

To give a better sense of smoothness, just the character model, or image, would rotate continuously, despite the real movement being just a straight line between modules. This implementation however was not smooth enough.

The second technique had smoother results, but was more complex: the ground was covered with several mesh colliders, and each mesh collider contained a normal that informed the player the path forward:

And here is the path covered with these mesh colliders as guides:

This is a top-down orthogonal screenshot of the new curve, using straight modules as a background image to better orient it in Blender. The curve dimensions could be related with the straight module dimensions of 6.5x5x7, but wouldn't be essential. Settled with an arc with external radius of 50 and internal radius of 40, divided in segments of 15 degrees each.

As the data and calculations are mostly embedded in the scene, the code is straightforward, with the collider pivot being the new target:

And the one I went with was the more mathematical one, and the perfectly smooth one, to calculate the angular speed given the four corners of the curve. The curve now has snapped Transforms on each corner that are passed as parameters to a script in the curve:

And the code that makes the calculation:

It could be written in less lines, but I preferred to make it more descriptive and less obfuscated.

The script that uses this data is the forward script in the player avatar:

An Autorunner Tree

Autorunners are a randomized genre, and this one have hubs with openings in four directions besides curves, so from time to time the runtime generated tree will subdivide itself into three branches, and each of these can subdivide themselves, continuing this recursively untill all available modules are in the scene.

This presents some challenges not present in classic data trees: for one, as all trees, the best way is to do it recursively, but, different from generic data trees, if we don't inherit from MonoBehaviour there's several functionalities that becomes inaccessible to us, like a Destroy() on game objects. Another particularity is that, different from other trees, spatially two branches cannot collide with each other, so a different type of "branch prediction" is necessary. Therefore before each new module is added we need to know what are the module types that are allowed and what are preferred at that point to avoid future collisions.

The natural way of using recursivity on game objects is using a prefab that references itself, and, in theory, this should work, but in practice the prefab start to modify itself at runtime, and as soon as the root branch has three branches, it clones itself and each new branch is instantiated as a new four-branch tree.

The solution here was to put a different game object as the root, which sole purpose is to have a reference to the original prefab, and all branch prefabs instantiates new prefabs with this indirect reference:

As we don't know in advance which branch the player will choose, they grow mostly at equivalent lengths, so some guide colliders must go ahead on each branch, and be destroyed as each new hub is randomly generated, to give room to three new branches with three new guide colliders.

There are different ways of improving collision prevention, but ultimately multiple branches are just visual cues to hint at what is ahead, and the player always runs forward in a single path. So, if we discard the unpredictabilities that arise from performance and frame rate issues, an infallible way of preventing branches to never collide is to halt new modules to be instantiated until enough deinstantiations occur. Here, the worst case scenario is where the player crosses the last hub that gives access to the single path ahead and that is halted at the obstructed crossroads with other branches. As soon as the player crosses that hub, the other branches are deinstantiated, and only a single path ahead remains, unobstructed. The only drawback is that, visually, a temporary vacuum of modules occur, but that are soon filled up. A mist and temporary lack of obstacles could be a way to overcome it.

Branch collision prevention using guides with branch deinstantions and halted intantiations until enough dequeues occur

The tree is built by one instance of the class PlayerPathBuilder and several instances of the class PlayerPathBranch that instantiates itself recursively.

The root PlayerPathBranch is created by PlayerPathBuilder:

And all modules available at the PrefabPool are created inside the root PlayerPathBranch game object or one of the instances created under it:

After that, whenever the player hits a dequeue collider, it's time to deinstantiate behind, alternately choose one of the branches ahead, and choose a new unobstructed module to instantiate ahead.
In the dequeue behind stage, if the hit was on a hub dequeue collider, it's time to make the chosen branch the new root branch and discard the other two branches and all other branches under them:

The following enqueue ahead stage is handled inside the PlayerPathBranch.Instantiate() called above. Besides getting a new module from the unhindered types and instantiating it at the end of the tree, it also does some clean up, as having a minimum of modules between each hub, and instantiating curves instead of straight corridors when this distantiates the branch from collisions. If a hub is randomly instantiated, then it's time to create three new branches under it and from now on alternate adding new modules between these branches, the same way it instantiated the hub because it was reached as the bottommost branch in the tree:

Here are the complete code listings:

PlayerPathBuilder.cs

PlayerPathBranch.cs

PrefabPool.cs

And some screenshots and a video of these in action:

Blender
GIMP
Unity

And these are the beginning and ending cutscenes of a game that uses this architecture: