我想实现自由视角,虚拟摇杆和多人位置同步的功能。将这三个功能放一块展示。看了下网盘日期,是去年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 = 0; MessageHandle.sendDragState (sumAngle, dragLevel); }</code></pre>
只在摇杆变动时传输摇杆状态,静止时则不需要频繁的传输位置或拖放信息,节省资源。同样接收到的摇杆状态,但服务器或其他客户端不能和自己客户端完全一样地模拟出行为,时间久差距变拉大。所以还要定时更新位置信息。void Update(){if (Time.time > time) { time = Time.time + 5f; if(GameObject.Find(StatePool.localEp)!=null) sendMyTransform (); } }</code></pre>
服务器采用socket异步通信方式,没什么好说的,看官方文档。方便起见,我只将从客户端收到的信息再进行分发而已。public class SocketClient : MonoBehaviour {static byte[] inbytes = new byte[1024]; static byte[] outbytes = new byte[1024]; public Socket socket; void Awake() { socket = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socket.BeginConnect ("127.0.0.1", 200, connectTarget, socket); } void connectTarget(IAsyncResult ar) { socket.EndConnect (ar); StatePool.localEp = socket.LocalEndPoint.ToString(); StatePool.RemoteEp = socket.RemoteEndPoint.ToString(); Initialization.initPlayers(); socket.BeginReceive (inbytes, 0, inbytes.Length, SocketFlags.None, receiveTarget, socket); } public void sendMsg(string msgSend) { outbytes = Encoding.UTF8.GetBytes (msgSend); socket.BeginSend(outbytes, 0, outbytes.Length, SocketFlags.None, sendTarget, socket); } void sendTarget(IAsyncResult ar) { } void receiveTarget(IAsyncResult ar) { int size = socket.EndReceive (ar); if (size > 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 = new byte[1024]; static byte[] outbytes = new byte[1024]; public Socket socket; void Awake() { socket = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socket.BeginConnect ("127.0.0.1", 200, connectTarget, socket); } void connectTarget(IAsyncResult ar) { socket.EndConnect (ar); StatePool.localEp = socket.LocalEndPoint.ToString(); StatePool.RemoteEp = socket.RemoteEndPoint.ToString(); Initialization.initPlayers(); socket.BeginReceive (inbytes, 0, inbytes.Length, SocketFlags.None, receiveTarget, socket); } public void sendMsg(string msgSend) { outbytes = Encoding.UTF8.GetBytes (msgSend); socket.BeginSend(outbytes, 0, outbytes.Length, SocketFlags.None, sendTarget, socket); } void sendTarget(IAsyncResult ar) { } void receiveTarget(IAsyncResult ar) { int size = socket.EndReceive (ar); if (size > 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(()=>{ Loom.QueueOnMainThread(()=>{ print(str); string[] msgList = str.Split('$'); string jsonData = msgList[1]; switch (msgList[0].Split('/')[1]) { case "03": lb.addLog ("accept 请求: 我的位置"); if (StatePool.player){ sendMyTransform(); }else{ sendMyCreate(); } break; case "19": transformInfo t = JsonUtility.FromJson<transformInfo>(jsonData); newCharacter (character, t.Ep, t.postion, t.eulerAngles); break; case "18": lb.addLog ("accept 收到: 位置角度信息,更新或创建"); transformInfo t1 = JsonUtility.FromJson<transformInfo>(jsonData); if(GameObject.Find(t1.Ep)!=null){ reTransform(GameObject.Find(t1.Ep), t1.postion, t1.eulerAngles); }else{ newCharacter(GameObject.Find(StatePool.localEp), t1.Ep, t1.postion, t1.eulerAngles); } break; case "28": dragState d = JsonUtility.FromJson<dragState>(jsonData); if(GameObject.Find(d.Ep)){ GameObject.Find(d.Ep).GetComponent<move>().setDragState(d.sumAngle, d.dragLevel); } break; } }); }); }
socket异步通信是多线程的,通过void receiveTarget(IAsyncResult ar) 收到消息后主动去处理,这不在主线程之内。UnityEngine不能在主线程之外调用。这里使用了一段代码使其在主线程中跑,当然这不合适。如果将收到的信息储存,另在主线程中去读取,这就被动了,没有实时性可言,也不该这么做。
数据传输格式socket的传输类型是bytes[]。消息层次是多层的,如数据类型,是否要求回复…当然这些也能用9_5_2_7代替。我将消息当作string处理,用符号分割成数组。主体数据则用unity自带的JsonUtility,转换成对象。

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