前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >unity3d:网络同步,状态同步,源码,C#服务器demo

unity3d:网络同步,状态同步,源码,C#服务器demo

作者头像
立羽
发布2023-08-24 15:43:25
3320
发布2023-08-24 15:43:25
举报
文章被收录于专栏:Unity3d程序开发Unity3d程序开发

协议数据单元

网络同步包最小单元PDU

代码语言:javascript
复制
// 预测的基础数据类型
public class PDU
{
public uint UID;            //玩家的唯一id
public PDUType type;		//PDU类型
public Vector3 position;	// 位置
public Vector3 forward;		// 朝向
public float speed;			// 速度: 速度为0表示静止
public float time;			// PDU发出的时间
public string anim;			// 当前的动作
}

需要发送PDU的情况,即是状态改变时情况

代码语言:javascript
复制
public enum PDUType
{
None			= 0, 	// 没有产生任何改动
OutOrbit 		= 1,	// 超出轨道
OverThreshold 	= 2,	// 和本地模拟超过一定的阈值
SpeedChange 	= 4,	// 速度发生改变
ActChange 		= 8,	// 动作发生改变
All 			= OutOrbit | OverThreshold | SpeedChange | ActChange,
};

超出轨道

a背后有个观察者b,b对着a运动的方向,发射一个带有宽度的轨道。a超过了轨道即发送PDU,好处是在玩家速度,方向不变时,只需要发送一次PDU,而不需要每时每刻都发送 图下两条绿线即为轨道

在这里插入图片描述
在这里插入图片描述

当a相对b的本地坐标.x超过了轨道轨道宽度的一半,即触发了超过轨道

代码语言:javascript
复制
// 检查是否还在轨道内,每帧调用
bool inOrbitJudge()
{
Vector3 currentPos = gameObject.transform.position;
Quaternion rot = transform.localRotation;

Vector3 vct = m_PDUCreater.transform.InverseTransformPoint(currentPos);
if ( Mathf.Abs(vct.x) > orbitWidth/2.0)
{
return false;
}

return true;
}

同时更新b的位置与朝向

代码语言:javascript
复制
Vector3 dir = transform.position - lastPosition;
m_PDUCreater.transform.position = transform.position;
m_PDUCreater.transform.LookAt(transform.position + dir.normalized * 5);

本地模拟超过一定的阈值

本地模拟出的位置b(根据发出的pdu的朝向,速度每帧计算出),与发送者的位置a偏差超过阈值。这么做的原因是玩家原地转向也能识别到,一般手游都是摇杆,还是比较难做到原地转向

代码语言:javascript
复制
localSimulatedPosition += currentPDU.forward * currentPDU.speed * Time.deltaTime;

if ((localSimulatedPosition - transform.position).magnitude > DistanceTolerance)// 如果和本地模拟超过一定的阈值也要发送PDU
{
iPDUType |= PDUType.OverThreshold;
}

客户端同步服务器时间

每个客户端每隔1s同步服务器时间,得到时间s后,会在本地进行update模拟累加 发送时会记录发送时间戳

代码语言:javascript
复制
//向服务器发送请求服务器时间
void SendSyncTime()
{
sendSyncTime = Time.time;
GameSocket.Instance.SendMsgProtoVoid(MsgIdDefine.ReqHeartBeat);
}

接收时,记录接收时间戳,假设一次传输的时间延迟发送,接收各占一半,此时服务器时间为

代码语言:javascript
复制
void OnRspHeartBeat(PtLong data)
{
float reciveNetTimeDiff = Time.time - sendSyncTime;
float serverTime = (float)data.value + reciveNetTimeDiff * 0.5f;
TimeManager.self.currentTime = serverTime;
}

远程玩家

远程玩家是个镜像,当有新PDU传入时,做插值运动到预测的位置 没有时,按照上一次的PDU状态运动,例如上一次有速度时,按照速度*朝向移动;上一次是没速度时,持续禁止状态

新PDU传入

远程的位置应该为 PDU传输过来的位置 + 朝向 * 速度 * (插值时间 + 消息延迟)

代码语言:javascript
复制
//当新PDU传入时改变远程玩家位置,朝向,动画,速度
if(newPDUComing)
{
//DeterminStateByAnimation(realPDU.anim);
//float curTime = pvpWJY.ServerTime.currentTime;
float curTime = TimeManager.self.currentTime;
float oldTime = realPDU.time;

// 消息延迟时间
float timeDiffer = curTime - oldTime;
if(timeDiffer < 0 || timeDiffer > 2)
Debug.LogError("server time error : " + timeDiffer);

timeDiffer = Mathf.Clamp(timeDiffer, 0, 2);

smoothTime = realSmoothTime;

// 公式:插值的目标位置 = PDU传输过来的位置 + 朝向 * 速度 * (插值时间 + 消息延迟)
targetPosition = realPDU.position + realPDU.forward * realPDU.speed * timeDiffer;
targetForward = realPDU.forward;
startLerpPosition = transform.position;
startLerpForward = transform.forward;
newPDUComing = false;

transform.position = targetPosition;
}

//当还剩下平滑插值时间,继续插值
if (smoothTime > 0)
{
smoothTime -= Time.deltaTime;
transform.position = Vector3.Lerp(targetPosition, startLerpPosition, smoothTime / realSmoothTime);
transform.forward = Vector3.Slerp(targetForward, startLerpForward, smoothTime / realSmoothTime);

}
else
{
if (realPDU != null)
{
transform.position += realPDU.forward * realPDU.speed * Time.deltaTime;
}
}

Demo演示

白色是玩家,发送数据给服务器 黑色是远程镜像,接收到服务器PDU包进行模拟运动 type为PDU改变的类型 在均速直线运动阶段,产生的网络包较少

在这里插入图片描述
在这里插入图片描述

源码

https://github.com/luoyikun/UnityForTest 先启动服务器 UnityForTest\Server\MultiServer.sln运行

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在局域网下,服务器会定时向局域网UDP广播TCP服务器的端口号 客户端接到了TCP的端口号,连接服务器 客户端场景 UnityForTest\Assets\NetSync\gdePvp\WjyNetSync.unity 点击运行,等待连接上服务器即可

在这里插入图片描述
在这里插入图片描述

按ws前进后退,ad转向

PS: 1.理论上,电脑是单网卡,一行代码不用改,先运行服务器,再运行客户端,可看效果 2.如果是笔记本或台式机有双网卡,既有有线,无线,自动获取的ip地址有2个,所以要看下代码写死ip地址 3.如果电脑装了虚拟机,也会出现双ip,也要看下代码写死ip地址

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-08-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 协议数据单元
    • 超出轨道
      • 本地模拟超过一定的阈值
      • 客户端同步服务器时间
      • 远程玩家
        • 新PDU传入
        • Demo演示
        • 源码
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档