消息类
using ProtoBuf;
using SGF.Codec;
using SGF.Network.Core;
using SGF.Utils;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 说明:最终所有的数据都存储在【FrameMessages】单例中,将该单例序列化保存到数据库中。
/// FrameMessage:参考代码:NetMessage.cs
/// MsgHead:参考代码:ProtocolHead.cs
/// </summary>
[ProtoContract]
public class FrameMessages
{
[ProtoMember(1)]
public List<string> SerializedMsgs = new List<string>();
[ProtoMember(2)]
public List<int> MsgsLength = new List<int>();
public List<FrameMessage> OriginMsgs = new List<FrameMessage>();
private static FrameMessages m_value = new FrameMessages();
public static FrameMessages Value { get { return m_value; } }
public int MsgCount = 0;
public void GetCountOrigin()
{
MsgCount = OriginMsgs.Count;
Debug.Log("OriginMsgs Count: " + MsgCount);
}
public void GetCountSerialized()
{
MsgCount = SerializedMsgs.Count;
Debug.Log("SerializedMsgs Count: " + MsgCount);
}
#if UNITY_EDITOR
public readonly static string Path = Application.persistentDataPath + "/AppConfig_Editor.data";
#else
public readonly static string Path = Application.persistentDataPath + "/AppConfig.data";
#endif
public static void Init()
{
Debug.Log("Path = " + Path);
var data = FileUtils.ReadFile(Path);
if (data != null && data.Length > 0)
{
var cfg = PBSerializer.NDeserialize(data, typeof(FrameMessages)); //反序列化整个FrameMessages
if (cfg != null)
{
m_value = cfg as FrameMessages;
m_value.GetCountSerialized();
for(int i = 0;i < m_value.MsgCount;i ++)
{
FrameMessage frmMsg = new FrameMessage();
frmMsg.Deserialize(SGFEncoding.HexToBytes(m_value.SerializedMsgs[i]), m_value.MsgsLength[i]);//反序列化每条FrameMessage
//Debug.Log("OriginInfo: " + frmMsg.head.frame);
//Debug.Log("MsgsLength: " + m_value.MsgsLength[i]);
//Debug.Log("TestList: " + m_value.TestList[i]);
m_value.OriginMsgs.Add(frmMsg);
}
}
}
}
public static void Save()
{
if (m_value != null)
{
m_value.GetCountOrigin();
for(int i = 0;i < m_value.MsgCount; i ++)
{
byte[] tmp = null;
int len = m_value.OriginMsgs[i].Serialize(out tmp); //序列化每条FrameMessage
if (tmp != null)
{
m_value.SerializedMsgs.Add(SGFEncoding.BytesToHex(tmp, len));
m_value.MsgsLength.Add(len);
}
else
{
Debug.LogError("Tmp is null");
}
}
byte[] data = PBSerializer.NSerialize(m_value); //序列化整个FrameMessages
FileUtils.SaveFile(Path, data);
}
}
}
public class FrameMessage
{
private static NetBuffer DefaultWriter = new NetBuffer(4096);
private static NetBuffer DefaultReader = new NetBuffer(4096);
public MsgHead head = new MsgHead(); //此处是可以自定义的
public byte[] content; //此处是可以自定义的
public FrameMessage Deserialize(NetBuffer buffer)
{
head.Deserialize(buffer); //反序列化协议头
content = new byte[head.dataSize];
buffer.ReadBytes(content, 0, head.dataSize); //把内容读出来
return this;
}
public NetBuffer Serialize(NetBuffer buffer)
{
head.Serialize(buffer); //先把自定义的协议头,序列化到buffer中
buffer.WriteBytes(content, 0, head.dataSize); //再用buffer读取协议内容
return buffer; //现在所有内容都在buffer【NetBuffer】对象中,下一步要查看,这个对象怎么发送过去。
}
public FrameMessage Deserialize(byte[] buffer, int size)
{
//lock (DefaultReader)
//{
DefaultReader.Attach(buffer, size);
return Deserialize(DefaultReader);
//}
}
public int Serialize(out byte[] tempBuffer)
{
//lock (DefaultWriter)
//{
DefaultWriter.Clear();
this.Serialize(DefaultWriter); //现在所有内容都在DefaultWriter【NetBuffer】对象中,下一步要查看,这个对象怎么发送过去。
tempBuffer = DefaultWriter.GetBytes(); //通过GetBytes导出了所有的比特流
return DefaultWriter.Length; //通过.length获得比特流长度
//}
}
}
public class MsgHead
{
public const int Length = 16; //协议头 16字节
public int objID = 0; //用户ID
public int cmd = 0; //命令字
public int frame = 0; //序列号,为了实现一一对应
public ushort dataSize = 0; //数据长度
public ushort checksum = 0; //校验和
public MsgHead Deserialize(NetBuffer buffer)
{
MsgHead head = this; //返回的其实没必要返回,就是对象本身
head.objID = buffer.ReadInt();
head.cmd = buffer.ReadInt();
head.frame = buffer.ReadInt();
head.dataSize = buffer.ReadUShort();
head.checksum = buffer.ReadUShort();
return head;
}
/// <summary>
/// 把自定义的协议头,序列化进buffer中
/// </summary>
/// <param name="buffer"></param>
/// <returns></returns>
public NetBuffer Serialize(NetBuffer buffer)
{
buffer.WriteInt(objID);
buffer.WriteInt(cmd);
buffer.WriteInt(frame);
buffer.WriteUShort(dataSize);
buffer.WriteUShort(checksum);
return buffer;
}
}
public enum CarState
{
GOOD, //完好无损
BROKEN, //坏了
SUBMERGED, //陷入
}
[ProtoContract]
public class CarMessage
{
[ProtoMember(1)]
public bool active;
[ProtoMember(2)]
public float[] pos;
[ProtoMember(3)]
public float[] rot;
[ProtoMember(4)]
public bool engine_on; //引擎状态
[ProtoMember(5)]
public CarState car_state;
[ProtoMember(6)]
public string anim_signal; //状态机信号
}
[ProtoContract]
public class HumanMessage
{
[ProtoMember(1)]
public bool active;
[ProtoMember(2)]
public float[] pos;
[ProtoMember(3)]
public float[] rot;
}
[ProtoContract]
public class CamMessage
{
[ProtoMember(1)]
public bool active;
[ProtoMember(2)]
public float[] pos;
[ProtoMember(3)]
public float[] rot;
}
public class UIClickMessage
{
[ProtoMember(1)]
public bool active;
[ProtoMember(2)]
public bool selected;
[ProtoMember(3)]
public bool setted;
[ProtoMember(4)]
public string clicked_item; //选中的UI名称
}
状态记录抽象类
using SGF.Codec;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 物体状态控制器
/// 自我判断状态是否发生变化,若发生变化,向timeline发送该帧数据
/// </summary>
public abstract class IStateController : MonoBehaviour
{
public int ObjID { get; private set; } //物体唯一ID
protected Timeline _timeline;
protected virtual void Awake()
{
GetObjID();
ms_ctrls.Add(ObjID, this); //将ID及对应StateController加入到列表
_timeline = Timeline.Instance;
GlobalEvent.onUpdate += Tick;
}
protected virtual void OnDestroy()
{
_timeline = null;
GlobalEvent.onUpdate -= Tick;
}
protected abstract void Init(); //初始化监听信息
protected abstract void Tick(); //每帧更新,判断自身管理状态是否发生变化
public void Send<TMsg>(int cmd, TMsg msg)
{
byte[] b_msg = PBSerializer.NSerialize(msg);
_timeline.ReceiveStates(ObjID, cmd, b_msg); //向timeline传递数据
}
public static void Recv(int cmd, byte[] msg)
{
}
/// <summary>
/// 获取ID
/// </summary>
private void GetObjID()
{
ObjID = -1;
ObjID = GameConfigDatabase.GetConfigData<ObjIDConf>(gameObject.name).ID;
if (ObjID == -1)
{
Debug.LogError("配表错误:未获取正确ID");
}
}
private static Dictionary<int, IStateController> ms_ctrls = new Dictionary<int, IStateController>(); //存放了所有的ID及对应控制器
public static IStateController GetCtrls(int objid)
{
return ms_ctrls[objid];
}
}
状态记录实现类
using System.Linq;
using UnityEngine;
/// <summary>
/// 人物状态
/// </summary>
public class HumanStateController : IStateController
{
private HumanMessage _sendMsg;
private HumanMessage _recvMsg;
public void SetRecvMsg(HumanMessage recvmsg, bool performMsg = false)
{
_recvMsg = recvmsg;
if(performMsg)
{
PerformMsg();
}
}
protected override void Awake()
{
base.Awake();
Init();
}
protected override void Init()
{
_sendMsg = new HumanMessage()
{
active = gameObject.activeSelf,
pos = transform.position.ToVecArray(),
rot = transform.rotation.ToQuatArray()
};
}
protected override void Tick()
{
if(Appmain.ms_appLoadtype == AppLoadtype.RECORD)
{
if(CheckDifference())
{
Send(ProtoCmd.HumanMsg, _sendMsg);
}
}
else if(Appmain.ms_appLoadtype == AppLoadtype.PLAYBACK)
{
//PerformMsg();
}
else
{
}
}
private bool CheckDifference()
{
bool changed = false;
if(_sendMsg.active != gameObject.activeSelf)
{
//Debug.Log("Change 01");
_sendMsg.active = gameObject.activeSelf;
changed = true;
}
float[] vpos = transform.position.ToVecArray();
if (!Enumerable.SequenceEqual (_sendMsg.pos, vpos))
{
//Debug.Log("Change 02 " + _sendMsg.pos[0] + _sendMsg.pos[1] + _sendMsg.pos[2] + " " + vpos[0] + vpos[1] + vpos[2]);
_sendMsg.pos = vpos;
changed = true;
}
float[] vquat = transform.rotation.ToQuatArray();
if (!Enumerable.SequenceEqual(_sendMsg.rot, vquat))
{
//Debug.Log("Change 03");
_sendMsg.rot = vquat;
changed = true;
}
return changed;
}
public void PerformMsg()
{
if (_recvMsg.active != gameObject.activeSelf)
{
gameObject.SetActive(_recvMsg.active);
}
Vector3 pos = _recvMsg.pos.ToVec3();
if (pos != transform.position)
{
transform.position = pos;
}
Quaternion quat = _recvMsg.rot.ToQuat4();
if (quat != transform.rotation)
{
transform.rotation = quat;
}
}
}
逻辑帧Timeline
using SGF.Codec;
using SGF.Event;
using SGF.Unity.Common;
using UnityEngine;
/// <summary>
/// 说明:定义了逻辑帧,逻辑帧可以保证录制和回放的同步。
/// 录制:ReceiveStates
/// 回放:SendFrameDatas
/// </summary>
public class Timeline : MonoSingleton<Timeline>
{
/// <summary>
/// 一帧时间内发生的所有事,都算这帧里的。
/// </summary>
public int Rate = 30; //每秒记录多少帧
public int CurrentFrame { get; private set; } //当前记录帧
private float _timer; //总时间轴
private float _currentTime //当前帧所在的时间点
{
get { return CurrentFrame / (float)Rate; } //当前帧/帧/s = 秒
}
public static SGFEvent onPlaybackOver = new SGFEvent(); //回放结束
protected override void InitSingleton()
{
base.InitSingleton();
_timer = 0;
CurrentFrame = 0;
}
private bool _readInit = true;
private void Update()
{
if(Input.GetKeyDown(KeyCode.O))
{
//将数据序列化并保存
Appmain.ms_appLoadtype = AppLoadtype.STOP;
}
else if(Input.GetKeyDown(KeyCode.P))
{
//将数据反序列化并开始分发
Appmain.ms_appLoadtype = AppLoadtype.PLAYBACK;
}
if(Appmain.ms_appLoadtype == AppLoadtype.RECORD)
{
_timer += Time.deltaTime;
while(_currentTime < _timer) //当前帧所在时间点,小于真正的时间,那么就补帧补到时间追上。(假如上一帧是第五帧,此刻是第十帧。然后,所有人向第十帧发送数据。)
{
CurrentFrame++;
}
GlobalEvent.onUpdate.Invoke(); //在当前帧设置完毕后,状态管控者们开始进行update。
}
else if(Appmain.ms_appLoadtype == AppLoadtype.PLAYBACK)
{
if(_readInit)
{
_readInit = false;
FrameMessages.Init();
for (int i = 0; i < FrameMessages.Value.MsgCount; i++)
{
FrameMessage msg = FrameMessages.Value.OriginMsgs[i];
MsgHead msghead = msg.head;
//Debug.Log("msg info: " + msghead.objID + " " + msghead.frame);
}
}
//return;
_timer += Time.deltaTime;
_lastReadFrm = CurrentFrame;
while (_currentTime < _timer) //当前帧所在时间点,小于真正的时间,那么就补帧补到时间追上。(假如上一帧是第五帧,此刻是第十帧。然后检测第五帧到第十帧的所有数据,逐帧执行。)
{
CurrentFrame++;
}
SendFrameDatas(_lastReadFrm, CurrentFrame);
GlobalEvent.onUpdate.Invoke(); //发送完数据后,各自控制器开始更新到最新的数据
}
else if(Appmain.ms_appLoadtype == AppLoadtype.STOP)
{
Appmain.ms_appLoadtype = AppLoadtype.NONE;
FrameMessages.Save();
}
}
//录制
public void ReceiveStates(int objid, int cmd, byte[] msg)
{
FrameMessage frmMsg = new FrameMessage();
frmMsg.head = new MsgHead();
frmMsg.head.objID = objid;
frmMsg.head.frame = CurrentFrame;
frmMsg.head.cmd = cmd;
frmMsg.head.dataSize = (ushort)msg.Length;
frmMsg.content = msg;
//Debug.Log("msg info: " + objid + " " + CurrentFrame + " " + frmMsg.head.dataSize);
FrameMessages.Value.OriginMsgs.Add(frmMsg);
}
private int _msgsPointIndex = 0; //回放时候,列表播放到哪的指针
private int _lastReadFrm = 0;
//回放
private void SendFrameDatas(int startFrm, int endFrm)
{
for(int i = _msgsPointIndex;i < FrameMessages.Value.MsgCount;i ++)
{
FrameMessage msg = FrameMessages.Value.OriginMsgs[i];
MsgHead msghead = msg.head;
if (msghead.frame >= startFrm && msghead.frame < endFrm)
{
//派发执行数据
switch (msghead.cmd)
{
case ProtoCmd.HumanMsg:
HumanMessage hmsg = PBSerializer.NDeserialize<HumanMessage>(msg.content);
HumanStateController hsc = IStateController.GetCtrls(msghead.objID) as HumanStateController;
hsc.SetRecvMsg(hmsg, true);
break;
case ProtoCmd.CarMsg:
CarMessage cmsg = PBSerializer.NDeserialize<CarMessage>(msg.content);
CarStateController csc = IStateController.GetCtrls(msghead.objID) as CarStateController;
csc.SetRecvMsg(cmsg, true);
break;
}
}
else
{
_msgsPointIndex = i;
break;
}
}
}
}