網(wǎng)上有很多關于pos機關聯(lián)app,unity3D 對接 workerman 實現(xiàn)聯(lián)機游戲的知識,也有很多人為大家解答關于pos機關聯(lián)app的問題,今天pos機之家(m.afbey.com)為大家整理了關于這方面的知識,讓我們一起來看下吧!
本文目錄一覽:
pos機關聯(lián)app
前言:Unity3d,相信很多人都知道是用來做游戲的。網(wǎng)上也有很多這類視頻的教程,我也試著學習過。但是當要實現(xiàn)多人實時對戰(zhàn)的教例比較少,而用 PHP 來做 Unity3d 的服務器端的就更少了。
我在網(wǎng)上看了一個作者用 C# 做聯(lián)機服務器端的文章后,就根據(jù)他的思路改了一個 PHP 版的。例子只是多個方塊在一個場景下移動,所有玩家可以實時看到。以下就以幾個小事例簡單介紹一下 PHP 與 Unity3D 通信的實現(xiàn)吧。(以下的環(huán)境只做參考,其他的版本也可以)
環(huán)境:1. Unity Hub 3.3.0-c1
2. Unity3D 2019
3. PHP 7.3
4. Workman 4.1
Workman 介紹workerman 是一款開源高性能 PHP 應用容器,他除了用于互聯(lián)網(wǎng)、即時通訊、APP 開發(fā)、硬件通訊、智能家居、物聯(lián)網(wǎng)等領域的開發(fā)外,也可以用于游戲服務器端的開發(fā),之前實現(xiàn)的一個五子棋多人聯(lián)機大戰(zhàn)雖然用的是 Swoole。但是實現(xiàn)思路類似,五子棋是給同房間內(nèi)的玩家更新棋子的坐標,而這里也是用于實時傳遞玩家的位置。
實現(xiàn)客戶端是 C#,就簡單先以和服務器端連接,發(fā)送,接收做例子,進一步就是方塊移動,坐標傳遞。
1. 簡單通訊
客戶端只是用面板畫出一個輸入框 (地址) 和顯示區(qū)域 (接收服務端發(fā)送的內(nèi)容),而服務器端是創(chuàng)建 TCP 服務,接收與發(fā)送。
(1). 客戶端連接
//連接 public void Connetion() { //清理text RecvText.text = ""; //socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //Connect string host = hostInput.text; int port = int.Parse(portInput.text); socket.Connect(host, port); clientText.text = "客戶端地址1 " + socket.LocalEndPoint.ToString(); //Recv socket.BeginReceive(readBuff, 0, BUFFER_SIZE, SocketFlags.None, ReceiveCb, null); }
(2). 客戶端接收
//接收回調(diào) private void ReceiveCb(IAsyncResult ar) { try { //count是接收數(shù)據(jù)的大小 int count = socket.EndReceive(ar); //數(shù)據(jù)處理 string str = System.Text.Encoding.UTF8.GetString(readBuff, 0, count); if (recvStr.Length > 300) recvStr = ""; recvStr += str + "\"; recvText.text = "接收的消息 " + recvStr; Debug.LogError("接收的消息 "+ recvStr); //繼續(xù)接收 socket.BeginReceive(readBuff, 0, BUFFER_SIZE, SocketFlags.None, ReceiveCb, null); } catch (Exception e) { recvText.text += "鏈接已斷開"; socket.Close(); } }
(3). 客戶端發(fā)送
//發(fā)送數(shù)據(jù) public void Send() { string str = textInput.text; byte[] bytes = System.Text.Encoding.Default.GetBytes("test:" + str); try { socket.Send(bytes); } catch { } }
2. workerman 安裝
(1). 新啟一個項目,進入該目錄,composer require workerman/workerman
(2). 創(chuàng)建一個 start.php
<?phpuse Workerman\\Worker;require_once __DIR__ . '/vendor/autoload.php';// #### 開啟TCP服務 ####$worker = new Worker('tcp://0.0.0.0:1234');// 4 processes//$worker->count = 4;// 客戶端連接回調(diào)$worker->onConnect = function ($connection) { echo "New Connection\";};// 接收客戶端消息$worker->onMessage = function ($Connection, $data) use ($worker) { // Send data to client echo json_encode($data) . "\"; //$ip = $connection->getRemoteIp(); foreach($worker->connections as $connection) { $connection->send($data); } //$connection->send("Hello $data \");};// 客戶端關閉回調(diào)$worker->onClose = function ($connection) { echo "Connection closed\";};Worker::runAll();?>
(3). 啟動,輸入 php start.php start,成功如下
(4). 打開客戶端的 6asyn 場景并運行,輸入 TCP 服務的地址和端口
(5). 點擊發(fā)送,就可以查看 workerman 接收到的信息。
2. 方塊移動案例
方塊移動服務器端幾乎不用修改,在連接成功后,將多個客戶端的坐標傳遞到服務器端,服務器處理后再給所有連接發(fā)送坐標,客戶端再將數(shù)據(jù)繪制到場景中。
(1). 前后端數(shù)據(jù)約定
POS 用于標識行為,比如 POS 為坐標移動,同理聊天可以用 IM,登陸用 LOGIN 做標識等 (攻擊)。第二個為客戶端連接標識,標識往后為坐標 X, Y, Z。
(2). 坐標的整合發(fā)送
服務器端在接收消息回調(diào)中,循環(huán)所有連接端,并給所有連接端發(fā)送從客戶端發(fā)送過來的坐標。
$worker->onMessage = function ($connection, $data) use ($worker) { // 循環(huán)連接 foreach($worker->connections as $connection) { // 發(fā)送坐標 $connection->Send($data); }};
客戶端維護一個名為 players 的字典,它將存放所有玩家的信息。msgList 是消息列表,接收到服務端的消息后,客戶端會將消息保存在 msgList 中,等待 Update 逐一進行處理。
using UnityEngine;using System;using System.Collections;using System.Collections.Generic;using System.Net;using System.Net.Sockets;using UnityEngine.UI;public class Walk : MonoBehaviour{ //socket和緩沖區(qū) Socket socket; const int BUFFER_SIZE = 1024; public byte[] readBuff = new byte[BUFFER_SIZE]; //玩家列表 Dictionary<string, GameObject> players = new Dictionary<string, GameObject>(); //消息列表 List<string> msgList = new List<string>(); //Player預設 public GameObject prefab; //自己的IP和端口 string id; //添加玩家 void AddPlayer(string id, Vector3 pos) { GameObject player = (GameObject)Instantiate(prefab, pos, Quaternion.identity); TextMesh textMesh = player.GetComponentInChildren<TextMesh>(); textMesh.text = id; players.Add(id, player); } //發(fā)送位置協(xié)議 void SendPos() { GameObject player = players[id]; Vector3 pos = player.transform.position; //組裝協(xié)議 string str = "POS "; str += id + " "; str += pos.x.ToString() + " "; str += pos.y.ToString() + " "; str += pos.z.ToString() + " "; byte[] bytes = System.Text.Encoding.Default.GetBytes(str); socket.Send(bytes); Debug.Log("發(fā)送 " + str); } //發(fā)送離開協(xié)議 void SendLeave() { //組裝協(xié)議 string str = "LEAVE "; str += id + " "; byte[] bytes = System.Text.Encoding.Default.GetBytes(str); socket.Send(bytes); Debug.Log("發(fā)送 " + str); } //移動 void Move() { if (id == "") return; GameObject player = players[id]; //上 if (Input.GetKey(KeyCode.UpArrow)) { player.transform.position += new Vector3(0, 0, 1); SendPos(); } //下 else if (Input.GetKey(KeyCode.DownArrow)) { player.transform.position += new Vector3(0, 0, -1); ; SendPos(); } //左 else if (Input.GetKey(KeyCode.LeftArrow)) { player.transform.position += new Vector3(-1, 0, 0); SendPos(); } //右 else if (Input.GetKey(KeyCode.RightArrow)) { player.transform.position += new Vector3(1, 0, 0); SendPos(); } } //離開 void OnDestory() { SendLeave(); } //開始 void Start() { Connect(); //請求其他玩家列表,略 //把自己放在一個隨機位置 UnityEngine.Random.seed = (int)DateTime.Now.Ticks; float x = 100 + UnityEngine.Random.Range(-30, 30); float y = 0; float z = 100 + UnityEngine.Random.Range(-30, 30); Vector3 pos = new Vector3(x, y, z); AddPlayer(id, pos); //同步 SendPos(); } //鏈接 void Connect() { //Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //Connect socket.Connect("192.168.1.199", 1234); id = socket.LocalEndPoint.ToString(); //Recv socket.BeginReceive(readBuff, 0, BUFFER_SIZE, SocketFlags.None, ReceiveCb, null); } //接收回調(diào) private void ReceiveCb(IAsyncResult ar) { try { int count = socket.EndReceive(ar); //數(shù)據(jù)處理 string str = System.Text.Encoding.UTF8.GetString(readBuff, 0, count); msgList.Add(str); //繼續(xù)接收 socket.BeginReceive(readBuff, 0, BUFFER_SIZE, SocketFlags.None, ReceiveCb, null); } catch (Exception e) { socket.Close(); } } void Update() { //處理消息列表 for (int i = 0; i < msgList.Count; i++) HandleMsg(); //移動 Move(); } //處理消息列表 void HandleMsg() { //獲取一條消息 if (msgList.Count <= 0) return; string str = msgList[0]; msgList.RemoveAt(0); //根據(jù)協(xié)議做不同的消息處理 string[] args = str.Split(' '); if (args[0] == "POS") { OnRecvPos(args[1], args[2], args[3], args[4]); } else if (args[0] == "LEAVE") { OnRecvLeave(args[1]); } } //處理更新位置的協(xié)議 public void OnRecvPos(string id, string xStr, string yStr, string zStr) { //不更新自己的位置 if (id == this.id) return; //解析協(xié)議 float x = float.Parse(xStr); float y = float.Parse(yStr); float z = float.Parse(zStr); Vector3 pos = new Vector3(x, y, z); //已經(jīng)初始化該玩家 if (players.ContainsKey(id)) { players[id].transform.position = pos; } //尚未初始化該玩家 else { AddPlayer(id, pos); } } //處理玩家離開的協(xié)議 public void OnRecvLeave(string id) { if (players.ContainsKey(id)) { Destroy(players[id]); players[id] = null; } }}
3. 演示效果
總結以前只是從入門的角度簡單介紹了一個二者通訊的方法,其實 workerman 可以基于 TCP 自定義協(xié)議,這樣就可以實現(xiàn)特別的封包解包了。后面如果有時間的話,可能會分享一下用 workerman 實現(xiàn)一個小成品的 3D 游戲。
以上就是關于pos機關聯(lián)app,unity3D 對接 workerman 實現(xiàn)聯(lián)機游戲的知識,后面我們會繼續(xù)為大家整理關于pos機關聯(lián)app的知識,希望能夠幫助到大家!