多人状态同步 知识和问题回顾

多人状态同步 知识和问题回顾我想实现自由视角 虚拟摇杆和多人位置同步的功能 将这三个功能放一块展示 看了下网盘日期 是去年 6 月份写的 虚拟摇杆和移动的同步 角色移动是往前的 所以只要确认角色 forward 向量的世界坐标 移动问题就能解决 键盘上的 wasd 操作只有 vertical 0 1 和 horizontal 0 1 它只有 8 个方向 通常键盘操作的游戏改变方向 按下方向键持续旋转角度来实现的 虚拟摇杆不同于键盘

大家好,我是讯享网,很高兴认识大家。

我想实现自由视角,虚拟摇杆和多人位置同步的功能。将这三个功能放一块展示。看了下网盘日期,是去年6月份写的。

虚拟摇杆和移动的同步

角色移动是往前的,所以只要确认角色forward向量的世界坐标,移动问题就能解决。

键盘上的wasd操作只有vertical[0,1]和horizontal[0,1]。它只有8个方向。通常键盘操作的游戏改变方向,按下方向键持续旋转角度来实现的。

虚拟摇杆不同于键盘,它可以确定任意一个方向。

protected override void Start() 
讯享网{ base.Start(); mRadius = (transform as RectTransform).sizeDelta.x * 0.5f; } public override void OnDrag (UnityEngine.EventSystems.PointerEventData eventData) { base.OnDrag (eventData); var contentPostion = this.content.anchoredPosition; if (contentPostion.magnitude > mRadius){ contentPostion = contentPostion.normalized * mRadius ; SetContentAnchoredPosition(contentPostion); } 
}
讯享网
利用UGUI的ScrollRect,分为摇杆底盘和摇杆,摇杆对底盘的相对坐标向量就是了。


摇杆往前推,角色是相对谁往前的方向。角色理当是往摄像机的前方移动,无论角色目前面向何处。

Vector3 Joy = new Vector3 (contentPostion.x, 0, contentPostion.y).normalized; Vector3 Cam = Vector3.Scale(Camera.main.transform.forward, new Vector3(1, 0, 1)).normalized; JoyAngle = Joy.x > 0?Vector2.Angle (new Vector2 (0, 1), new Vector2 (Joy.x, Joy.z)):360-Vector2.Angle (new Vector2 (0, 1), new Vector2 (Joy.x, Joy.z)); CamAngle = Cam.x > 0?Vector2.Angle (new Vector2(0,1),new Vector2(Cam.x,Cam.z)):360-Vector2.Angle (new Vector2(0,1),new Vector2(Cam.x,Cam.z)); sumAngle = JoyAngle+CamAngle;

接受到摇杆输入的方向contentPostion后,在为角色重新确定前方需要考虑到摄像机的方向。sumAngle是角色相对世界旋转角度。

讯享网dragLevel = System.Math.Abs(contentPostion.x)/mRadius > 0.6f || System.Math.Abs(contentPostion.y)/mRadius > 0.6f? 2f : 1f;

根据拖放摇杆的位置和底盘半径比得出强度。做走和跑。

状态传输

public override void OnEndDrag (UnityEngine.EventSystems.PointerEventData eventData) 
{ base.OnEndDrag (eventData); dragLevel &#61; 0; MessageHandle.sendDragState (sumAngle, dragLevel); }</code></pre> 

只在摇杆变动时传输摇杆状态,静止时则不需要频繁的传输位置或拖放信息,节省资源。

同样接收到的摇杆状态,但服务器或其他客户端不能和自己客户端完全一样地模拟出行为,时间久差距变拉大。 所以还要定时更新位置信息。
 void Update(){ 
 if (Time.time &gt; time) { time &#61; Time.time &#43; 5f; if(GameObject.Find(StatePool.localEp)!&#61;null) sendMyTransform (); } }</code></pre> 


服务器 采用socket异步通信方式,没什么好说的,看官方文档。 方便起见,我只将从客户端收到的信息再进行分发而已。
public class SocketClient : MonoBehaviour { 
static byte[] inbytes &#61; new byte[1024]; static byte[] outbytes &#61; new byte[1024]; public Socket socket; void Awake() { socket &#61; new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socket.BeginConnect (&#34;127.0.0.1&#34;, 200, connectTarget, socket); } void connectTarget(IAsyncResult ar) { socket.EndConnect (ar); StatePool.localEp &#61; socket.LocalEndPoint.ToString(); StatePool.RemoteEp &#61; socket.RemoteEndPoint.ToString(); Initialization.initPlayers(); socket.BeginReceive (inbytes, 0, inbytes.Length, SocketFlags.None, receiveTarget, socket); } public void sendMsg(string msgSend) { outbytes &#61; Encoding.UTF8.GetBytes (msgSend); socket.BeginSend(outbytes, 0, outbytes.Length, SocketFlags.None, sendTarget, socket); } void sendTarget(IAsyncResult ar) { } void receiveTarget(IAsyncResult ar) { int size &#61; socket.EndReceive (ar); if (size &gt; 0) { MessageHandle.readMsg(Encoding.UTF8.GetString (inbytes, 0, size)); } socket.BeginReceive(inbytes, 0, 1024, SocketFlags.None, receiveTarget, socket); } 
}


客户端消息接收
public class SocketClient : MonoBehaviour { 
static byte[] inbytes &#61; new byte[1024]; static byte[] outbytes &#61; new byte[1024]; public Socket socket; void Awake() { socket &#61; new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socket.BeginConnect (&#34;127.0.0.1&#34;, 200, connectTarget, socket); } void connectTarget(IAsyncResult ar) { socket.EndConnect (ar); StatePool.localEp &#61; socket.LocalEndPoint.ToString(); StatePool.RemoteEp &#61; socket.RemoteEndPoint.ToString(); Initialization.initPlayers(); socket.BeginReceive (inbytes, 0, inbytes.Length, SocketFlags.None, receiveTarget, socket); } public void sendMsg(string msgSend) { outbytes &#61; Encoding.UTF8.GetBytes (msgSend); socket.BeginSend(outbytes, 0, outbytes.Length, SocketFlags.None, sendTarget, socket); } void sendTarget(IAsyncResult ar) { } void receiveTarget(IAsyncResult ar) { int size &#61; socket.EndReceive (ar); if (size &gt; 0) { MessageHandle.readMsg(Encoding.UTF8.GetString (inbytes, 0, size)); } socket.BeginReceive(inbytes, 0, 1024, SocketFlags.None, receiveTarget, socket); } 
}

消息处理
客户端收到信息后,调用MessageHandle处理。
public static void readMsg(string str) { 
 Loom.RunAsync(()&#61;&gt;{ Loom.QueueOnMainThread(()&#61;&gt;{ print(str); string[] msgList &#61; str.Split(&#39;$&#39;); string jsonData &#61; msgList[1]; switch (msgList[0].Split(&#39;/&#39;)[1]) { case &#34;03&#34;: lb.addLog (&#34;accept 请求: 我的位置&#34;); if (StatePool.player){ sendMyTransform(); }else{ sendMyCreate(); } break; case &#34;19&#34;: transformInfo t &#61; JsonUtility.FromJson&lt;transformInfo&gt;(jsonData); newCharacter (character, t.Ep, t.postion, t.eulerAngles); break; case &#34;18&#34;: lb.addLog (&#34;accept 收到: 位置角度信息,更新或创建&#34;); transformInfo t1 &#61; JsonUtility.FromJson&lt;transformInfo&gt;(jsonData); if(GameObject.Find(t1.Ep)!&#61;null){ reTransform(GameObject.Find(t1.Ep), t1.postion, t1.eulerAngles); }else{ newCharacter(GameObject.Find(StatePool.localEp), t1.Ep, t1.postion, t1.eulerAngles); } break; case &#34;28&#34;: dragState d &#61; JsonUtility.FromJson&lt;dragState&gt;(jsonData); if(GameObject.Find(d.Ep)){ GameObject.Find(d.Ep).GetComponent&lt;move&gt;().setDragState(d.sumAngle, d.dragLevel); } break; } }); }); } 

socket异步通信是多线程的,通过void receiveTarget(IAsyncResult ar) 收到消息后主动去处理,这不在主线程之内。 UnityEngine不能在主线程之外调用。这里使用了一段代码使其在主线程中跑,当然这不合适。 如果将收到的信息储存,另在主线程中去读取,这就被动了,没有实时性可言,也不该这么做。
数据传输格式 socket的传输类型是bytes[]。消息层次是多层的,如数据类型,是否要求回复…当然这些也能用9_5_2_7代替。 我将消息当作string处理,用符号分割成数组。主体数据则用unity自带的JsonUtility,转换成对象。

小讯
上一篇 2025-02-19 23:52
下一篇 2025-02-21 07:16

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/14341.html