单场景+update分发消息 网络游戏客户端架构设计模式可靠性证明

博客围绕Cocos开发展开,介绍网络模块将收到的消息扔到消息队列,在组件update里抛出以避免切换场景时消息未处理;UI模块通过addChild添加prefab到场景树,保证网络消息处理。还展示可视化场景树,并证明依靠update分发网络消息十分可靠,不会漏消息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1)网络模块

在websocket收到消息,不是直接 evt.getInstance().emit("msg"+ msgId, msgData);  这样发不出来,而是扔到socket组件的消息队列msg_queue,在一个组件的update里面进行抛出消息。

解决的问题: 避免切换场景时,觉得:服务器消息发过来了,但是我在切换场景,没有处理。

2)ui模块

切换场景其实不再是切换场景了,而是仅仅 addChild 一个简单的prefab,通过addChild的方式添加到场景树里面,由于单场景,及其cocos是单线程的,addChild时,依次执行prefab的onLoad  start,  是不会出现 socket组件来update分发网络消息的。因此,网络消息不可能出现没有处理的情况。

3)直观的可视化场景树

由于可以有工具方便查看场景节点树所有节点,一切变得如此直观!!!

4)可靠性证明

当前场景树:

Canvas-->test_ctrl组件
      socket-->test_socket组件

待动态加载节点: test_prefab-->test_prefab组件

test.ctrl.js

var evt = require("evt");

cc.Class({
    extends: cc.Component,

    properties: {
        test_prefab: cc.Prefab
    },

    // LIFE-CYCLE CALLBACKS:

    onLoad () {
        this.init_event_manager();
        cc.log("testctrl onLoad");
    },

    init_event_manager: function(){

        evt.getInstance().on("change-game", function (data, then) {
            var p = cc.instantiate(then.test_prefab).getComponent("testprefab");
            then.node.addChild(p.node);
        }, this);
    },

    onDestroy: function(){
        evt.getInstance().removeByNode(this);
    },

    start () {

        cc.log("testctrl start");
    },
    
    update: function (dt) {
        if(this.flag){
            return;
        }

        if(!this.flag){
            this.flag = true;
        }
        cc.log("testctrl update");
    }
});

test_socket.js

var evt = require("evt");

cc.Class({
    extends: cc.Component,

    properties: {

    },

    // LIFE-CYCLE CALLBACKS:

    onLoad () {

        cc.log("testsocket onLoad");

        this.msg_queue = [
            {
                cmdId: "change-game",
            },
            {
                cmdId: "1",
            },

            {
                cmdId: "2",
            },
            {
                cmdId: "3",
            },
            {
                cmdId: "4",
            },
            {
                cmdId: "5",
            },
        ];
    },

    start () {
        cc.log("testsocket start");
    },

    update (dt) {
        if(this.msg_queue.length > 0){
            var msg = this.msg_queue[i];
            cc.log(msg.cmdId);
            evt.getInstance().emit(msg.cmdId, {});
            this.msg_queue.splice(0, 1);
        }
    },
});

3.test_prefab.js

cc.Class({
    extends: cc.Component,

    properties: {

    },

    // LIFE-CYCLE CALLBACKS:

    onLoad () {
        cc.log("testprefab onLoad");
    },

    start () {
        cc.log("testprefab Start");
    },

    update: function (dt) {
        if(this.flag){
            return;
        }

        if(!this.flag){
            this.flag = true;
        }
        cc.log("testprefab update");
    }
});

4.evt.js

var evt = cc.Class({

    ctor: function(){
        this._list = {};
    },

    statics:{
        _instance: null,

    },

    on: function (key, func, node) {
        if(!this._list[key]){
            this._list[key] = [];
        }

        var d = {};
        d.func = func;
        d.node = node;
        this._list[key].push(d);
    },

    removeByNode: function (node) {
        for(var key in this._list){
            var d = this._list[key];
            for(var i = 0; i < d.length; i++){
                if(d[i].node == node){
                    d.splice(i, 1);
                    i--;
                }
            }
        }
    },

    emit: function (key, arg) {
        if(this._list[key]){
            var dataList = this._list[key];
            for(var i = 0; i < dataList.length; i++){
                var d = dataList[i];
                if(d.node instanceof cc.Component){
                    if(cc.isValid(d.node)){
                        d.func(arg, d.node);
                    }else{
                        d.func(arg, d.node);
                    }
                }
            }
        }else{
            // cc.log("emit event key=[", key, "] not found");
        }
    }
});

evt._instance = null;

evt.getInstance = function(){
    if(!evt._instance){
        evt._instance = new evt();
    }
    return evt._instance;
};

module.exports = evt;

先说打印结果:

ctrl onLoad  socket onLoad -->

ctrl start  socket start-->

ctrl update  socket update-->

ctrl update  socket update -->

ctrl update  socket update -->

prefab1 onLoad    prefab2 onLoad          prefab1 start  prefab2 start (模拟这一帧率一下子加载了多个资源)-->

ctrl update socket update prefab1 update prefab2 update-->

ctrl update socket update prefab1 update prefab2 update-->

ctrl update socket update prefab1 update prefab2 update-->

蓝色部分表示:没有新节点情况下,update无穷无尽在更新。

结论:

1)加载场景树的话,依次执行

onLoad1 onLoad2 ...-->start1 start2...-->update1 update2...;

2)当出现有新的节点(假如是3 4 5  三个新节点)加入时,就执行这三个节点的

onLoad3 onLoad4  onLoad 5...  和 start3 start4 start 5;

3)update1 update2 update 3 update 4 update5... 这样无穷无尽的执行下去; 

因此,依靠update来依次分发网络消息,是十分可靠的做法。不可能会漏消息,因为一旦出现新节点,由于cocos 单线程的特性:那么等待新节点的onLoad、start执行完,才会从msg_queue分发下一个消息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值