CocosCreator-系统事件是怎么产生及触发的?

本文深入探讨CocosCreator中系统事件的传递路径,从浏览器的事件到达引擎,再到游戏节点的详细过程。分析了关键文件如CCGame.js、CCInputManager.js和CCEventManager.js的角色,讲解了事件的注册、分发和触发机制,同时介绍了事件监听器的排序算法。
摘要由CSDN通过智能技术生成

写在前面

开个头

依旧是面试官系列。
面试官:怎么实现节点的点击事件监听?
我:用this.node.on()实现
面试官:那他是怎么产生的呢?
我:…
本文以触摸事件(cc.Node.EventType.TOUCH_START)作为主线,简单分析一下Cocos引擎中的事件是如何从浏览器传递到Node的事件中的。
你可能还想要问,那事件是怎么从浏览器产生的呢?我:…

一些话

我是个即将毕业的应届生,受水平所限,难免有些地方写的不是很好,但不动手的话,也不会进步,就指望大家多多包涵啦,有错误的地方也劳烦大家伙们指正。
代码中,注释以 //_ 开头的是引用了官方注释。
为提高可读性,代码中会省略部分类似或与主题不是特别相关的代码。

环境

Cocos Creator 2.4
Chrome 88

概要

模块作用

事件监听机制应该是所有游戏都必不可少的内容。不管是按钮的点击还是物体的拖动,都少不了事件的监听与分发。
主要的功能还是通过节点的on/once函数,对系统事件(如触摸、点击)进行监听,随后触发对应的游戏逻辑。同时,也支持用户发射/监听自定义的事件,这方面可以看一下官方文档监听和发射事件

涉及文件

本次涉及文件较多。有点难截图,直接文字描述吧。

类名 路径 作用
CCNode cocos2d\core\CCNode.js 注册、接收事件
CCGame cocos2d\core\CCGame.js 调用初始化、注册浏览器事件、兼容处理
CCInputManager cocos2d\core\platform\CCInputManager.js 初始化、注册浏览器事件
CCEventManager cocos2d\core\event-manager\CCEventManager.js 分发系统事件

其中,CCGame和CCInputManager都有涉及注册事件,但他们负责的是不同的部分。

源码解析

事件是怎么(从浏览器)到达引擎的?

想知道这个问题,必须要了解引擎和浏览器的交互是从何而起。
上代码。

CCGame.js

// 初始化事件系统
_initEvents: function () {
   
  var win = window, hiddenPropName;

  //_ register system events
  // 注册系统事件,这里调用了CCInputManager的方法
  if (this.config.registerSystemEvent)
    _cc.inputManager.registerSystemEvent(this.canvas);

  // document.hidden表示页面隐藏,后面的if用于处理浏览器兼容
  if (typeof document.hidden !== 'undefined') {
   
    hiddenPropName = "hidden";
  } else if (typeof document.mozHidden !== 'undefined') {
   
    hiddenPropName = "mozHidden";
  } else if (typeof document.msHidden !== 'undefined') {
   
    hiddenPropName = "msHidden";
  } else if (typeof document.webkitHidden !== 'undefined') {
   
    hiddenPropName = "webkitHidden";
  }

  // 当前页面是否隐藏
  var hidden = false;

  // 页面隐藏时的回调,并发射game.EVENT_HIDE事件
  function onHidden () {
   
    if (!hidden) {
   
      hidden = true;
      game.emit(game.EVENT_HIDE);
    }
  }
  //_ In order to adapt the most of platforms the onshow API.
  // 为了适配大部分平台的onshow API。应该是指传参的部分...
  // 页面可视时的回调,并发射game.EVENT_SHOW事件
  function onShown (arg0, arg1, arg2, arg3, arg4) {
   
    if (hidden) {
   
      hidden = false;
      game.emit(game.EVENT_SHOW, arg0, arg1, arg2, arg3, arg4);
    }
  }

  // 如果浏览器支持隐藏属性,则注册页面可视状态变更事件
  if (hiddenPropName) {
   
    var changeList = [
      "visibilitychange",
      "mozvisibilitychange",
      "msvisibilitychange",
      "webkitvisibilitychange",
      "qbrowserVisibilityChange"
    ];
    // 循环注册上面的列表里的事件,同样是是为了兼容
    // 隐藏状态变更后,根据可视状态调用onHidden/onShown回调函数
    for (var i = 0; i < changeList.length; i++) {
   
      document.addEventListener(changeList[i], function (event) {
   
        var visible = document[hiddenPropName];
        //_ QQ App
        visible = visible || event["hidden"];
        if (visible)
          onHidden();
        else
          onShown();
      });
    }
  }
  // 此处省略部分关于 页面可视状态改变 的兼容性代码

  // 注册隐藏和显示事件,暂停或重新开始游戏主逻辑。
  this.on(game.EVENT_HIDE, function () {
   
    game.pause();
  });
  this.on(game.EVENT_SHOW, function () {
   
    game.resume();
  });
}

其实核心代码只有一点点…为了保持对各个平台的兼容性,
重要的地方有两个:

  1. 调用CCInputManager的方法
  2. 注册页面可视状态改变事件,并派发game.EVENT_HIDE和game.EVENT_SHOW事件。

来看看CCInputManager。

CCInputManager.js

// 注册系统事件 element是canvas
registerSystemEvent (element) {
   
  if(this._isRegisterEvent) return;

  // 注册过了,直接return
  this._glView = cc.view;
  let selfPointer = this;
  let canvasBoundingRect = this._canvasBoundingRect;

  // 监听resize事件,修改this._canvasBoundingRect
  window.addEventListener('resize', this._updateCanvasBoundingRect.bind(this));

  let prohibition = sys.isMobile;
  let supportMouse = ('mouse' in sys.capabilities);
  // 是否支持触摸
  let supportTouches = ('touches' in sys.capabilities);
	
  // 省略了鼠标事件的注册代码
  
  //_register touch event
  // 注册触摸事件
  if (supportTouches) {
   
    // 事件map
    let _touchEventsMap = {
   
      "touchstart": function (touchesToHandle) {
   
        selfPointer.handleTouchesBegin(touchesToHandle);
        element.focus();
      },
      "touchmove": function (touchesToHandle) {
   
        selfPointer.handleTouchesMove(touchesToHandle);
      },
      "touchend": function (touchesToHandle) {
   
        selfPointer.handleTouchesEnd(touchesToHandle);
      },
      "touchcancel": function (touchesToHandle) {
   
        selfPointer.handleTouchesCancel(touchesToHandle);
      }
    };

    // 遍历map注册事件
    let registerTouchEvent = function (eventName) {
   
      let handler = _touchEventsMap[eventName];
      // 注册事件到canvas上
      element.addEventListener(eventName, (function(event) {
   
        if (!event.changedTouches) return;
        let body = document.body;

        // 计算偏移量
        canvasBoundingRect.adjustedLeft = canvasBoundingRect.left - (body.scrollLeft || window.scrollX || 0);
        canvasBoundingRect.adjustedTop = canvasBoundingRect.top - (body.scrollTop || window.scrollY || 0);
        // 从事件中获得触摸点,并调用回调函数
        handler(selfPointer.getTouchesByEvent(event, canvasBoundingRect));
        // 停止事件冒泡
        event.stopPropagation(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值