Time Reversal Ability

Game Engine - Unity Engine, C#

Learning Outcome - 2 types of Time Walk ability

About the Project :

  • I implemented 2 different types of Time Reversal Ability in this project.
  • Below are some detailed example of both ability from above video.

Version 1 :

  • Storage of Time - Unlimited Seconds
  • Reversal Time Rate - Normal Speed
  • Control Over Ability - Can travel back Till toggle turned off or First position reached
  • Video and Code Snippet are below.
    private void Update()
    {
        // Adding time when the update starts
        currentDataTimer += Time.deltaTime;
        totalTimeBeforeAbility += Time.deltaTime;

        // Checking if the player is not currently time walking
        if (canCollectTimeWalkData)
        {
            if (currentDataTimer >= secondsBTWData)
            {
                // Getting data
                timeWalkData.Add(GetTimeWalkData());

                currentDataTimer = 0f;
            }
        }

        // Drawing gizmos of the data we stored earlier
        for (int i = 0; i < timeWalkData.Count - 1; i++)
        {
            Debug.DrawLine(timeWalkData[i].objectPosition, timeWalkData[i + 1].objectPosition);
        }

        if (Input.GetKeyDown(KeyCode.E))
        {
            // Stop collecting data when ability is performed
            canCollectTimeWalkData = false;

            StartCoroutine(Cast());                

            // Stopping the rotation of player and camera
            if (isPlayer)
                cameraController.Lock(true);
        }

        else if (Input.GetKeyUp(KeyCode.E))
        {
            StopCoroutine(Cast());
            timeWalkData.Clear();                
            totalTimeBeforeAbility = 0f;

            if (isPlayer)
                cameraController.Lock(false);

            canCollectTimeWalkData = true;
        }
    }

    // Getting data
    public TimeWalkData GetTimeWalkData()
    {
        //for player, to store camera info as well
        if (isPlayer)
        {
            return new TimeWalkData
            {
                objectPosition = transform.position,
                objectRotation = transform.rotation,
                cameraPosition = cameraController.transform.position,
                cameraRotation = cameraController.transform.rotation
            };
        }
        //for platform, only to store position and rotation of platform
        else
        {
            return new TimeWalkData
            {
                objectPosition = transform.position,
                objectRotation = transform.rotation,
            };
        }
    }

    // Overriding the cast method from Ability
    public override IEnumerator Cast()
    {
        // Stop collecting data when ability is performed
        //canCollectTimeWalkData = false;

        // Setting fields for calculation
        float secondsForEachData = totalTimeBeforeAbility / timeWalkData.Count;
        Vector3 currentDataStartPosition = transform.position;
        Quaternion currentDataStartRotation = transform.rotation;
        Vector3 currentDataCameraPosotion = Vector3.zero;
        Quaternion currentDataCameraRotation = Quaternion.identity;

        //if player, set camera position and rotation too
        //if platform, return
        if (isPlayer)
        {
            currentDataCameraPosotion = cameraController.transform.position;
            currentDataCameraRotation = cameraController.transform.rotation;
        }

        // Only perform time walk if there are any data in list
        while (timeWalkData.Count > 0)
        {
            // Set the ability time to zero
            float abilityTime = 0;

            // Performing the ability untill next list's data
            while (abilityTime < secondsForEachData && !canCollectTimeWalkData)
            {
                transform.position = Vector3.Lerp(currentDataStartPosition, timeWalkData[timeWalkData.Count - 1].objectPosition, abilityTime / secondsForEachData);

                transform.rotation = Quaternion.Slerp(currentDataStartRotation, timeWalkData[timeWalkData.Count - 1].objectRotation, abilityTime / secondsForEachData);

                if (isPlayer)
                {
                    //cameraController.transform.position = Vector3.Lerp(currentDataCameraPosotion, timeWalkData[timeWalkData.Count - 1].cameraPosition, abilityTime / secondsForEachData);
                    cameraController.transform.rotation = Quaternion.Slerp(currentDataCameraRotation, timeWalkData[timeWalkData.Count - 1].cameraRotation, abilityTime / secondsForEachData);
                }

                abilityTime += Time.deltaTime;

                yield return null;
            }

            // Adding next list's data to current data
            if (!canCollectTimeWalkData)
            {
                currentDataStartPosition = timeWalkData[timeWalkData.Count - 1].objectPosition;
                currentDataStartRotation = timeWalkData[timeWalkData.Count - 1].objectRotation;

                if (isPlayer)
                {
                    currentDataCameraPosotion = timeWalkData[timeWalkData.Count - 1].cameraPosition;
                    currentDataCameraRotation = timeWalkData[timeWalkData.Count - 1].cameraRotation;
                }
            }
            //positionCam = timeWalkData[0].cameraPosition;

            // Remove performed data
            if (!canCollectTimeWalkData)
                timeWalkData.RemoveAt(timeWalkData.Count - 1);
        }

        //if (isPlayer)
        //    cameraController.Lock(false);
        //
        //canCollectTimeWalkData = true;
    }
                        

Version 2 :

  • Storage of Time - Limited Seconds
  • Reversal Time Rate - Fast-Paced Reversal
  • Control Over Ability - Can't Stop the Reversal in between until First position reached.
  • Video and Code Snippet are below.
    private void Update()
    {
        // Adding time when the update starts
        currentDataTimer += Time.deltaTime;
    
        // Checking if the player is not currently time walking
        if (canCollectTimeWalkData)
        {
            if (currentDataTimer >= secondsBTWData)
            {
                if (timeWalkData.Count >= maxTimeWalkData)
                {
                    // Romove data from the start of the list
                    timeWalkData.RemoveAt(0);
                }
    
                // Getting data
                timeWalkData.Add(GetTimeWalkData());
                currentDataTimer = 0f;
            }
        }
    
        // Drawing gizmos of the data we stored earlier
        for (int i = 0; i < timeWalkData.Count - 1; i++)
        {
            Debug.DrawLine(timeWalkData[i].objectPosition, timeWalkData[i + 1].objectPosition);
        }
    
        if (Input.GetKeyDown(KeyCode.E))
        {
            StartCoroutine(Cast());
            // Stopping the rotation of player and camera
            if(isPlayer)
                cameraController.Lock(true);
        }
    }
    
    // Getting data
    public TimeWalkData GetTimeWalkData()
    {
        //for player, to store camera info as well
        if (isPlayer)
        {
            return new TimeWalkData
            {
                objectPosition = transform.position,
                objectRotation = transform.rotation,
                cameraPosition = cameraController.transform.position,
                cameraRotation = cameraController.transform.rotation
            };
        }
        //for platform, only to store position and rotation of platform
        else
        {
            return new TimeWalkData
            {
                objectPosition = transform.position,
                objectRotation = transform.rotation,
            };
        }            
    }
    
    // Overriding the cast method from Ability
    public override IEnumerator Cast()
    {
        // Stop collecting data when ability is performed
        canCollectTimeWalkData = false;
    
        // Setting fields for calculation
        float secondsForEachData = timeWalkDuration / timeWalkData.Count;
        Vector3 currentDataStartPosition = transform.position;
        Quaternion currentDataStartRotation = transform.rotation;
        Vector3 currentDataCameraPosotion = Vector3.zero;
        Quaternion currentDataCameraRotation = Quaternion.identity;
        
        //if player, set camera position and rotation too
        //if platform, return
        if (isPlayer)
        {
            currentDataCameraPosotion = cameraController.transform.position;
            currentDataCameraRotation = cameraController.transform.rotation;
        }
            
        // Only perform time walk if there are any data in list
        while (timeWalkData.Count > 0)
        {
            // Set the ability time to zero
            float abilityTime = 0;
            // Performing the ability untill next list's data
            while (abilityTime < secondsForEachData)
            {
                transform.position = Vector3.Lerp(currentDataStartPosition, timeWalkData[timeWalkData.Count - 1].objectPosition, abilityTime / secondsForEachData);
                transform.rotation = Quaternion.Slerp(currentDataStartRotation, timeWalkData[timeWalkData.Count - 1].objectRotation, abilityTime / secondsForEachData);
        
                if (isPlayer)
                {
                    //cameraController.transform.position = Vector3.Lerp(currentDataCameraPosotion, timeWalkData[timeWalkData.Count - 1].cameraPosition, abilityTime / secondsForEachData);
                    cameraController.transform.rotation = Quaternion.Slerp(currentDataCameraRotation, timeWalkData[timeWalkData.Count - 1].cameraRotation, abilityTime / secondsForEachData);
                }                    
                abilityTime += Time.deltaTime;
                yield return null;
            }
        
            // Adding next list's data to current data
            currentDataStartPosition = timeWalkData[timeWalkData.Count - 1].objectPosition;
            currentDataStartRotation = timeWalkData[timeWalkData.Count - 1].objectRotation;
        
            if (isPlayer)
            {
                currentDataCameraPosotion = timeWalkData[timeWalkData.Count - 1].cameraPosition;
                currentDataCameraRotation = timeWalkData[timeWalkData.Count - 1].cameraRotation;
            }
                
            //positionCam = timeWalkData[0].cameraPosition;
            // Remove performed data
            timeWalkData.RemoveAt(timeWalkData.Count - 1);                
        }
        
        if(isPlayer)
            cameraController.Lock(false);
        
        canCollectTimeWalkData = true;
    }