In order to test this system, we need a simple game where we can test it. Let's create one!
The Player
Create a cube in your scene, this will be our player-character. Then create a new C#-script calls Player.cs and adapt the Update()-function to look like this:
- void Update()
- {
- transform.Translate (Vector3.forward * 3.0f * Time.deltaTime * Input.GetAxis ("Vertical"));
- transform.Rotate (Vector3.up * 200.0f * Time.deltaTime * Input.GetAxis ("Horizontal"));
- }
Then angle the camera so that it views the cube from above, with room on its side where we can move it. Lastly, create a plane to act as floor and assign some different materials to each object, so that we're not moving it inside of a void. It should look like this:
![]() |
The TimeController
Now create a new C#-script called TimeController.cs and add it to a new empty GameObject. This will handle the actual recording and subsequent rewinding of the game.
In order to make this work, we will record the movement of the player character. When we then press the rewind button we will adapt the player coordinates. To do so start by creating a variable to hold the player, like this:
- public GameObject player;
![]() |
- public ArrayList playerPositions;
- void Start()
- {
- playerPositions = new ArrayList();
- }
First, let's save the data:
- void FixedUpdate()
- {
- playerPositions.Add (player.transform.position);
- }
This code will store the player-position of each frame in the array. Now we need to apply it!
We'll add a check to see if the rewind button was pressed. For this, we need a boolean variable:
- public bool isReversing = false;
- void Update()
- {
- if(Input.GetKey(KeyCode.Space))
- {
- isReversing = true;
- }
- else
- {
- isReversing = false;
- }
- }
- void FixedUpdate()
- {
- if(!isReversing)
- {
- playerPositions.Add (player.transform.position);
- }
- else
- {
- player.transform.position = (Vector3) playerPositions[playerPositions.Count - 1];
- playerPositions.RemoveAt(playerPositions.Count - 1);
- }
- }
- using UnityEngine;
- using System.Collections;
- public class TimeController: MonoBehaviour
- {
- public GameObject player;
- public ArrayList playerPositions;
- public bool isReversing = false;
- void Start()
- {
- playerPositions = new ArrayList();
- }
- void Update()
- {
- if(Input.GetKey(KeyCode.Space))
- {
- isReversing = true;
- }
- else
- {
- isReversing = false;
- }
- }
- void FixedUpdate()
- {
- if(!isReversing)
- {
- playerPositions.Add (player.transform.position);
- }
- else
- {
- player.transform.position = (Vector3) playerPositions[playerPositions.Count - 1];
- playerPositions.RemoveAt(playerPositions.Count - 1);
- }
- }
- }
- using UnityEngine;
- using System.Collections;
- public class Player: MonoBehaviour
- {
- private TimeController timeController;
- void Start()
- {
- timeController = FindObjectOfType(typeof(TimeController)) as TimeController;
- }
- void Update()
- {
- if(!timeController.isReversing)
- {
- transform.Translate (Vector3.forward * 3.0f * Time.deltaTime * Input.GetAxis ("Vertical"));
- transform.Rotate (Vector3.up * 200.0f * Time.deltaTime * Input.GetAxis ("Horizontal"));
- }
- }
- }
Now you should be able to move around the world, and rewind your movement by pressing space. If you download the build package attached to this article and open TimeRewindingFunctionality01 you can try it out!
But wait, why does our simple player-cube keep looking in the last direction we left them in? Because we didn't get around to also record its rotation!
For that you need another array to keep its rotation-values, to instantiate it at the beginning, and to save and apply the data the same way we handled position-data
- using UnityEngine;
- using System.Collections;
- public class TimeController: MonoBehaviour
- {
- public GameObject player;
- public ArrayList playerPositions;
- public ArrayList playerRotations;
- public bool isReversing = false;
- void Start()
- {
- playerPositions = new ArrayList();
- playerRotations = new ArrayList();
- }
- void Update()
- {
- if(Input.GetKey(KeyCode.Space))
- {
- isReversing = true;
- }
- else
- {
- isReversing = false;
- }
- }
- void FixedUpdate()
- {
- if(!isReversing)
- {
- playerPositions.Add (player.transform.position);
- playerRotations.Add (player.transform.localEulerAngles);
- }
- else
- {
- player.transform.position = (Vector3) playerPositions[playerPositions.Count - 1];
- playerPositions.RemoveAt(playerPositions.Count - 1);
- player.transform.localEulerAngles = (Vector3) playerRotations[playerRotations.Count - 1];
- playerRotations.RemoveAt(playerRotations.Count - 1);
- }
- }
- }
Conclusion
We have built a simple prototype game with an already usable time-rewinding system, but it is far from done yet. In the next part of this series we'll make it much more stable and versatile, and add some neat effects.
Here is what we still need to do:
Only record every ~12th frame and interpolate between the recorded ones to save on the huge data load
Only record the last ~75 player positions and rotations to make sure the array doesn't become too unwieldy and the game doesn't crash
We'll also take a look at how to extend this system past just the player-character:
Record more than just the player
Add an effect to signify rewinding is happening (like VHS-blurring)
Use a custom class to hold player position and rotation instead of arrays
Written by Matthias Zarzecki
If you found this post interesting, follow and support us.
Suggest for you:
Unity 5 Professional Guide - Develop a 2D Arkanoid Game!
Unity 5 Professional Guide - Mastering C# Programming!
Make VR Games in Unity with C# - Cardboard, Gear VR, Oculus
Learn to Code by Making Games - The Complete Unity Developer
Start Learning Unity3d by Making 5 Games from Scratch


No comments:
Post a Comment