using System; using System.Collections.Generic; using Cysharp.Threading.Tasks; using JNGame.Sync.Frame.Service; using JNGame.Sync.System.View; using UnityEngine; namespace JNGame.Sync.Frame { public abstract class JNSyncFrameService : JNSyncDefaultService { public override int DeltaTime => _nSyncTime / _nDivideFrame; //同步时间 (和服务器保持一致) private int _nSyncTime = 67; public int NSyncTime => _nSyncTime; //大于多少帧进行追帧 private int _nMaxFrameBan = 4; public int NMaxFrameBan => _nMaxFrameBan; //大于多少帧进行快速追帧 private int _nMaxFrameLoopBan = 20; public int NMaxFrameLoopBan => _nMaxFrameLoopBan; //将服务器帧数进行平分 private int _nDivideFrame = 2; public int NDivideFrame => _nDivideFrame; //本地帧数 private int _nLocalFrame = -1; public int NLocalFrame => _nLocalFrame; //帧队列 private Queue _nFrameQueue = new(); public Queue NFrameQueue => _nFrameQueue; //暂存帧列表 private Dictionary _nFrameTempQueue = new(); //是否请求后台数据 private bool _isRequestServerData = false; //帧更新 int dtTotal = 0; //输入更新 int dtInputTotal = 0; //是否开始同步 private bool _isStart = false; public bool IsStart => _isStart; //是否在追帧 private bool _isLoop = false; public bool IsLoop => _isLoop; protected JNSyncFrameService() : base() { } public override void Initialize() { base.Initialize(); _isStart = true; } /// /// 视图帧更新 /// public override void Execute() { #if (!ENTITAS_DISABLE_VISUAL_DEBUGGING && UNITY_EDITOR) if (paused) return; #endif if (!IsStart) return; base.Execute(); int deltaTime = TickTime; dtTotal += deltaTime; dtInputTotal += deltaTime; try { int nSyncTime = this.DyTime(); if (nSyncTime > 0) { this._isLoop = false; if (dtTotal > nSyncTime && _nFrameQueue.Count > 0) { this.OnRunSimulate(); dtTotal -= nSyncTime; } if (_nFrameQueue.Count <= 0) { dtTotal = 0; } } else { this._isLoop = true; //追帧运行 保持前端 15 帧 刷新 long endTime = (new DateTimeOffset(DateTime.UtcNow)).ToUnixTimeMilliseconds() + 100; while (this.DyTime() == 0 && ((new DateTimeOffset(DateTime.UtcNow)).ToUnixTimeMilliseconds()) < endTime) { this.OnRunSimulate(); } dtTotal = 0; } //更新输入 if (dtInputTotal > (_nSyncTime / _nDivideFrame)) { dtInputTotal = 0; OnRunInput(); } } catch (Exception e) { Debug.LogError(e.Message); } } /// /// 运行输入 /// public void OnRunInput() { var inputs = GetInputs(); if (inputs.Inputs.Count > 0) { OnSendInput(inputs); } } /// /// 自适应间隔时间 /// /// public int DyTime(){ int dt = this._nSyncTime / this._nDivideFrame; int loop = dt; // return dt; //大于nMaxFrameBan 进行 追帧 if(this._nFrameQueue.Count > this._nMaxFrameBan) { //计算超过的帧数 int exceed = this._nFrameQueue.Count - this._nMaxFrameBan; int most = this._nMaxFrameLoopBan - this._nMaxFrameBan; int ldt = ((most - exceed) / most) * dt; //自适应追帧算法 if(exceed <= this._nMaxFrameLoopBan){ loop = ldt; }else{ loop = 0; } }else{ loop = dt; } return loop; } /// /// 接受帧数 /// public void AddFrame(JNFrameInfo frame,bool isLatestData = true) { //我需要的下一帧 int index = _nLocalFrame + 1; //判断接受的帧是否下一帧 如果不是则加入未列入 if (frame.Index != index){ _nFrameTempQueue.TryAdd(frame.Index,frame); //在未列入中拿到需要的帧 JNFrameInfo tamp = null; if ((tamp = _nFrameTempQueue.GetValueOrDefault(index,null)) == null) { var that = this; //如果没有则向服务器请求我需要的帧数 if (!that._isRequestServerData) { that._isRequestServerData = true; //请求 that.OnServerData(this._nLocalFrame, 0).ContinueWith(infos => { foreach (var frameInfo in infos.Frames) { if(frameInfo.Index >= this._nLocalFrame) that.AddFrame(frameInfo,false); } that._isRequestServerData = false; }).Forget(); } return; } else { //如果有则覆盖 frame = tamp; } } //删除临时帧 _nFrameTempQueue.Remove(frame.Index); _nLocalFrame = index; //分帧插入 _nFrameQueue.Enqueue(frame); for (var i = 0; i < _nDivideFrame - 1; i++) { _nFrameQueue.Enqueue(new JNFrameInfo()); } } /// /// 获取输入 /// /// protected abstract JNFrameInputs GetInputs(); /// /// 运行帧 /// protected virtual void OnRunSimulate() { Debug.Log("Run OnRunSimulate"); if (!(NFrameQueue.TryDequeue(out var frame))) return; Simulate(); } /// /// 发送帧数据 /// /// protected abstract void OnSendInput(JNFrameInputs inputs); /// /// 获取帧数据 /// /// /// /// protected abstract UniTask OnServerData(int start,int end); } }