unity python服务器_Unity如何连接服务器: 一个简单的例子

Unity3D本身是用来做客户端的通用游戏引擎, 要建立网络连接的话, 其实需要使用的是C#本身的网络和线程模块, 即System.Net.Sockets & System.Threading. 本文中我做了一个简单的例子, 适合那些需要做Unity客户端连接服务器功能的人入门.

整体项目

客户端: 我做的项目主要是一个简单的Demo, 画面上只有三个按钮和两个输入框, 通过点击按钮可以实现相应的操作.

服务端: 服务端是一个Python写的服务器. 这个部分不是我本文的重点, 大家可以参考别的网上文章, 了解如何写一个C++, Python或者Java服务器, 无论什么语言写的服务器都是可以与Unity进行交互的.

Unity Network Demo

login点击后, console上显示了发出的消息

server显示成功登陆

下载项目后, 使用Unity导入, 可以看到Scripts文件夹中有六个脚本, 其中NetworkCore和UIManager是主要的脚本, Json开头的脚本不是重点, 他们只是Json编码解码相关的一个库(文中我是直接使用的https://github.com/gering/Tiny-JSON这个老外写的纯C#版本Json Parser), Json的编码和解析也不是本文重点, 只要找到一个库能用即可.

后续补充: Json的工具库现在推荐使用Newtonsoft出品的json.NET. 下载地址https://github.com/JamesNK/Newtonsoft.Json/releases, 在Unity2018.1中, 请使用其中的Bin\net20\Newtonsoft.Json.dll这个大小513KB的DLL(此处我也在微云存了一个供大家快速下载https://share.weiyun.com/5pky2k3), 由于Unity2018用的还是.NET2.0版本, 因此要用老的.

脚本一览

学习步骤

下载客户端和服务端, 运行起来. 之后主要学习NetworkCore.cs和UIManager.cs这两个脚本的内容(两个脚本并不复杂), 最关键的部分是如何建立连接, 建立后台线程, 发送和接收数据, 以及Json相关的字典操作.

脚本1: NetworkCore.cs

using System;

using System.Collections.Generic;

using System.Net.Sockets;

using System.Text;

using System.Threading;

using UnityEngine;

using Tiny;

public class NetworkCore : MonoBehaviour {

public string serverAddress = "127.0.0.1";

public int serverPort = 5000;

public string username = "chen";

public string password = "123";

private TcpClient _client;

private NetworkStream _stream; // C#中采用NetworkStream的方式, 可以类比于python网络编程中的socket

private Thread _thread;

private byte[] _buffer = new byte[1024]; // 接收消息的buffer

private string receiveMsg = "";

private bool isConnected = false;

void Start() {

}

public void OnApplicationQuit() {

Dictionary dict = new Dictionary()

{

{"code", "exit"}

};

SendData(Encode(dict)); // 退出的时候先发一个退出的信号给服务器, 使得连接被正确关闭

Debug.Log("exit sent!");

CloseConnection ();

}

// --------------------public--------------------

public void Login() {

SetupConnection();

Dictionary dict = new Dictionary()

{

{"code", "login"},

{"username", username},

{"password", password}

};

SendData(Encode(dict));

Debug.Log("start!");

}

public void SendGameData(int score, int health) {

Dictionary dict = new Dictionary()

{

{"code", "gds"},

{"score", score.ToString()},

{"health", health.ToString()}

};

SendData(Encode(dict));

}

// -----------------------private---------------------

private void SetupConnection() {

try {

_thread = new Thread(ReceiveData); // 传入函数ReceiveData作为thread的任务

_thread.IsBackground = true;

_client = new TcpClient(serverAddress, serverPort);

_stream = _client.GetStream();

_thread.Start(); // background thread starts working while loop

isConnected = true;

} catch (Exception e) {

Debug.Log (e.ToString());

CloseConnection ();

}

}

private void ReceiveData() { // 这个函数被后台线程执行, 不断地在while循环中跑着

Debug.Log ("Entered ReceiveData function...");

if (!isConnected) // stop the thread

return;

int numberOfBytesRead = 0;

while (isConnected && _stream.CanRead) {

try {

numberOfBytesRead = _stream.Read(_buffer, 0, _buffer.Length);

receiveMsg = Encoding.ASCII.GetString(_buffer, 0, numberOfBytesRead);

_stream.Flush();

Debug.Log(receiveMsg);

receiveMsg = "";

} catch (Exception e) {

Debug.Log (e.ToString ());

CloseConnection ();

}

}

}

private void SendData(String msgToSend)

{

byte[] bytesToSend = Encoding.ASCII.GetBytes(msgToSend);

if (_stream.CanWrite)

{

_stream.Write(bytesToSend, 0, bytesToSend.Length);

}

}

private void CloseConnection() {

if (isConnected) {

_thread.Interrupt (); // 这个其实是多余的, 因为isConnected = false后, 线程while条件为假自动停止

_stream.Close ();

_client.Close ();

isConnected = false;

receiveMsg = "";

}

}

// ---------------------util----------------------

// encode dict to to json and wrap it with \r\n as delimiter

string Encode(Dictionary dict)

{

string json = Json.Encode(dict);

string header = "\r\n" + json.Length.ToString() + "\r\n";

string result = header + json;

Debug.Log("encode result:" + result);

return result;

}

// decode data, 注意要解决粘包的问题, 这个程序写法同GameLobby中的相应模块一模一样

// 参考 https://github.com/imcheney/GameLobby/blob/master/server/util.py

Dictionary Decode(string raw)

{

string payload_str = "";

string raw_leftover = raw;

if (raw.Substring(0, 2).Equals("\r\n"))

{

int index = raw.IndexOf("\r\n", 2);

int payload_length = int.Parse(raw.Substring(2, index - 2 + 1)); // 注意, C#'s substring takes start and length as args

if (raw.Length >= index + 2 + payload_length)

{

payload_str = raw.Substring(index + 2, payload_length);

raw_leftover = raw.Substring(index + 2 + payload_length);

}

}

return Json.Decode>(payload_str);

}

}

脚本2: UIManager.cs

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

using UnityEngine.UI; //using 关键字用于在程序中包含命名空间。一个程序可以包含多个 using 语句。

public class UIManager : MonoBehaviour {

public InputField scoreInputField;

public InputField healthInputField;

NetworkCore networkCore;

// Use this for initialization

void Start () {

networkCore = GetComponent();

}

// Update is called once per frame

void Update () {

}

public void OnLoginButton() {

networkCore.Login();

}

public void OnSendButton() {

int score = int.Parse(scoreInputField.text);

int health = int.Parse(healthInputField.text);

networkCore.SendGameData(score, health);

}

public void OnQuitButton()

{

int score = int.Parse(scoreInputField.text);

int health = int.Parse(healthInputField.text);

networkCore.SendGameData(score, health);

Application.Quit();

}

}

后续持续开发优化建议

你可能感兴趣的:(unity,python服务器)