【Unity】简单实现时间回溯

玩家移动脚本

RegisterObjectForTimeRecord方法在希望开始记录特定对象的时间状态时进行调用
UnregisterObjectForTimeRecord方法在不再需要记录特定对象的时间状态时被调用,没调用的话,上一次纪录的痕迹不会删除

using UnityEngine;

public class MOVE : MonoBehaviour
{
    public float moveSpeed = 5f;  // 移动速度
    private bool isMove=false;
    public TimeReturn timeReturn;//时间回溯脚本
    void Update()
    {
        float h = Input.GetAxis("Horizontal");  // 获取水平输入
        float v = Input.GetAxis("Vertical");  // 获取垂直输入
        if (Mathf.Abs(h) > 0 || Mathf.Abs(v) > 0)
        {
            isMove = true;
            Vector3 moveDirection = new Vector3(h, 0f, v).normalized;  // 构造移动方向向量并归一化
            transform.position += moveDirection * moveSpeed * Time.deltaTime;  // 更新物体的位置
        }
        else
        {
            isMove = false;
        }

        if (isMove)
        {
            timeReturn.RecordTime(); // 记录当前时间状态
        }
        
        if (Input.GetKeyDown(KeyCode.R))
		{
		    StartRewind();//然后逐帧回溯 所有对象的时间状态
		}
		if (Input.GetKeyDown(KeyCode.T))
		{
		    timeReturn.UnregisterObjectForTimeRecord(this.transform);//再注销
		}
		if (Input.GetKeyDown(KeyCode.E))
		{
		    timeReturn.RegisterObjectForTimeRecord(this.transform);//先注册
		}
		// 示例中使用了Input.GetKeyDown检测用户输入,实际应用中可以根据实际需求调用方法
    }
    private void StartRewind()
    {
        timeReturn.StartRewindTime(); 
    }
}

时间回溯脚本

TimeRecord被定义为一个结构体主要是出于以下考虑:

轻量级:结构体是一个值类型,它在内存中的存储方式更加简单,这使得它在一些情况下比类更加高效。由于TimeRecord可能会被大量创建和使用(比如记录每一帧的游戏对象状态),使用结构体可以减少内存开销。

不可变性:结构体是不可变的,这意味着一旦创建后,它的字段不能被修改。在记录时间状态时,这有助于保持记录的不变性,防止无意中对记录的状态进行修改。

语义上的合理性:TimeRecord作为一个记录时间状态的数据结构,其内容在逻辑上更类似于一个值而不是一个对象的实例。这种语义上的相符性使得使用结构体更加合适。

然而,需要注意的是结构体也有其适用的场景和限制。结构体适合简单的数据传输和封装,但对于复杂的对象以及需要动态修改的状态,类可能更为合适。

using UnityEngine;
using System.Collections.Generic;
using System.Linq;

// 时间记录的结构体
public struct TimeRecord
{
    public Vector3 position;
    public Quaternion rotation;
    // 其他需要记录的状态,比如速度、动作等
}

public class TimeReturn : MonoBehaviour
{
    private Dictionary<Transform, List<TimeRecord>> objectTimeRecords = new Dictionary<Transform, List<TimeRecord>>();  // 存储多个对象的时间状态
    private bool isRewinding = false; // 标识是否正在回溯
    private int currentRewindFrame = 0; // 当前回溯到的帧数
 
    /// <summary>
    /// 记录所有注册对象的时间状态
    /// </summary>
    public void RecordTime()
    {
        foreach (Transform objTransform in objectTimeRecords.Keys)
        {
            if (!objectTimeRecords.ContainsKey(objTransform))
            {
                objectTimeRecords[objTransform] = new List<TimeRecord>();
            }
            TimeRecord record = new TimeRecord();
            record.position = objTransform.position;
            record.rotation = objTransform.rotation;
            // 存储其他状态
            objectTimeRecords[objTransform].Add(record);
        }
    }
    /// <summary>
    /// 每帧更新回溯状态
    /// </summary>
    void Update()
    {
        if (isRewinding)
        {
            RewindTimeFrameByFrame(); // 倒流时间
        }
    }

    /// <summary>
    /// 逐帧回溯所有对象的时间状态
    /// </summary>
    public void StartRewindTime()
    {
        if (objectTimeRecords.Count > 0 && objectTimeRecords.Values.First().Count > 0)
        {
            currentRewindFrame = objectTimeRecords.Values.First().Count - 1; //objectTimeRecords.Values.First().Count 定位到第一个注册对象的时间记录列表,并获取它的长度。这样的语法利用了 LINQ 扩展方法,需要确保在使用之前引入 System.Linq 命名空间
            isRewinding = true;
            Time.timeScale = 0f; // 将时间缩放设为0,实现暂停
        }
    }

    /// <summary>
    /// 逐帧回溯
    /// </summary>
    public void RewindTimeFrameByFrame()
    {
        if (currentRewindFrame >= 0)
        {
            foreach (Transform objTransform in objectTimeRecords.Keys)
            {
                TimeRecord record = objectTimeRecords[objTransform][currentRewindFrame];
                objTransform.position = record.position;
                objTransform.rotation = record.rotation;
                // 根据当前帧还原其他状态
            }
            currentRewindFrame--;
        }
        else
        {
            StopRewindTime();
        }
    }

    /// <summary>
    /// 停止回溯
    /// </summary>
    public void StopRewindTime()
    {
        isRewinding = false;
        currentRewindFrame = 0;
        Time.timeScale = 1f; // 将时间缩放恢复为正常值
    }

    /// <summary>
    /// 注册需要记录时间状态的对象
    /// </summary>
    /// <param name="objTransform"></param>
    public void RegisterObjectForTimeRecord(Transform objTransform)
    {
        if (!objectTimeRecords.ContainsKey(objTransform))
        {
            objectTimeRecords[objTransform] = new List<TimeRecord>();
        }
    }

    /// <summary>
    /// 取消注册对象
    /// </summary>
    /// <param name="objTransform"></param>
    public void UnregisterObjectForTimeRecord(Transform objTransform)
    {
        if (objectTimeRecords.ContainsKey(objTransform))
        {
            objectTimeRecords.Remove(objTransform);
        }
    }
}

以上就是简单实现时间回溯全部代码,希望帮到你

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值