记录一个回放工具,留在今后备用!
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相关代码)