標籤:unity 遊戲開發
提要
今天做了一個行動裝置的網路通訊demo,分兩個部分,一個是網路連接,一個是資料通訊。
需要兩台Android裝置A,B。A作用戶端,B作服務端。
最終的效果是玩家控制裝置A中的方塊,B中的方塊也一起動,同時在A的加速度感應器的資訊在B中也即時更新。
網路連接
首先兩台裝置要連網,且IP在同一個網段,比如串連在同一個路由上,或者通過筆記本發出wifi訊號,然後把裝置連在上面。
在Unity3d中建立一個新工程,在情境中建立兩個空物體,一個Client,一個Server。
在client建立一個指令碼client.cs
using UnityEngine;using System.Collections;public class client : MonoBehaviour { private string IP = "10.66.208.191"; private string clientIp; private string clientIpSplite; private Vector3 acceleration; public GameObject cube; private bool cubeInitialed = false; //Connet port private int Port = 10000; void Awake() { clientIp = Network.player.ipAddress; string[] tmpArray = clientIp.Split(‘.‘); clientIpSplite = tmpArray[0] + "." + tmpArray[1] + "." + tmpArray[2] + "."; } void OnGUI() { switch (Network.peerType) { case NetworkPeerType.Disconnected: StartConnect(); break; case NetworkPeerType.Server: break; case NetworkPeerType.Client: OnConnect(); break; case NetworkPeerType.Connecting: break; } } void StartConnect() { if (GUILayout.Button("Connect Server")) { NetworkConnectionError error = Network.Connect(IP, Port); Debug.Log("connect status:" + error); } } void OnConnect() { if(!cubeInitialed) { Network.Instantiate(cube, transform.position, transform.rotation, 0); cubeInitialed = true; } }}
用戶端根據當前當前的狀態來執行相應的動作。StartConnect負責串連,用到了
static NetworkConnectionError Connect(string[] IPs, int remotePort)
第一個參數是Ip,第二個參數是連接埠。
串連上之後調用OnConnect函數初始化一個方塊。注意這個方塊是在用戶端初始化的,屬於這個用戶端,建立成功之後會在其他的一桶串連的裝置上都執行個體化一個cube出來,但是只有在這個client上NetworkView.isMine才為true。
接下來是服務端的代碼。
using UnityEngine;using System.Collections;public class server : MonoBehaviour { private int serverPort; public GUIText status; void Awake() { serverPort = 10000; } //OnGUI方法,所有GUI的繪製都需要在這個方法中實現 void OnGUI() { //Network.peerType是端類型的狀態: //即disconnected, connecting, server 或 client四種 switch (Network.peerType) { //禁止用戶端串連運行, 伺服器未初始化 case NetworkPeerType.Disconnected: StartServer(); break; //運行於伺服器端 case NetworkPeerType.Server: OnServer(); break; //運行於用戶端 case NetworkPeerType.Client: break; //正在嘗試串連到伺服器 case NetworkPeerType.Connecting: break; } GUILayout.Label(Network.player.ipAddress); } void StartServer() { //當使用者點擊按鈕的時候為true if (GUILayout.Button("建立伺服器")) { //初始化本機伺服器連接埠,第一個參數就是本機接收多少串連 NetworkConnectionError error = Network.InitializeServer(12, serverPort, false); Debug.Log("錯誤記錄檔" + error); } } void OnServer() { GUILayout.Label("服務端已經運行,等待用戶端串連"); int length = Network.connections.Length; for(int i = 0; i < length; i++) { GUILayout.Label("用戶端" + i); GUILayout.Label("用戶端ip" + Network.connections[i].ipAddress); GUILayout.Label("用戶端連接埠" + Network.connections[i].port); } } void OnSerializeNetworkView(BitStream stream, NetworkMessageInfo info){ // Always send transform (depending on reliability of the network view) if (stream.isWriting) { Vector3 pos = transform.localPosition; Quaternion rot = transform.localRotation; stream.Serialize(ref pos); stream.Serialize(ref rot); } // When receiving, buffer the information else { // Receive latest state information Vector3 pos = Vector3.zero; Quaternion rot = Quaternion.identity; stream.Serialize(ref pos); stream.Serialize(ref rot); }}}
點擊螢幕上的建立伺服器之後就在裝置上建立了一個服務端,監聽對應的連接埠,當有其他裝置串連上來的時間,用戶端的資訊就會列印出來,可以支援多個裝置的串連。
還要建立一個cube的prefab,用於動態建立。
CubeController用於控制方塊的運動,NetWorkView用於資料通訊。
資料通訊
需要進行資料通訊的GameObject都要添加一個NetworkView 組件,資料通訊有兩種方式,狀態同步和RPC(遠端程序呼叫)。在CubeController.cs中,兩種方法都有用到。
using UnityEngine;using System.Collections;public class CubeController : MonoBehaviour { private GUIText accelText; void Start() { accelText = GameObject.FindGameObjectWithTag("AccelTip").GetComponent<GUIText>() as GUIText; accelText.text = ""; } void Update() { if(Network.isClient) { Vector3 acceleration = Input.acceleration; accelText.text = "" + acceleration; networkView.RPC("UpdateAcceleration", RPCMode.Others, acceleration); } Vector3 moveDir = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical")); Vector3 cubescreenPos = Camera.main.WorldToScreenPoint(transform.position); if (Input.GetMouseButton(0)) { moveDir = new Vector3(Input.mousePosition.x - cubescreenPos.x, Input.mousePosition.y - cubescreenPos.y, 0f).normalized; } Debug.Log("moveDir: " + moveDir); float speed = 5; transform.Translate(speed * moveDir * Time.deltaTime); } void OnSerializeNetworkView(BitStream stream, NetworkMessageInfo info) { if (stream.isWriting) { Vector3 pos = transform.position; stream.Serialize(ref pos); } else { Vector3 receivedPosition = Vector3.zero; stream.Serialize(ref receivedPosition); transform.position = receivedPosition; } } [RPC] void UpdateAcceleration(Vector3 acceleration) { accelText.text = "" + acceleration; }}
function OnSerializeNetworkView(stream : BitStream, info : NetworkMessageInfo) {}
這是在Network class中提供的一個func. 主要負責message sent / receive,他會同步被network view所關注的script中的對象,也就是當你寫了一個script內含OnSerializeNetworkView(){},並且丟到observed屬性中,則OnSerializeNetworkView()裡的code就會開始運作。基本上他透過BitStream物件收髮網路上的資訊,使用上不需要瞭解封包的問題,也不需要知道如何切割封包。在這的demo中,服務端只負責接收資訊,所以只執行else後面的代碼,用戶端發送資訊,執行if後面的代碼。
這裡cube的state synchronization選的是Unreliable,對應的通訊協議是UDP,特點是無串連,比較快。
RPC典型的應用情境就是聊天室,使用也非常簡單,首先定義一個rpc函數在(在上面加上[RPC]),然後通過NetWork.RPC來調用就可以了。這裡是把用戶端重力感應器的資料傳了出去,在介面上更新。
參考
unity3D的網路資料傳輸 & 角色控制 - http://ppb440219.blogspot.com/2011/12/unity3d.html
網路視圖 Network View - http://game.ceeger.com/Components/class-NetworkView.html
遠端程序呼叫的細節 RPC Details - http://game.ceeger.com/Components/net-RPCDetails.html
狀態同步的細節 State Synchronization Detailshttp://game.ceeger.com/Components/net-StateSynchronization.html
Unity Networking Tutorial - http://www.palladiumgames.net/tutorials/unity-networking-tutorial/