【微信小游戏】微信对战小游戏知识储备

一、前提

在微信小游戏异常火爆的前提下,开发个小游戏才是正事,而不是玩个小游戏才是正事!

废话不多说,步入今天的正题,在慢慢成熟起来的小游戏生态中我们的小游戏如果只是单机+排行的组合,难免显得小游戏单调,乏味今天我们就来尝试下联网操作。

二、准备

工具:cocos creator

版本:v1.9.1 

语言:JavaScript,开源库onfire.js

介绍:我们主要通过WebSocket来实现客户端跟服务器的交互,服务器用Node.js,技术自己选,按照自己擅长的语言。

三、实战

客户端

1、新建场景Login,场景中包含三个按钮,两个文本。按钮分别是建立连接、发送数据,关闭连接。文本分别是显示发送数据和接收数据。绑定事件,绑定脚本就不再多说了。

Login.js代码如下:

cc.Class({
    extends: cc.Component,

    properties: {
        lbl_sendMsg: {
            default: null,        
            type: cc.Label,
            tooltip: "发送的数据"
        },
        lbl_responseMsg: {
            default: null,        
            type: cc.Label,
            tooltip: "接收的数据"
        },
  
    },

    start () {
        this.netControl = require('NetControl');
    },

    // 打开连接
    onOpenSocket(){
        console.log("连接完成");
    },

    // 接收服务器返回的数据
    onMessage(obj){
        console.log("It's HelloWorld onMessage----->"+ obj.data );
        this.lbl_responseMsg.string = obj.data;
        var responseData = JSON.parse(obj.data);
        console.log("消息协议号:"+responseData.MsgId);
    },

    // 关闭 WebSocket 连接
    onCloseSocket(){
        console.log("关闭连接" + new Date().getTime());
    },

    // 销毁事件注册
    onDestroy(){
        onfire.un(this.openSocket);
        onfire.un(this.msssageFire);
        onfire.un(this.closeSocket);
    },

    btnSocketOpen(){
        console.log("开始连接");
        this.netControl.connect();
        this.openSocket = onfire.on("onopen",this.onOpenSocket.bind(this));
    },

    btnSocketSend(){
        // console.log("socket send!!! ");
        this.msssageFire = onfire.on("onmessage",this.onMessage.bind(this));
        var jsonTmp = "{ \"MsgId\": 1001, \"Name\": \" + \"知心购物\" + \", \"QQGroup\": \"" + 418177552 + "\" }";
        this.netControl.send(jsonTmp);

        //发送的数据
        this.lbl_sendMsg.string = jsonTmp;
    },

    btnSocketClose(){
        this.closeSocket = onfire.on("onclose",this.onCloseSocket.bind(this));
        //关闭:包含主动关闭连接和服务器关闭连接
        this.netControl.close();
    },


    // update (dt) {},
});

2、NetConfig.js代码如下

/**
 * 当前的网络配置
 */
module.exports={
    host:"ws://localhost",
    port:3000
};

3、NetControl.js代码如下

//定义全局的变量
window.onfire = require("onfire");           //处理事件的类库
var netConfig = require('NetConfig');
var NetControl = {
    _sock:{},  //当前的webSocket的对象
    connect: function () {
        if(this._sock.readyState !==1){
            //当前接口没有打开
            //重新连接
            this._sock = new WebSocket(netConfig.host+":"+netConfig.port);
            this._sock.onopen = this._onOpen.bind(this);
            this._sock.onclose = this._onClose.bind(this);
            this._sock.onmessage = this._onMessage.bind(this);
        }
        return this;
    },

    // 关闭连接
    close: function(){
        if (this._sock.readyState ===1) {
            this._sock.close();
        }
    },

    _onOpen:function(){
        onfire.fire("onopen");
    },

    _onClose:function(err){
        onfire.fire("onclose",err);
    },

    _onMessage:function(obj){

        onfire.fire("onmessage",obj);
    },

    send:function(msg){
        this._sock.send(msg);
        console.log("send msg"+msg);
    },

};

module.exports=NetControl;

4、onfire.js代码如下

/**
 * Created by Administrator on 2018/4/17 0017.
 */
/**
 Copyright (c) 2016 hustcc http://www.atool.org/
 License: MIT
 https://github.com/hustcc/onfire.js
 **/
/* jshint expr: true */
!function (root, factory) {
    if (typeof module === 'object' && module.exports)
        module.exports = factory();
    else
        root.onfire = factory();
}(typeof window !== 'undefined' ? window : this, function () {
    var __onfireEvents = {},
        __cnt = 0, // evnet counter
        string_str = 'string',
        function_str = 'function',
        hasOwnKey = Function.call.bind(Object.hasOwnProperty),
        slice = Function.call.bind(Array.prototype.slice);

    function _bind(eventName, callback, is_one, context) {
        if (typeof eventName !== string_str || typeof callback !== function_str) {
            throw new Error('args: '+string_str+', '+function_str+'');
        }
        if (! hasOwnKey(__onfireEvents, eventName)) {
            __onfireEvents[eventName] = {};
        }
        __onfireEvents[eventName][++__cnt] = [callback, is_one, context];

        return [eventName, __cnt];
    }
    function _each(obj, callback) {
        for (var key in obj) {
            if (hasOwnKey(obj, key)) callback(key, obj[key]);
        }
    }
    /**
     *  onfire.on( event, func, context ) -> Object
     *  - event (String): The event name to subscribe / bind to
     *  - func (Function): The function to call when a new event is published / triggered
     *  Bind / subscribe the event name, and the callback function when event is triggered, will return an event Object
     **/
    function on(eventName, callback, context) {
        return _bind(eventName, callback, 0, context);
    }
    /**
     *  onfire.one( event, func, context ) -> Object
     *  - event (String): The event name to subscribe / bind to
     *  - func (Function): The function to call when a new event is published / triggered
     *  Bind / subscribe the event name, and the callback function when event is triggered only once(can be triggered for one time), will return an event Object
     **/
    function one(eventName, callback, context) {
        return _bind(eventName, callback, 1, context);
    }
    function _fire_func(eventName, args) {
        if (hasOwnKey(__onfireEvents, eventName)) {
            _each(__onfireEvents[eventName], function(key, item) {
                item[0].apply(item[2], args); // do the function
                if (item[1]) delete __onfireEvents[eventName][key]; // when is one, delete it after triggle
            });
        }
    }
    /**
     *  onfire.fire( event[, data1 [,data2] ... ] )
     *  - event (String): The event name to publish
     *  - data...: The data to pass to subscribers / callbacks
     *  Async Publishes / fires the the event, passing the data to it's subscribers / callbacks
     **/
    function fire(eventName) {
        // fire events
        var args = slice(arguments, 1);
        setTimeout(function () {
            _fire_func(eventName, args);
        });
    }
    /**
     *  onfire.fireSync( event[, data1 [,data2] ... ] )
     *  - event (String): The event name to publish
     *  - data...: The data to pass to subscribers / callbacks
     *  Sync Publishes / fires the the event, passing the data to it's subscribers / callbacks
     **/
    function fireSync(eventName) {
        _fire_func(eventName, slice(arguments, 1));
    }
    /**
     * onfire.un( event ) -> Boolean
     *  - event (String / Object): The message to publish
     * When passed a event Object, removes a specific subscription.
     * When passed event name String, removes all subscriptions for that event name(hierarchy)
     *
     * Unsubscribe / unbind an event or event object.
     *
     * Examples
     *
     *  // Example 1 - unsubscribing with a event object
     *  var event_object = onfire.on('my_event', myFunc);
     *  onfire.un(event_object);
     *
     *  // Example 2 - unsubscribing with a event name string
     *  onfire.un('my_event');
     **/
    function un(event) {
        var eventName, key, r = false, type = typeof event;
        if (type === string_str) {
            // cancel the event name if exist
            if (hasOwnKey(__onfireEvents, event)) {
                delete __onfireEvents[event];
                return true;
            }
            return false;
        }
        else if (type === 'object') {
            eventName = event[0];
            key = event[1];
            if (hasOwnKey(__onfireEvents, eventName) && hasOwnKey(__onfireEvents[eventName], key)) {
                delete __onfireEvents[eventName][key];
                return true;
            }
            // can not find this event, return false
            return false;
        }
        else if (type === function_str) {
            _each(__onfireEvents, function(key_1, item_1) {
                _each(item_1, function(key_2, item_2) {
                    if (item_2[0] === event) {
                        delete __onfireEvents[key_1][key_2];
                        r = true;
                    }
                });
            });
            return r;
        }
        return true;
    }
    /**
     *  onfire.clear()
     *  Clears all subscriptions
     **/
    function clear() {
        __onfireEvents = {};
    }
    return {
        on: on,
        one: one,
        un: un,
        fire: fire,
        fireSync: fireSync,
        clear: clear
    };
});

服务器

1、app.js代码如下

var WebSocketServer = require('ws').Server,
wss = new WebSocketServer({ port: 3000 });
wss.on('connection', function (ws) {
    console.log('client connected');
    ws.on('message', function (message) {
        console.log("接受消息");
        console.log(`[SERVER] Received: ${message}`);
        // ws.send(`ECHO: ${message}`, (err) => {
        ws.send(`{"MsgId":1001, "Name":"微信搜索知心购物小程序"}`, (err) => {
            if (err) {
                console.log(`[SERVER] error: ${err}`);
            }
        });
    });
});

2、package.json

"dependencies": {
    "websocket": "^1.0.26",
    "ws": "1.1.1"
}

3、通过npm install 安装依赖,通过 node app.js 开启服务器

四、效果

1、建立连接:

服务器:服务器执行打印,输出client connected!

客户端:客户端连接成功后,执行回调this.openSocket注册的函数,进打印操作。

2、发送数据:

服务器:服务器打印收到的数据。

客户端:客户端显示服务器返回的数据。

3、关闭连接:

客户端:点击关闭连接后,执行我们的回调,打印关闭连接; 此时如果再次点击发送数据,则会报 not open 错误

至此,整个的网络流程已完成。

五、思考

1、WebSocket最基本的网络连接,网络请求,网络关闭。

2、应用到游戏中远不止这些可以满足,至少有心跳机制断线重连机制

3、玩家之间如何随机匹配对战,好友之间如何1V1匹配对战。

4、WebSocket中不使用Json数据,而使用Protobuf数据。

5、更多问题,欢迎加群讨论学习,QQ群:418177552

posted @ 2018-06-14 20:42  柳轩涤俗  阅读(711)  评论(0编辑  收藏  举报