Time Manipulation in Unity – Recorded Solutions

We wanted a way for the player to not get stuck while playing and here is how we tackled that problem…

Articles:
Level Creation
Rewinding Time
Recorded Solutions
• Testing Automation (coming soon)

We think that simply letting the player skip levels would be a bad idea as the player won’t learn the techniques needed to solve the level. The same techniques will be needed in the future, which means the game will only become harder and harder for them. And so we searched for some other options, something that will hopefully also teach the player how to beat this type of levels.

Solution Visuals

At the moment we had already recorded some solutions using Gifcam to not forget the perfect solution ourself. And considering the types of ideas we wanted to teach in the beginning – showing the solution would have been good enough.

But why not go a step further and have a player follow along, doing it themselves – this way the chances of learning are higher, plus the perception is better, as they’ve completed it.

Also, this would be a way for curious players to see what is the perfect solution for a particular level. We know players can go online and try to search it, but why not allow this in game as it is more convenient.

As we already had a timeline – it was easy to add an option of recording solutions and showing them to the player, ensuring they understand how to tackle this kind of levels.

This also solved the problem of unexpectedly hard levels. Sure, we playtest the levels but some people will find some particular mechanic or idea more tricky than we’ve expected.

Limiting the use

Next, we had to decide when to let the player use our “solution” feature. Having level skip available to the player for free or even after some made up number of attempts can break the game and is susceptible to exploiting –– failing on purpose to skip the level.

Instead, we wanted a system that will require the player to earn that ability.

And so we introduced a resource you can earn by improving the results on the earlier levels or getting better results in the first place, plus some extra, for completing special levels. Getting better results will also help you unlock the next level pack, so it’s a time well spent.

And because the resource is limited, you will not spend it thoughtlessly. You’ve worked hard to earn it after all.

Level Dust Resource Reward

Implementation

To show the player the solution to the level – we first record it in the Editor and serialize it to JSON. We could have chosen other formats but that is not really a place were we needed optimization, plus we have already used Newtonsoft and it was easy to serialize/deserialize that way. (If you’re not yet using it – you really should try it).

To record the actions for each object – we first have to uniquely identify them. You can’t use GameObject.GetInstanceID() as it will reset after Editor is reopened.

But what you can do is add an ID to those scripts and then write a Unity editor script that will open all scenes, look for specific class objects, modify them (set the incremental id) and then save the scene.
Here is how you can go about doing this:

Imagine you had a bunch of TestObject objects on a bunch of scenes. You just add ID to the class.

TestObject.cs

1using UnityEngine;
2 
3public class TestObject : MonoBehaviour 
4{ 
5  public int ID;
6  public string olderData;
7}

And then create an IDSetter class similar to this one:

IDSetter.cs

 1using UnityEngine;
 2using System.Collections.Generic;
 3using UnityEditor.SceneManagement;
 4using UnityEditor;
 5
 6public class IDSetter: MonoBehaviour {
 7    
 8    [MenuItem("Tools/Set IDs")]
 9    public static void SetIDs()
10    {
11        // Go though each of scenes included in the build
12        foreach (EditorBuildSettingsScene scene in EditorBuildSettings.scenes)
13        {
14            
15            EditorApplication.OpenScene(scene.path);
16            
17            // Find all the objects you want to set the ID for
18            var objects = FindObjectsOfType<TestObject>();
19            for (int id = 0; id < objects.Length; ++id)
20            {
21                objects[id].ID = id;
22            }
23
24            // Save the modified scene
25            EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
26            EditorSceneManager.SaveScene(EditorSceneManager.GetActiveScene());
27        }
28
29    }
30}

This will add a button to the Unity top menu “Tools -> Set IDs”. When you press it – the SetIDs method will be executed and will set unique IDs to each object in each scene added to Build Settings.

Warning: We don’t recommend doing this without making some sort of a backup beforehand. We ourself host our project on github.com and could easily revert back if something went wrong.

Recording the solution is as simple as just toggling a checkbox in one of the scripts and then playing the level normally. For each line created – we remember the time since level startup and two object IDs, between which the line was created.

If you complete the level successfully and get a perfect result –– it will be saved and will be used for replays. After modifying the levels we can just re-record the solution and it will work once again. But what if the level was modified but the new solution wasn’t re-recorded you might ask – well, we will address this question in the next article.

When we then show the solution, we create a transparent wide line (we’ve been using Vectrosity), that shows how you should link the crystals at this moment of time. To help the player get ready for creating a line – the line blinks and changes its color at the moment it should be built.

Solution Example using Vectrosity

Sidenote: We have analytics in the game looking at how many tries it takes to complete each level so that we know their real difficulty and can alter the game appropriately if needed. But when sending data to the analytics we need to separate level completions that were made using solution from the normal ones. It may sound obvious now, but this is one of those things you might forget before release.

Having a separate ratio of completions to losses for the “with solution” attempts also allows us to see if some recorded solutions are hard to follow and we will need to remake them or alter the levels somehow.

Final Thoughts

Having a way for players to not get stuck on any particular level is huge. Not already having a unique ID for each object (when dozens of levels already were created) is not a problem. And with the help of tools like Newtonsoft JSON we could quickly save and load solutions for the levels.

There are a lot more game design applications for a thing like this, we’ve just shown the one we’ve used for our game.

If you liked this article and would like to see more like this one, or just catch the next article in this series – you can subscribe to Let’s Make a Game.

Sharing is caring!


You may also like:

Related Content: Time Manipulation in Unity – Rewinding Time

Time Manipulation in Unity – Rewinding Time

Related Content: Time Manipulation in Unity – Level Creation

Time Manipulation in Unity – Level Creation

Related Content: Iterative Design – Why Playtesting Matters

Iterative Design – Why Playtesting Matters

Related Content: Making a Game Jam Game During Self-Isolation

Making a Game Jam Game During Self-Isolation