Unity 回放功能

记录一个回放工具,留在今后备用!

Recorder.cs

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

public class RecordStateList
{
    public Dictionary<float,Vector3> pos = new Dictionary<float, Vector3>();
    public Dictionary<float, Quaternion> rot = new Dictionary<float, Quaternion>();
    public Dictionary<float, Vector3> scale = new Dictionary<float, Vector3>();
    public bool isDispose = false;
    private GameObject go;
    private float startRecordTime;
    private float startPlayTime;
    private float playSpd = 1;
    private bool stoped = false;
    private Action endCb = null;
    private Vector3 lastPos;
    private Quaternion lastRot;
    private Vector3 lastScale;

    public RecordStateList(GameObject g)
    {
        go = g;
    }

    public void Start()
    {

        if (go != null)
        {
            lastPos = go.transform.position;
            lastRot = go.transform.rotation;
            lastScale = go.transform.localScale;
        }
        startRecordTime = Time.time;
        Record();
        SupportBehavior.inst.m_onFixedUpdate.Add(Record);
    }

    public void Record()
    {
        if (go == null)
        {
            Stop();
            isDispose = true;
        }
        else
        {
            pos.Add(Time.time - startRecordTime, go.transform.position);
            rot.Add(Time.time - startRecordTime, go.transform.rotation);
            scale.Add(Time.time - startRecordTime, go.transform.localScale);
        }
    }

    public void Stop()
    {
        stoped = true;
        SupportBehavior.inst.m_onFixedUpdate.Remove(Record);
    }

    public void Play(Action playEndCb,float spd = 1f)
    {
        if (!stoped)
        {
            Stop();
        }
        endCb = playEndCb;
        playSpd = spd;
        startPlayTime = Time.unscaledTime;
        PlayPreframe();
        SupportBehavior.inst.m_onUpdate.Add(PlayPreframe);
    }

    public void PlayPreframe()
    {
        try
        {
            if (!isDispose)
            {
                float sec = playSpd * (Time.unscaledTime - startPlayTime);
                bool hasCb = false;
                Action cb = () =>
                {
                    if (!hasCb)
                    {
                        hasCb = true;
                        SupportBehavior.inst.m_onUpdate.Remove(PlayPreframe);
                        endCb();
                        return;
                    }
                };
                if (go != null)
                {
                    go.transform.position = GetPosBetween(sec, pos, () =>
                    {
                        cb();
                    });
                    go.transform.rotation = GetRotBetween(sec, rot, () =>
                    {
                        cb();
                    });
                    go.transform.localScale = GetPosBetween(sec, scale, () =>
                    {
                        cb();
                    });
                }
                else
                {
                    cb();
                }
            }else
            {
                endCb();
            }
            
        }
        catch(Exception e)
        {
            Debug.LogError("!Error!" + e.ToString());
        }
    }

    public void Reset()
    {
        if (go != null)
        {
            go.transform.position = lastPos;
            go.transform.rotation = lastRot;
            go.transform.localScale = lastScale;
        }
    }

    public void Dispose()
    {
        SupportBehavior.inst.m_onUpdate.Remove(PlayPreframe);
        pos.Clear();
        rot.Clear();
        scale.Clear();
        isDispose = true;
    }

    private Vector3 GetPosBetween(float t,Dictionary<float, Vector3> dic,Action cb)
    {
        List<float> times = new List<float>(dic.Keys);
        for(int i = 0; i < times.Count; i++)
        {
            if (i+1 < times.Count)
            {
                if (t == times[i])
                {
                    return dic[times[i]];
                }
                if (times[i] < t && times[i + 1] > t)
                {
                    float k = times[i];
                    float nk = times[i + 1];
                    Vector3 min = dic[k];
                    Vector3 max = dic[nk];
                    float percent = (t - k) / (nk - k);
                    return min + ((max - min) * percent);
                }
                else if (t <= times[0])
                {
                    return dic[times[0]];
                }
            }
            else
            {
                //Vector3 p = dic[times[times.Count - 1]];
                cb();
                return lastPos;
            }
        }
        return lastPos;//dic[times[times.Count - 1]];
    }

    private Quaternion GetRotBetween(float t,Dictionary<float,Quaternion>dic,Action cb)
    {
        List<float> times = new List<float>(dic.Keys);
        for (int i = 0; i < times.Count; i++)
        {
            if (i + 1 < times.Count)
            {
                if (t == times[i])
                {
                    return dic[times[i]];
                }
                if (times[i] < t && times[i + 1] > t)
                {
                    float k = times[i];
                    float nk = times[i + 1];
                    Quaternion min = dic[k];
                    Quaternion max = dic[nk];
                    float percent = (t - k) / (nk - k);
                    return Quaternion.Lerp(min,max, percent);
                }
                else if (t <= times[0])
                {
                    return dic[times[0]];
                }
            }
            else
            {
                //Quaternion r = dic[times[times.Count - 1]];
                cb();
                return lastRot;
            }
        }
        return lastRot;
    }
}

public class Recorder {
    
    private Dictionary<GameObject, RecordStateList> records = new Dictionary<GameObject, RecordStateList>();

    private GameObject root;

    private static List<Recorder> allRecords = new List<Recorder>();

	public Recorder(GameObject go)
    {
        root = go;
        Clear();
    }

    public void Clear()
    {
        records.Clear();
        root.IterateGameObject(g =>
        {
            records.Add(g, new RecordStateList(g));
        });
    }

    public void StartRecord()
    {
        Clear();
        foreach (KeyValuePair<GameObject,RecordStateList>pair in records)
        {
            pair.Value.Start();
        }
    }

    public void Stop()
    {
        foreach (KeyValuePair<GameObject, RecordStateList> pair in records)
        {
            pair.Value.Stop();
        }
    }

    public void Play(float spd,Action cb)
    {
        int cbNum = 0;
        List<GameObject> gos = new List<GameObject>(records.Keys);
        for(int i = 0; i < gos.Count; i++)
        {
            if (gos[i] == null)
            {
                records.Remove(gos[i]);
            }
        }
        Behaviour[] comps = root.GetComponents<Behaviour>();
        for(int i = 0; i < comps.Length; i++)
        {
            comps[i].enabled = false;
        }
        foreach (KeyValuePair<GameObject, RecordStateList> pair in records)
        {
            pair.Value.Play(() => {
                cbNum++;
                if (cbNum >= records.Count)
                {
                    for (int i = 0; i < comps.Length; i++)
                    {
                        comps[i].enabled = true;
                    }
                    foreach (KeyValuePair<GameObject, RecordStateList> p in records)
                    {
                        p.Value.Reset();
                        p.Value.Dispose();
                    }
                    cb();
                }
            }, spd);
        }
    }

    public static void Record(List<GameObject> objs)
    {
        allRecords.Clear();
        for (int i = 0; i < objs.Count; i++)
        {
            allRecords.Add(new Recorder(objs[i]));
            allRecords[i].StartRecord();
        }
    }

    public static void StopRecord()
    {
        for (int i = 0; i < allRecords.Count; i++)
        {
            allRecords[i].Stop();
        }
    }

    public static void ReplayRecord(float spd,Action cb)
    {
        int id = 0;
        for(int i = 0; i < allRecords.Count; i++)
        {
            allRecords[i].Play(spd,()=>
            {
                id++;
                if (id == allRecords.Count)
                {
                    cb();
                    allRecords.Clear();
                }
            });
        }
    }
}

接下来是演示如何使用的案例脚本:

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


public class playerTest : MonoBehaviour {

    public Animator anim;
    private Vector3 targetPos;
    //private Recorder record;

    private bool isReplay = false;
    void Awake()
    {
        anim = GetComponent<Animator>();
        //record = new Recorder(gameObject);
    }

    void Start()
    {

    }

    void Update()
    {

    }

    private void OnGUI()
    {
        if (GUILayout.Button("Record"))
        {
            //record.StartRecord();
            Recorder.Record(new List<GameObject>() { gameObject });
        }
        if (GUILayout.Button("Play"))
        {
            isReplay = true;
            //anim.enabled = false;
            //record.Play(0.1f,()=> { });
            Recorder.ReplayRecord(0.5f, () =>
            {
                Debug.Log("All OK!!!");
            });
        }
        if (GUILayout.Button("Run1"))
        {
            anim.SetTrigger("Run01");
            targetPos = new Vector3(5f, 0, 3f);
        }
        if (GUILayout.Button("Run2"))
        {
            anim.SetTrigger("Run02");
            targetPos = new Vector3(3f, 0, 6f);
        }
        if (!isReplay && Vector3.Distance(transform.position, targetPos) > 0.1f)
        {
            transform.LookAtXZ(targetPos);
            transform.position += (targetPos - transform.position).normalized * 4f * Time.deltaTime;
        }
    }
}

SupportBehavior工具是之前博客记录的一个工具类,主要是延时功能。地址:https://blog.csdn.net/qq_32225289/article/details/84952406

遍历物体的子物体:

  /// <summary>
    /// 遍历go c
    /// </summary>
    /// <param name="go"></param>
    /// <param name="handle"></param>
    static public void IterateGameObject(this GameObject go, Action<GameObject> handle)
    {
        Queue q = new Queue();
        q.Enqueue(go);
        while (q.Count != 0)
        {
            GameObject tmpGo = (GameObject)q.Dequeue();
            foreach (Transform t in tmpGo.transform)
            {
                q.Enqueue(t.gameObject);
            }
            if (handle != null)
            {
                handle(tmpGo);
            }
        }
    }

PS:(若存在回放之后人物出现拉伸问题,注释掉和scale相关代码)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值