Skip to content

Instantly share code, notes, and snippets.

@Marsgames
Last active November 5, 2020 21:40
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Marsgames/e6371b9e1e53d24e6eac2ed7e9b4c0bb to your computer and use it in GitHub Desktop.
Save Marsgames/e6371b9e1e53d24e6eac2ed7e9b4c0bb to your computer and use it in GitHub Desktop.
A simple system to rewind time like in Prince of Persia
// Source : https://www.twitch.tv/videos/519292916?filter=all&sort=time
#region Author
/////////////////////////////////////////
// RAPHAËL DAUMAS --> ICanRewind
// https://raphdaumas.wixsite.com/portfolio
// https://github.com/Marsgames
/////////////////////////////////////////
#endregion
public interface ICanRewind
{
#region Unity's functions
#endregion Unity's functions
///////////////////////////////////////////////////////////
#region Functions
void Rewind();
void Record();
#endregion Functions
///////////////////////////////////////////////////////////
#region Accessors
#endregion Accessors
}
#region Author
/////////////////////////////////////////
// RAPHAËL DAUMAS --> Player
// https://raphdaumas.wixsite.com/portfolio
// https://github.com/Marsgames
/////////////////////////////////////////
#endregion
using UnityEngine;
using System.Collections.Generic;
[RequireComponent(typeof(Rigidbody2D))]
public class Player : MonoBehaviour, ICanRewind
{
#region Variables
[SerializeField] private float jumpVelocity = 5;
[SerializeField] private float fallOffMultiplier = 2.5f;
[SerializeField] private float lowJumpMultiplier = 2;
private Rigidbody2D rigidbody;
private Stack<Instant> instants = new Stack<Instant>();
#endregion Variables
///////////////////////////////////////////////////////////
#region Unity's functions
private void Start()
{
TimeManager.Instance.SetRewindable(this);
rigidbody = GetComponent<Rigidbody2D>();
}
private void Update()
{
Move();
}
#endregion Unity's functions
///////////////////////////////////////////////////////////
#region Functions
private void Move()
{
if (Input.GetKey(KeyCode.Q))
{
transform.position += Vector3.left * .1f;
}
if (Input.GetKey(KeyCode.D))
{
transform.position += Vector3.right * .1f;
}
Jump(KeyCode.Space);
}
/// <summary>
/// Function to jump like in Mario (long press jumps higher than short press)
/// </summary>
/// <param name="jumpButton">Jump button.</param>
private void Jump(KeyCode jumpButton)
{
if (Input.GetKeyDown(jumpButton))
{
rigidbody.velocity = Vector2.up * jumpVelocity;
}
if (rigidbody.velocity.y < 0)
{
rigidbody.velocity += Vector2.up * (Physics2D.gravity.y * rigidbody.gravityScale) * (fallOffMultiplier - 1) * Time.deltaTime;
}
else if (rigidbody.velocity.y > 0 && !Input.GetKey(jumpButton))
{
rigidbody.velocity += Vector2.up * (Physics2D.gravity.y * rigidbody.gravityScale) * (lowJumpMultiplier - 1) * Time.deltaTime;
}
}
/// <summary>
/// Set the value back to rewind time
/// </summary>
public void Rewind()
{
Instant t;
if (instants.Count > 1)
{
// Pop remove element from stack when we use it
t = instants.Pop();
}
else
{
// Peek only get element
t = instants.Peek();
}
transform.position = t.GetPosition();
rigidbody.velocity = t.GetVelocity();
}
/// <summary>
/// Save everything we want to save for the player
/// </summary>
public void Record()
{
Instant t = new Instant(transform.position, rigidbody.velocity);
instants.Push(t);
}
#endregion Functions
///////////////////////////////////////////////////////////
#region Accessors
#endregion Accessors
#region Struct
/// <summary>
/// Structure that will contains all informations we wants to record for this class
/// </summary>
struct Instant
{
private Vector2 instantPosition;
private Vector2 instantVelocity;
public Instant(Vector2 position, Vector2 velocity)
{
instantPosition = position;
instantVelocity = velocity;
}
public Vector2 GetPosition()
{
return instantPosition;
}
public Vector2 GetVelocity()
{
return instantVelocity;
}
}
#endregion Struct
}
// Source : https://www.twitch.tv/videos/519292916?filter=all&sort=time
#region Author
/////////////////////////////////////////
// RAPHAËL DAUMAS --> TimeManager
// https://raphdaumas.wixsite.com/portfolio
// https://github.com/Marsgames
/////////////////////////////////////////
#endregion
using System.Collections.Generic;
using UnityEngine;
public class TimeManager : MonoBehaviour
{
#region Variables
[HideInInspector] public static TimeManager Instance { get; private set; }
private bool isRewinding;
private List<ICanRewind> rewindables = new List<ICanRewind>();
#endregion Variables
///////////////////////////////////////////////////////////
#region Unity's functions
private void Awake()
{
if (!Instance)
{
Instance = this;
}
else if (this != Instance)
{
Destroy(gameObject);
}
DontDestroyOnLoad(this);
}
private void Update()
{
RewindButton(KeyCode.S);
}
/// <summary>
/// Record/Rewind time when isRewinding is true
/// Maybe it should be dirrectly into RewindButton function ?
/// </summary>
private void FixedUpdate()
{
foreach (ICanRewind obj in rewindables)
{
if (isRewinding)
{
obj.Rewind();
}
else
{
obj.Record();
}
}
}
#endregion Unity's functions
///////////////////////////////////////////////////////////
#region Functions
/// <summary>
/// Set isRewinding when rewindButton is pressed
/// </summary>
/// <param name="rewindButton">Rewind button.</param>
private void RewindButton(KeyCode rewindButton)
{
if (Input.GetKeyDown(rewindButton))
{
isRewinding = true;
}
else if (Input.GetKeyUp(rewindButton))
{
isRewinding = false;
}
}
#endregion Functions
///////////////////////////////////////////////////////////
#region Accessors
/// <summary>
/// Used for objects implementing ICanRewind to call there Record/Rewind functions
/// </summary>
/// <param name="obj">Object.</param>
public void SetRewindable(ICanRewind obj)
{
rewindables.Add(obj);
}
#endregion Accessors
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment