用Unity同时开发【微信小游戏】【安卓】【IOS】游戏#6.2 WebSocket通信

【系列文章目录】



前言

本篇来实现WebSocket通信


一、导入UnityWebSocket插件

要在Unity中使用WebSocket我查看了官方文档
我一直觉得Unity是一个把所有东西都为开发者准备好的引擎
然而这次我错了,它摆烂了,Unity并没有为我们提供WebScoket的内容
Unity中没有直接能用的WebSocketAPI

所以只好使用插件了,这里我使用的是UnityWebSocket
网上也有很多教程用的是BestHttp
我也心动过,但是它好贵,坐等打折好了

言归正传,首先我们先在UnityWebSocketRelease页面下载插件
然后将它导入到我们的项目中
这个插件提供的Demo来供我们参考

二、在TS中使用

这个插件的使用是在C#中的,我们需要封装一层来让它能被TS使用
这里我定义了一个WebSocketClient

public class WebSocketClient
{
    
    
    private string connectAddress;
    protected IWebSocket _socket;

    public Action<string> JSOnOpen;
    public Action<string> JSOnMessage;
    public Action<CloseStatusCode, string> JSOnClose;
    public Action<string> JSOnError;

    public ushort State
    {
    
    
        get
        {
    
    
            if (_socket == null)
            {
    
    
                return 0;
            }
            else
            {
    
    
                return (ushort)(_socket.ReadyState + 1);
            }
        }
    }
    //连接
    public void Connect(string address)
    {
    
    
        connectAddress = address;
        if (_socket == null)
        {
    
    
            _socket = new WebSocket(address);
            _socket.OnOpen += Socket_OnOpen;
            _socket.OnMessage += Socket_OnMessage;
            _socket.OnClose += Socket_OnClose;
            _socket.OnError += Socket_OnError;
            _socket.ConnectAsync();
        }
    }

    //发送string
    public void SendStr(string str)
    {
    
    
        try
        {
    
    
            _socket.SendAsync(str);
        }
        catch (Exception e)
        {
    
    
            Debug.LogError(e);
        }
    }

    //发送byte
    public void SendByte(string data)
    {
    
    
        try
        {
    
    
            var bytes = System.Text.Encoding.UTF8.GetBytes(data);
            _socket.SendAsync(bytes);
        }
        catch (Exception e)
        {
    
    
            Debug.LogError(e);
        }
    }

    //关闭
    public void Close()
    {
    
    
        _socket.CloseAsync();
    }

    private void Socket_OnOpen(object sender, OpenEventArgs e)
    {
    
    
        JSOnOpen?.Invoke(connectAddress);
    }

    private void Socket_OnMessage(object sender, MessageEventArgs e)
    {
    
    
        try
        {
    
    
            if (e.IsBinary)
            {
    
    
                JSOnMessage?.Invoke(e.Data);
                //Debug.Log("WebSocket:" + string.Format("Receive Bytes ({1}): {0}", e.Data, e.RawData.Length));
            }
            else if (e.IsText)
            {
    
    
                JSOnMessage?.Invoke(e.Data);
                //Debug.Log("WebSocket:" + string.Format("Receive: {0}", e.Data));
            }
        }
        catch (Exception err)
        {
    
    
            Debug.LogError(err);
        }
    }

    private void Socket_OnClose(object sender, CloseEventArgs e)
    {
    
    
        JSOnClose?.Invoke(e.StatusCode, e.Reason);
        //Debug.Log("WebSocket:" + string.Format("Closed: StatusCode: {0}, Reason: {1}", e.StatusCode, e.Reason));
    }

    private void Socket_OnError(object sender, ErrorEventArgs e)
    {
    
    
        JSOnError?.Invoke(e.Message);
        //Debug.Log("WebSocket:" + string.Format("Error: {0}", e.Message));
    }
}

需要注意的是,我们需要声明使用的Action类型,才能正常使用

_jsEnv.UsingAction<string>();
_jsEnv.UsingAction<CloseStatusCode, string>();

别忘了为它生成一下胶水代码

接下来我们来在TS中继续工作

import {
    
     UnityWebSocket, WebSocketClient } from "csharp";
import {
    
     TSLog } from "../../CustomLog/TSLog";
import {
    
     IBaseMessageData, SocketMessageType, WebSocketState } from "../NetDefine";

//C#中我增加了一个Null类型
export enum WebSocketState {
    
    
    Null = 0,
    Connecting = 1,
    Open = 2,
    Closing = 3,
    Closed = 4
}

export interface IWebSocket {
    
    
    /**
     * 连接WebSocket
     * @param address 连接地址
     */
    Connect(address: string): void;
    /**
     * 发送数据
     * @param data Json数据
     * @param droppable 是否可抛弃,true在未连接状态下会被抛弃,false会等待连接后发送数据
     */
    Send(data: IBaseMessageData, droppable?: boolean): void;
    /**
     * 关闭连接
     */
    Close(): void;
    /**
     * 监听消息
     * @param state 监听的类型
     * @param fun 回调函数
     */
    ListenMessage(state: SocketMessageType, fun: Function): void;
    /**
     * 移除监听
     * @param state 监听的类型
     * @param fun 回调函数
     */
    RemoveListen(state: SocketMessageType, fun: Function): void;
}

export class JSWebSocket extends WebSocketClient implements IWebSocket {
    
    
    private _waitSendCash: Array<string> = new Array<string>();
    private _messageHandler: Map<SocketMessageType, Array<Function>> = new Map<SocketMessageType, Array<Function>>();

    constructor() {
    
    
        super();
        this.JSOnOpen = this.Socket_OnOpen.bind(this);
        this.JSOnMessage = this.Socket_OnMessage.bind(this);
        this.JSOnClose = this.Socket_OnClose.bind(this);
        this.JSOnError = this.Socket_OnError.bind(this);
    }

    RemoveListen(state: SocketMessageType, fun: Function): void {
    
    
        if (this._messageHandler.has(state)) {
    
    
            let array = this._messageHandler.get(state);
            let newArray = new Array<Function>();
            array.forEach(v => {
    
    
                if (v != fun) {
    
    
                    newArray.push(v);
                }
            });
            this._messageHandler.set(state, newArray);
            array.length = 0;
            array = null;
        }
    }

    ListenMessage(state: SocketMessageType, fun: Function): void {
    
    
        if (!this._messageHandler.has(state)) {
    
    
            this._messageHandler.set(state, new Array<Function>());
        }
        this._messageHandler.get(state).push(fun);
    }

    public Send(data: IBaseMessageData, droppable: boolean = true): void {
    
    
        let jstr = JSON.stringify(data);
        TSLog.Log("stringify:" + jstr)
        //判断连接
        if (this.State != WebSocketState.Open) {
    
    
            //未连接且非可抛弃数据加入待发送池
            if (!droppable) {
    
    
                this._waitSendCash.push(jstr);
            }
        }
        else {
    
    
            //已连接直接发送
            this.SendByte(jstr);
        }
    }

    private Socket_OnOpen(address: string) {
    
    
        TSLog.Log("OnConnect---->" + address);
        if (this._waitSendCash.length > 0) {
    
    
            TSLog.Log("sendcash")
            this._waitSendCash.forEach(jstr => {
    
    
                this.SendByte(jstr);
            });
        }

        this._waitSendCash.length = 0;
    }

    private Socket_OnMessage(jstr: string) {
    
    
        TSLog.Log("OnMessage---->" + jstr);
        if (jstr == "echo.websocket.events sponsored by Lob.com") return;
        let jsdata: IBaseMessageData = JSON.parse(jstr) as IBaseMessageData;

        //解析Json根据类型分发Action
        if (this._messageHandler.has(jsdata.type)) {
    
    
            let handlers = this._messageHandler.get(jsdata.type);
            for (let i = 0; i < handlers.length; i++) {
    
    
                if (handlers[i] != null) {
    
    
                    try {
    
    
                        handlers[i](jsdata);
                    }
                    catch (e) {
    
    
                        TSLog.Error(e);
                    }

                }
            }
        }
    }

    private Socket_OnClose(errCode: UnityWebSocket.CloseStatusCode, reason: string) {
    
    
        TSLog.Log("OnClose---->" + reason);
    }

    private Socket_OnError(message: string) {
    
    
        TSLog.Log("Connect---->" + message);
    }
}

这样我们在TS中就可以使用WebSocket了

扫描二维码关注公众号,回复: 14346165 查看本文章

这里我定义了一个全局的mainSocket

export const mainSocket:IWebSocket = new JSWebSocket();

以及一些配置和数据接口

export let mainSocketAddress: string = "wss://echo.websocket.events";
export let httpAddress: string = "";

export interface IBaseMessageData {
    
    
    type: SocketMessageType;
}

export enum SocketMessageType {
    
    
    Null = 0,
    Test = 1
}

export enum WebSocketState {
    
    
    Null = 0,
    Connecting = 1,
    Open = 2,
    Closing = 3,
    Closed = 4
}

在游戏中我们会有很多个功能使用到Socket
我不想在Socket中去为每个功能写处理
而是在每个功能的Hanlder中去处理
这里我写了一个TestHanlder来测试

export interface Req_Test_MessageData extends IBaseMessageData {
    
    
    data: {
    
    
        str: number,
        str2: number
    }
}

export interface Resp_Test_MessageData extends IBaseMessageData {
    
    
    data: {
    
    
        str: number,
        str2: number
    }
}

export interface ITestHandler{
    
    
    Connect(): ITestHandler;
    ReqTest(n1: number, n2: number): ITestHandler;
    Close(time: number): ITestHandler;
}

export class TestHanler extends Singleton<TestHanler> implements ITestHandler {
    
    
    constructor() {
    
    
        super();
        mainSocket.ListenMessage(SocketMessageType.Test, this.OnRespTest.bind(this))
    }

    public OnRespTest(resp: Resp_Test_MessageData): void {
    
    
        TSLog.Log(resp.data.str)
        TSLog.Log(resp.data.str2)
    }

    public ReqTest(n1: number, n2: number): ITestHandler {
    
    
        let req: Req_Test_MessageData = {
    
    
            type: SocketMessageType.Test,
            data: {
    
    
                str: n1,
                str2: n2
            }
        }

        mainSocket.Send(req, false);

        return this;
    }

    public Connect(): ITestHandler {
    
    
        mainSocket.Connect(mainSocketAddress);
        return this;
    }

    public Close(time: number = 5000): ITestHandler {
    
    
        setTimeout(() => {
    
    
            mainSocket.Close();
        }, time)
        return this;
    }
}

然后我们就可以调用了

new TestHandler()
    .Connect()  //连接服务器
    .ReqTest(5, 1)  //请求数据
    .Close(5000);   //关闭连接

嗯,挺简洁的,测试了也没有什么问题
测试结果
具体内容还需要与服务端联调,目前以及实现了功能


猜你喜欢

转载自blog.csdn.net/ruanlinxi1994/article/details/125508685