关于Unity的回放功能(二)
前言
上次因为公司项目没有完全展开,所以上次写的回放功能不是最终的版本,现在有时间,可以将回放功能所用到的内容从新整理记录一下。用于以后做参考。在上一篇文章中写了一部分了,具体可以查看上一篇文章,这次是对上一篇文章进行更加细化的整理和编写。其中有一部分涉及到了公司项目,所以有报错的地方,但是思路的话还是比较清晰的。
直接上代码
using Common;
using DG.Tweening;
//using Net.Share;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using UI;
using UnityEngine;
using UnityEngine.UI;
//用于控制回放时各个UI按钮及玩家移动的类
using Common;
using DG.Tweening;
//using Net.Share;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using UI;
using UnityEngine;
using UnityEngine.UI;
public class ReplayManager : MonoSingleton<ReplayManager>
{
public ReplayPanel replayPanel;
public ReplayData data;//首次所用data数据
public bool rePlay;
public float timer;
//按下按键时间,用于玩家按下、抬起WASD按键
public List<float> pressTimeW, liftUpTimeW;
public List<float> pressTimeA, liftUpTimeA;
public List<float> pressTimeS, liftUpTimeS;
public List<float> pressTimeD, liftUpTimeD;
public List<float> mouseDownTime, mouseUpTime;
//这个时间的话是判断在按下、抬起WASD按键的过程中间隔是多久,通过时间差来回放玩家的移动
float Wtime;
float Atime;
float Stime;
float Dtime;
float rotationTime;
/// <summary>
/// 玩家移动类
/// </summary>
public PlayerMove playerMove;
public float replaySpeed = 1;//回放速度
public float totalTimer = 0;//回放的总时长
/// <summary>
/// 提示框
/// </summary>
public GameObject message;
protected override void Initialize()
{
data = new ReplayData();
pressTimeW = new List<float>();
liftUpTimeW = new List<float>();
pressTimeA = new List<float>();
liftUpTimeA = new List<float>();
pressTimeS = new List<float>();
liftUpTimeS = new List<float>();
pressTimeD = new List<float>();
liftUpTimeD = new List<float>();
mouseDownTime = new List<float>();
mouseUpTime = new List<float>();
playerMove = GameObject.FindObjectOfType<PlayerMove>();
}
public float replayTimer;
protected override void Awake()
{
base.Awake();
}
/// <summary>
/// 判断回放是否完成
/// </summary>
public bool isFinish = false;
private void Update()
{
if (!rePlay)
{
timer += Time.deltaTime * replaySpeed;
}
if (rePlay)
{
if (replayTimer >= data.totalTimer)
{
isFinish = true;
if (isFinish == true)
{
message.SetActive(true);
replayPanel.totalTime = "";
message.transform.Find("GameObject/Content").GetComponent<Text>().text = "流程回放已结束,请退出该任务流程回放";
//在这里要设置一下
replayPanel.transform.Find("Buttons").gameObject.SetActive(false);
}
rePlay = false;
}
replayTimer += Time.deltaTime * replaySpeed;
StartReplayProgress();
}
}
/// <summary>
/// 这里是在回放过程中,对按钮的事件进行复现的功能,复现完成后,删除对应的按钮事件
/// </summary>
private void StartReplayProgress()
{
for (int i = 0; i < UIManager.Instance.buttonEventTime.Count; i++)
{
if (UIManager.Instance.buttonEventTime[i] < replayTimer)
{
//如果小于的话,直接删除这个元素
foreach (var button in UIManager.Instance.buttons)
{
try
{
if (button == null) continue;
if (button.name == data.buttonDic[UIManager.Instance.buttonEventTime[i]])
{
button.onClick.Invoke();
UIManager.Instance.buttonEventTime.Remove(UIManager.Instance.buttonEventTime[i]);
return;
}
}
catch (Exception e)
{
Debug.Log(button.name);
Debug.Log(e.ToString());
}
}
}
}
for (int i = 0; i < UIManager.Instance.inputFieldTime.Count; i++)
{
if (UIManager.Instance.inputFieldTime[i] < replayTimer)
{
//如果小于的话,直接删除这个元素
foreach (var input in UIManager.Instance.inputFields)
{
if (input == null) continue;
foreach (var name in data.inputFieldDic[UIManager.Instance.inputFieldTime[i]].Keys)
{
if (input.name == name)
{
try
{
input.onValueChanged.Invoke(data.inputFieldDic[UIManager.Instance.inputFieldTime[i]][UIManager.Instance.inputFieldName[i]]);
UIManager.Instance.inputFieldName.Remove(name);
UIManager.Instance.inputFieldTime.Remove(UIManager.Instance.inputFieldTime[i]);
return;
}
catch (Exception e)
{
Debug.Log(e.ToString());
}
}
}
}
}
}
//对回放的InputToggle进行复现
for (int i = 0; i < UIManager.Instance.inputToggleTime.Count; i++)
{
if (UIManager.Instance.inputToggleTime[i] < replayTimer)
{
//如果小于的话,直接删除这个元素
foreach (var toggle in UIManager.Instance.toggles)
{
if (toggle == null) continue;
foreach (var name in data.toggleDic[UIManager.Instance.inputToggleTime[i]].Keys)
{
//如果InputToggle的名字重复的话,有时候就会判断错误,所以需要给他再添加一个判定方式
//这里用增加一个父物体来进行判定
if (toggle.transform.parent.name + toggle.name == name)
{
try
{
Debug.Log("当前Toggle的IsOn为:" + data.toggleDic[UIManager.Instance.inputToggleTime[i]][UIManager.Instance.togglesName[i]]);
toggle.isOn = data.toggleDic[UIManager.Instance.inputToggleTime[i]][UIManager.Instance.togglesName[i]];
toggle.onValueChanged.Invoke(data.toggleDic[UIManager.Instance.inputToggleTime[i]][UIManager.Instance.togglesName[i]]);
UIManager.Instance.inputToggleTime.Remove(UIManager.Instance.inputToggleTime[i]);
UIManager.Instance.togglesName.Remove(UIManager.Instance.togglesName[i]);
return;
}
catch (Exception e)
{
Debug.Log(e.ToString());
}
}
}
}
}
}
//对回放的DropDown的按钮进行复现
for (int i = 0; i < UIManager.Instance.dropDownTime.Count; i++)
{
if (UIManager.Instance.dropDownTime[i] < replayTimer)
{
//如果小于的话,直接删除这个元素
foreach (var dropdown in UIManager.Instance.dropdowns)
{
if (dropdown == null) continue;
foreach (var name in data.dropDownDic[UIManager.Instance.dropDownTime[i]].Keys)
{
//如果Toggle的名字重复的话,有时候就会判断错误,所以需要给他再添加一个判定方式
//这里用增加一个父物体来进行判定
if (dropdown.transform.parent.name + dropdown.name == name)
{
try
{
dropdown.value = data.dropDownDic[UIManager.Instance.dropDownTime[i]][name];
dropdown.onValueChanged.Invoke(data.dropDownDic[UIManager.Instance.dropDownTime[i]][name]);
UIManager.Instance.dropDownTime.Remove(UIManager.Instance.dropDownTime[i]);
UIManager.Instance.dropDownName.Remove(UIManager.Instance.dropDownName[i]);
return;
}
catch (Exception e)
{
Debug.Log(e.ToString());
}
}
}
}
}
}
for (int i = 0; i < pressTimeA.Count; i++)
{
if (pressTimeA[i] < replayTimer)//当前时间如果大于按下的时间的话,那么就调用人物移动的方法
{
if (pressTimeA[i] < liftUpTimeA[i])
{
Atime += Time.deltaTime * replaySpeed;
if (Atime < liftUpTimeA[i] - pressTimeA[i])
{
playerMove.transform.Translate(-data.AoriginPlayerTimeAndSpeedDic[pressTimeA[i]] * replaySpeed, 0, 0, Space.Self);
}
else
{
pressTimeA.Remove(pressTimeA[i]);
liftUpTimeA.Remove(liftUpTimeA[i]);
Atime = 0;
return;
}
}
}
}
for (int i = 0; i < pressTimeW.Count; i++)
{
if (pressTimeW[i] < replayTimer)//当前时间如果大于按下的时间的话,那么就调用人物移动的方法
{
if (pressTimeW[i] < liftUpTimeW[i])
{
Wtime += Time.deltaTime * replaySpeed;
if (Wtime < liftUpTimeW[i] - pressTimeW[i])
{
playerMove.transform.Translate(0, 0, data.WoriginPlayerTimeAndSpeedDic[pressTimeW[i]] * replaySpeed, Space.Self);
}
else
{
pressTimeW.Remove(pressTimeW[i]);
liftUpTimeW.Remove(liftUpTimeW[i]);
Wtime = 0;
return;
}
}
}
}
for (int i = 0; i < pressTimeS.Count; i++)
{
if (pressTimeS[i] < replayTimer)//当前时间如果大于按下的时间的话,那么就调用人物移动的方法
{
if (pressTimeS[i] < liftUpTimeS[i])
{
Stime += Time.deltaTime * replaySpeed;
if (Stime < liftUpTimeS[i] - pressTimeS[i])
{
playerMove.transform.Translate(0, 0, -data.SoriginPlayerTimeAndSpeedDic[pressTimeS[i]] * replaySpeed, Space.Self);
}
else
{
pressTimeS.Remove(pressTimeS[i]);
liftUpTimeS.Remove(liftUpTimeS[i]);
Stime = 0;
return;
}
}
}
}
for (int i = 0; i < pressTimeD.Count; i++)
{
if (pressTimeD[i] < replayTimer)//当前时间如果大于按下的时间的话,那么就调用人物移动的方法
{
if (pressTimeD[i] < liftUpTimeD[i])
{
Dtime += Time.deltaTime * replaySpeed;
if (Dtime < liftUpTimeD[i] - pressTimeD[i])
{
playerMove.transform.Translate(data.DoriginPlayerTimeAndSpeedDic[pressTimeD[i]] * replaySpeed, 0, 0, Space.Self);
}
else
{
pressTimeD.Remove(pressTimeD[i]);
liftUpTimeD.Remove(liftUpTimeD[i]);
Dtime = 0;
return;
}
}
}
}
for (int i = 0; i < mouseUpTime.Count; i++)
{
if (mouseUpTime[i] < replayTimer)//当前时间如果大于按下的时间的话,那么就调用人物旋转的方法
{
if ((i + 1) < mouseUpTime.Count)
{
rotationTime = mouseUpTime[i + 1] - mouseUpTime[i];
playerMove.transform.DORotate(new Vector3(data.playerUpRotX[mouseUpTime[i]], data.playerUpRotY[mouseUpTime[i]], data.playerUpRotZ[mouseUpTime[i]]), rotationTime / (replaySpeed));
data.playerUpRotX.Remove(mouseUpTime[i]);
data.playerUpRotY.Remove(mouseUpTime[i]);
data.playerUpRotZ.Remove(mouseUpTime[i]);
mouseUpTime.Remove(mouseUpTime[i]);
}
}
}
}
//重新回放时清除之前所有的数据
public void ClearData()
{
UIManager.Instance.buttonEventTime.Clear();
UIManager.Instance.inputFieldTime.Clear();
UIManager.Instance.inputFieldName.Clear();
UIManager.Instance.inputToggleTime.Clear();
UIManager.Instance.dropDownTime.Clear();
UIManager.Instance.togglesName.Clear();
UIManager.Instance.dropDownName.Clear();
UIManager.Instance.goChangeTime.Clear();
pressTimeW.Clear();
liftUpTimeW.Clear();
pressTimeA.Clear();
liftUpTimeA.Clear();
pressTimeS.Clear();
liftUpTimeS.Clear();
pressTimeD.Clear();
liftUpTimeD.Clear();
mouseUpTime.Clear();
//在这里首先判断出来是否有任务准备和射后恢复阶段
string Processcontent = "";
}
public void GetData(ReplayData data)
{
//根据得到的所有命令,在这里开始播放
foreach (var item in data.buttonDic.Keys)
{
UIManager.Instance.buttonEventTime.Add(item);
}
foreach (var item in data.inputFieldDic.Keys)
{
UIManager.Instance.inputFieldTime.Add(item);
}
foreach (var item in data.inputFieldDic.Keys)
{
foreach (var name in data.inputFieldDic[item].Keys)
{
UIManager.Instance.inputFieldName.Add(name);
}
}
foreach (var item in data.toggleDic.Keys)
{
UIManager.Instance.inputToggleTime.Add(item);
}
foreach (var item in data.toggleDic.Keys)
{
foreach (var name in data.toggleDic[item].Keys)
{
UIManager.Instance.togglesName.Add(name);
}
}
foreach (var item in data.dropDownDic.Keys)
{
foreach (var name in data.dropDownDic[item].Keys)
{
UIManager.Instance.dropDownName.Add(name);
}
}
foreach (var item in data.goNameDic.Keys)
{
UIManager.Instance.goChangeTime.Add(item);
}
foreach (var item in data.dropDownDic.Keys)
{
UIManager.Instance.dropDownTime.Add(item);
}
//得到所有移动和旋转按键按下时的时间
foreach (var item in data.WoriginPlayerTimeAndSpeedDic.Keys)
{
pressTimeW.Add(item);
}
foreach (var item in data.WendPlayerTimeAndSpeedDic.Keys)
{
liftUpTimeW.Add(item);
}
foreach (var item in data.AoriginPlayerTimeAndSpeedDic.Keys)
{
pressTimeA.Add(item);
}
foreach (var item in data.AendPlayerTimeAndSpeedDic.Keys)
{
liftUpTimeA.Add(item);
}
foreach (var item in data.SoriginPlayerTimeAndSpeedDic.Keys)
{
pressTimeS.Add(item);
}
foreach (var item in data.SendPlayerTimeAndSpeedDic.Keys)
{
liftUpTimeS.Add(item);
}
foreach (var item in data.DoriginPlayerTimeAndSpeedDic.Keys)
{
pressTimeD.Add(item);
}
foreach (var item in data.DendPlayerTimeAndSpeedDic.Keys)
{
liftUpTimeD.Add(item);
}
foreach (var item in data.playerUpRotX.Keys)
{
mouseUpTime.Add(item);
}
//在这里让replayPanel出现
replayPanel.gameObject.SetActive(true);
this.data = data;
}
//开始回放
public void StartProcess()
{
replayTimer = 0;
rePlay = true;
}
//获取所有的玩家位置
public void GetCurrentPos(Dictionary<float, float> playerPosX, Dictionary<float, float> playerPosY, Dictionary<float, float> playerPosZ)
{
playerPosX.Add(timer, transform.position.x);
playerPosY.Add(timer, transform.position.y);
playerPosZ.Add(timer, transform.position.z);
}
//从服务器得到之前保存的回放数据
//[Rpc]
public void GetFirstRePlayDataResult(ReplayData data)
{
this.data = data;
Debug.Log("得到文件成功");
GetData(this.data);
//获取之前保存的回放时的总时长
replayPanel.totalTime = FormatNumberDateTime(((int)data.totalTimer).ToString());
}
/// <summary>
/// 秒转换成时分秒(分的)
/// </summary>
/// <param name="secondTime"></param>
/// <returns></returns>
public string FormatNumberDateTime(string secondTime)
{
if (string.IsNullOrEmpty(secondTime))
{
return "";
}
else
{
long mss = long.Parse(secondTime);
string DateTimes = null;
long days = mss / (60 * 60 * 24);
long hours = (mss % (60 * 60 * 24)) / (60 * 60);
long minutes = (mss % (60 * 60)) / 60;
long seconds = mss % 60;
if (minutes > 0)
{
DateTimes = minutes + ":" + seconds;
}
else
{
DateTimes = "00:" + seconds;
}
return DateTimes;
}
}
}
接下来是UI面板控制回放
using MVC;
//using Net.Share;
using System;
using System.Collections;
using System.Collections.Generic;
using UI;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Video;
public class ReplayPanel : BasePanel
{ // 开始流程 重新开始流程
private Button startProcessBtn, reStartProcess;
private Button returnProcessBtn;//退出回放流程
private Text speedText, totalTimeText;
protected override void Awake()
{
base.Awake();
InitComponent();
AddListener();
}
public override void InitComponent()
{
base.InitComponent();
startProcessBtn = transform.Find("StartProcessBtn").GetComponent<Button>();
returnProcessBtn = transform.Find("ReturnProcessBtn").GetComponent<Button>();
reStartProcess = transform.Find("ReStartProcessBtn").GetComponent<Button>();
speedText = transform.Find("Buttons/Speed").GetComponent<Text>();
totalTimeText = transform.Find("Buttons/总时长").GetComponent<Text>();
}
public string totalTime;
//当前回放时间
public float timer;
private void Start()
{
reStartProcess.gameObject.SetActive(false);
//向服务器获取保存的回放数据 玩家用户名 任务名称 第几次回放
Send("GetRePlayData", LoginManager.instance.replayAcc, NetworkManager.instance.taskName, LoginManager.instance.replayNumber, true);//在这里得到具体哪一次的回放
}
private void Update()
{
if (ReplayManager.Instance.rePlay)
{
timer += Time.deltaTime * ReplayManager.Instance.replaySpeed;
long minutes = ((long)timer % (60 * 60)) / 60;
long seconds = (long)timer % 60;
totalTimeText.text = string.Format("{0:D2}:{1:D2}", minutes, seconds) + "/" + totalTime;
}
}
public override void AddListener()
{
base.AddListener();
//在这里的话之前还有暂停、继续、加速、减速回放过程,因为涉及到项目内容,所以这里暂时不写。
startProcessBtn.onClick.AddListener(StartProcess);
reStartProcess.onClick.AddListener(ReStartProcess);
}
/// <summary>
/// 重新开始流程
/// </summary>
private void ReStartProcess()
{
CoverListMessage.ins.Show("温馨提示", "是否重新开始回放流程?", "重新回放流程");
}
/// <summary>
/// 重新播放回放流程
/// </summary>
public void RestartProcessResult()
{
ReplayManager.Instance.replaySpeed = 1;
ReplayManager.Instance.isFinish = false;
speedText.text = "回放速度: 1X";
Time.timeScale = 1;
//先把之前uimanager里面的数据全部删除
ReplayManager.Instance.ClearData();
//重新给存储的数据赋值,然后开始重新回放
Send("GetRePlayData", LoginManager.instance.replayAcc, NetworkManager.instance.taskName, LoginManager.instance.replayNumber, false);//在这里得到具体哪一次的回放
}
private void StartProcess()
{
ReplayManager.Instance.StartProcess();
}
//[Rpc]
public void GetRePlayDataResult(ReplayData data)
{
ReplayManager.Instance.data = data;
Debug.Log("重新得到文件成功");
transform.SetAsLastSibling();
ReplayManager.Instance.GetData(ReplayManager.Instance.data);
UnityEngine.SceneManagement.Scene scene = UnityEngine.SceneManagement.SceneManager.GetActiveScene();
timer = 0;
totalTime = FormatNumberDateTime(((int)data.totalTimer).ToString());
totalTimeText.text = string.Format("{0:D2}:{1:D2}", 0, 0) + "/" + totalTime;
//开始重新执行
StartProcess();
}
/// <summary>
/// 秒转换成时分秒(分的)
/// </summary>
/// <param name="secondTime"></param>
/// <returns></returns>
public string FormatNumberDateTime(string secondTime)
{
if (string.IsNullOrEmpty(secondTime))
{
return "";
}
else
{
long mss = long.Parse(secondTime);
string DateTimes = null;
long days = mss / (60 * 60 * 24);
long hours = (mss % (60 * 60 * 24)) / (60 * 60);
long minutes = (mss % (60 * 60)) / 60;
long seconds = mss % 60;
if (minutes > 0)
{
DateTimes = minutes + ":" + seconds;
}
else
{
DateTimes = "00:" + seconds;
}
return DateTimes;
}
}
}
接下来是通用单例类,如果项目中有单例类的话就不用写这个类了。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 脚本单例类
/// </summary>
public abstract class MonoSingleton<T> : MonoBehaviour where T : MonoSingleton<T>
{
//public static T Instance { get; private set; }
//private void Awake()
//{
// Instance = this as T;
//}
//何时何地都可以正确访问Instance?
//按需加载
private static T instance;
public static T Instance
{
get
{
if (instance == null)
{
//instance = this as T;
instance = FindObjectOfType<T>();
//如果在场景中没有找到该对象
if (instance == null)
{
//创建脚本对象,立即执行Awake,为instance赋值。
new GameObject("Singleton of " + typeof(T)).AddComponent<T>();
}
else
{
instance.Initialize();
}
}
return instance;
}
}
public static T I
{
get { return Instance; }
}
protected virtual void Initialize() { }
/*
如果自行附加到物体中,希望instance属性可以在Awake赋值。
情景:
1. 如果单例类自行附加到游戏对象中。
-- 客户端代码在Awake中访问Instance
-- 客户端代码在Awake以后访问Instance
2. 如果单例类没有附加。
*/
protected virtual void Awake()
{
if (instance == null)
{
instance = this as T;
Initialize();
}
}
/*
如何使用脚本单例类:
1. 定义管理类,继承单例类,传递管理类类型。
2. 重写Init方法进行初始化。
**********************************
3. 何时何地都可以通过以下代码访问管理类成员
管理类.Instance.成员
*/
}
接下来就是保存的数据类了
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
[Serializable]
public class ReplayData
{
public ReplayData()
{
buttonDic = new Dictionary<float, string>();
toggleDic = new Dictionary<float, Dictionary<string, bool>>();
dropDownDic = new Dictionary<float, Dictionary<string, int>>();
inputFieldDic = new Dictionary<float, Dictionary<string, string>>();
WoriginPlayerTimeAndSpeedDic = new Dictionary<float, float>();
WendPlayerTimeAndSpeedDic = new Dictionary<float, float>();
AoriginPlayerTimeAndSpeedDic = new Dictionary<float, float>();
AendPlayerTimeAndSpeedDic = new Dictionary<float, float>();
SoriginPlayerTimeAndSpeedDic = new Dictionary<float, float>();
SendPlayerTimeAndSpeedDic = new Dictionary<float, float>();
DoriginPlayerTimeAndSpeedDic = new Dictionary<float, float>();
DendPlayerTimeAndSpeedDic = new Dictionary<float, float>();
AplayerPosX = new Dictionary<float, float>();
AplayerPosY = new Dictionary<float, float>();
AplayerPosZ = new Dictionary<float, float>();
WplayerPosX = new Dictionary<float, float>();
WplayerPosY = new Dictionary<float, float>();
WplayerPosZ = new Dictionary<float, float>();
SplayerPosX = new Dictionary<float, float>();
SplayerPosY = new Dictionary<float, float>();
SplayerPosZ = new Dictionary<float, float>();
DplayerPosX = new Dictionary<float, float>();
DplayerPosY = new Dictionary<float, float>();
DplayerPosZ = new Dictionary<float, float>();
playerDownRotX = new Dictionary<float, float>();
playerDownRotY = new Dictionary<float, float>();
playerDownRotZ = new Dictionary<float, float>();
playerDownRotW = new Dictionary<float, float>();
playerUpRotX = new Dictionary<float, float>();
playerUpRotY = new Dictionary<float, float>();
playerUpRotZ = new Dictionary<float, float>();
playerUpRotW = new Dictionary<float, float>();
}
public float totalTimer = 0;
public Dictionary<float, string> buttonDic;//时间、按钮名称
public Dictionary<float, Dictionary<string, bool>> toggleDic;//时间、toggle名称、按钮的bool值
public Dictionary<float, Dictionary<string, int>> dropDownDic;//时间、DropDown名称、DropDown的index值
public Dictionary<float, Dictionary<string, string>> inputFieldDic;//时间、input的名字,input里面的内容
public Dictionary<float, float> WoriginPlayerTimeAndSpeedDic;//按下W键位的时间、速度
public Dictionary<float, float> WendPlayerTimeAndSpeedDic; //松开W键位的时间、速度
public Dictionary<float, float> AoriginPlayerTimeAndSpeedDic;//按下A键位的时间、速度
public Dictionary<float, float> AendPlayerTimeAndSpeedDic; //松开A键位的时间、速度
public Dictionary<float, float> SoriginPlayerTimeAndSpeedDic;//按下S键位的时间、速度
public Dictionary<float, float> SendPlayerTimeAndSpeedDic; //松开S键位的时间、速度
public Dictionary<float, float> DoriginPlayerTimeAndSpeedDic;//按下D键位的时间、速度
public Dictionary<float, float> DendPlayerTimeAndSpeedDic; //松开D键位的时间、速度
public Dictionary<float, float> AplayerPosX;//按下A键位时当前时间、当前玩家位置X坐标
public Dictionary<float, float> AplayerPosY;//按下A键位时当前时间、当前玩家位置Y坐标
public Dictionary<float, float> AplayerPosZ;
public Dictionary<float, float> WplayerPosX;//当前时间,当前玩家位置X坐标
public Dictionary<float, float> WplayerPosY;//当前时间、当前玩家位置Y坐标
public Dictionary<float, float> WplayerPosZ;
public Dictionary<float, float> SplayerPosX;//当前时间、当前玩家位置X坐标
public Dictionary<float, float> SplayerPosY;//当前时间、当前玩家位置Y坐标
public Dictionary<float, float> SplayerPosZ;
public Dictionary<float, float> DplayerPosX;//当前时间、当前玩家位置X坐标
public Dictionary<float, float> DplayerPosY;//当前时间、当前玩家位置Y坐标
public Dictionary<float, float> DplayerPosZ;
public Dictionary<float, float> playerDownRotX;//当前时间、当前玩家旋转X坐标
public Dictionary<float, float> playerDownRotY;
public Dictionary<float, float> playerDownRotZ;
public Dictionary<float, float> playerDownRotW;
public Dictionary<float, float> playerUpRotX;//当前时间、当前玩家旋转X坐标
public Dictionary<float, float> playerUpRotY;
public Dictionary<float, float> playerUpRotZ;
public Dictionary<float, float> playerUpRotW;
}
在程序中按下UI(比如按钮、toggle、在InputField)时,将此时按下的时间记录下来,保存到程序中。例如在按下按钮、Toggle、InputField触发事件时,同时也将数据进行记录。
//按下按钮
if (!ReplayManager.Instance.rePlay)
ReplayManager.Instance.data.buttonDic.Add(ReplayManager.Instance.timer, submitButton.name);
//按下Toggle开关
if (!ReplayManager.Instance.rePlay)
{
ReplayManager.Instance.data.toggleDic.Add(ReplayManager.Instance.timer, new Dictionary<string, bool>());
ReplayManager.Instance.data.toggleDic[ReplayManager.Instance.timer].Add(item.parent.name + item.GetComponent<Toggle>().name, isOn);
}
//在InputField中输入文字
if (!ReplayManager.Instance.rePlay)
{
ReplayManager.Instance.data.inputFieldDic.Add(ReplayManager.Instance.timer, new Dictionary<string, string>());
ReplayManager.Instance.data.inputFieldDic[ReplayManager.Instance.timer].Add(input.gameObject.name, content);
}
在最后保存的时候,将ReplayData类发送给服务器,将ReplayData中的数据转化为二进制文件进行保存,保存的代码为
/// <summary>
/// 保存回放记录
/// </summary>
/// <param name="playerAcc">玩家用户名</param>
/// <param name="taskName">任务名称</param>
/// <param name="data">数据类</param>
[Rpc]
public void SetRePlayData(string playerAcc, string taskName, ReplayData data)
{
//首先设置数据库信息
db = new SQLServerDatabase(0);
db.UpdateValues("Student_TaskPlan",
new string[] { "hasReplay" },
new string[] { "true" },
"playerAcc", playerAcc, "TaskName", taskName);
int numberOfReplayTime = serverManager.player.numberOfTask + 1;
string rootPath = Environment.CurrentDirectory.ToString();
string path = rootPath + "\\" + "ReplayFile" + "\\" + playerAcc;//通过人物名生成第一个文件夹
string path2 = path + "\\" + taskName;
string path3 = path2 + "\\" + "第" + numberOfReplayTime.ToString() + "次回放.rep";//保存第几次考核的回放
DirectoryInfo d = Directory.CreateDirectory(path);//创建根目录 返回值是一个文件夹
DirectoryInfo d2 = Directory.CreateDirectory(path2);
File.Create(path3).Close();
ReplayData replayData = new ReplayData();
replayData = data;
BinaryFormatter bf = new BinaryFormatter();
using (FileStream fs = File.Create(path3))
{
bf.Serialize(fs, replayData);
}
//发送给玩家,告诉玩家回放已经保存成功
serverManager.Send(serverManager.player, "SetRePlayDataResult", "保存回放成功");
}
//在这里是读取之前保存的回放数据
///用户名、任务名称、想要查看第几次回放
public void GetRePlayData(string playerAcc, string taskName, string numberOfTask, bool isFirst)
{
string rootPath = Environment.CurrentDirectory.ToString();
string path = rootPath + "\\" + "ReplayFile" + "\\" + playerAcc + "\\" + taskName + "\\" + numberOfTask + ".rep";
BinaryFormatter bf = new BinaryFormatter();
ReplayData data = new ReplayData();
using (FileStream fs = File.Open(path, FileMode.Open, FileAccess.Read))
{
//创建data类
data = bf.Deserialize(fs) as ReplayData;
}
if (isFirst)
serverManager.SendRT(serverManager.player, "GetFirstRePlayDataResult", data);
else
{
serverManager.SendRT(serverManager.player, "GetRePlayDataResult", data);
}
}
总结
写到这里差不多就将回放功能的大致框架完成了,中间可能有些乱,但是写这篇文章的初衷就是给自己以后看的,大致内容还是比较清楚的。如果有看到这篇文章后想要了解更多、或者有什么不懂的地方的朋友可以加我一起讨论。