[cocos creator] - 碰撞系统范例

前言

cocos creator 碰撞系统网上能搜到的教程较少,而且不够详细,碰撞系统又是十分常用的系统,在此记录个人学习的过程以及分享学习成果,后续将会不断完善这个系列,完善之后做成专辑《cocos creator 碰撞系统从入门到 跑路 熟练》。

准备工作:触摸节点跟随鼠标移动:触摸节点跟随鼠标移动

需要提前了解的事情:cocos creator 矩形:绘制矩形

绘制 UI

绘制所需要的场景,Background 为白色的背景,Player 为黑色矩形,Enemy 为红色矩形,两个节点分别挂在 Background 下。

image

碰撞分组

cocos creator 的碰撞系统通过碰撞分组来检测哪些可以进行碰撞,哪些不能碰撞。如果没有配置碰撞分组就无法触发碰撞事件,选择 Player 节点,点击右侧 Group 旁边的编辑按钮:

image

将默认的 default 改为 player,并且增加一个 enemy 分组,把 player 与 enemy 相交的那个方框勾选起来,说明这两个分组可以进行碰撞:

image

没有勾选的说明不能碰撞,例如:player & player、enemy & enemy,玩家分组和玩家分组无法触发碰撞事件,敌人分组和敌人分组也无法触发碰撞事件。

也许有人无法理解这边的碰撞分组怎么看,其实这是一个表格,如果你玩过口袋妖怪,那么很快就能理解了,因为这其实跟属性克制表一模一样:

image

如果没玩过也没关系,看这里。

其实碰撞分组也是一个表格:

image

从第一行开始看起,如果勾选了交汇处的方框,说明 player 和 enemy 两个分组可以触发碰撞事件:

image

如果勾选了旁边的方框,说明 player 和 player 可以触发碰撞:

image

勾选了某个位置,就是让那个位置所在的行和列可以进行碰撞。

将 Player 节点设置为 player 分组,再将 Enemy 节点设置为 enemy 分组。

对了,还有地板,再添加 ground 分组,并设置让敌人可以和地板碰撞:

image

这样就设置好所有的碰撞分组了。

绑定脚本

创建 script 文件夹用来保存脚本:

image

script 下创建 player.jsenemy.js 两个脚本文件,文件内容如下。

player.js 为上一篇文章的脚本,绑定了触摸事件,可以拖动 Player 节点移动:

cc.Class({
    extends: cc.Component,

    properties: {
    },

    start() {
        // 绑定触摸移动事件
        this.node.on(cc.Node.EventType.TOUCH_MOVE, this.touchMoveEvent, this);
    },

    /**
     * 拖动节点事件
     * @param {*} event 
     */
    touchMoveEvent(event) {
        // 当前节点
        let node = this.node;
        // 获取触摸坐标
        let touchPosition = event.getLocation();
        // 转换为节点所在坐标系的坐标
        let nodePosition = this.node.parent.convertToNodeSpaceAR(touchPosition);
        // 水平方向最大距离
        nodePosition.x = this.getNodePositionX(nodePosition.x);
        // 竖直方向最大距离
        nodePosition.y = this.getNodePositionY(nodePosition.y);
        // 设置节点位置
        node.position = nodePosition;
    },

    /**
     * 获取x坐标
     * @param {integer} nodePosition 
     */
    getNodePositionX(nodePositionX) {
        let node = this.node;
        // 获取屏幕大小
        let windowSize = cc.view.getVisibleSize();
        // 节点一半的宽度
        let nodeHalfWidth = node.width / 2;
        // x坐标值,屏幕宽度的一半,左侧为负数,右侧为正数
        let xPos = windowSize.width / 2;
        if (nodePositionX - nodeHalfWidth <= -xPos) {
            nodePositionX = nodeHalfWidth - xPos
        } else if (nodePositionX + nodeHalfWidth >= xPos) {
            nodePositionX = xPos - nodeHalfWidth;
        }

        return nodePositionX;
    },

    /**
     * 获取y坐标
     * @param {integer} nodePositionY 
     */
    getNodePositionY(nodePositionY) {
        let node = this.node;
        // 获取屏幕大小
        let windowSize = cc.view.getVisibleSize();
        // 节点一半的高度
        let nodeHalfHeight = node.height / 2;
        let yPos = windowSize.height / 2;
        if (nodePositionY - nodeHalfHeight <= -yPos) {
            nodePositionY = nodeHalfHeight - yPos
        } else if (nodePositionY + nodeHalfHeight >= yPos) {
            nodePositionY = yPos - nodeHalfHeight;
        }

        return nodePositionY;
    },
});

enemy.js 内容如下:

cc.Class({
    extends: cc.Component,

    properties: {
    },

    start() {

    },
});

player.js 拖到 Player 节点上,将 enemy.js 拖到 Enemy 节点上,脚本的绑定也完成了。

添加碰撞组件

刚接触碰撞系统时,很容易忽略这一个步骤,添加好分组后还必添加碰撞组件,否则依然无法触发碰撞事件。

选择 Player 节点,添加碰撞组件 BoxCollision

image

BoxCollision 为碰撞盒子,可以理解为节点实际可以发生碰撞的区域,只有两个节点的碰撞区域接触了,才会触发碰撞事件。

设置碰撞盒子的宽(W)和高(H)等于方块的宽高:

image

同理,Enemy 节点也需要添加 BoxCollision 组件,宽高同样设置为 100。

添加上下左右四个 Ground 节点,设置碰撞分组并添加碰撞组件

image

启用碰撞系统

碰撞系统需要启用后才会触发,编辑 player.js,在 start 方法添加如下内容:

// 开启碰撞
let manager = cc.director.getCollisionManager();
manager.enabled = true;
// 绘制碰撞组件的形状(调试用)
manager.enabledDebugDraw = true;
// 绘制碰撞组件的包围盒(调试用)
manager.enabledDrawBoundingBox = true;

manager.enabledDebugDraw 设置为 true 可以看到碰撞时的形状:

image

enabledDebugDraw 开启后,如果再将 enabledDrawBoundingBox 设置为 true,可以看到碰撞盒子的边框:

image

这两个属性可以在调试的时候设置为 true,调试完成后记得将其设置为 false。

碰撞事件

编辑 enemy.js,添加如下方法:

onCollisionEnter(other, self) {
    cc.log(other, self);
}

保存后,运行游戏,拖动 Player 节点与 Enemy 节点触碰,查看控制台:

image

onCollisionEnter 为触发碰撞时的回调事件,当两个可以发生碰撞分组的节点互相接触时触发这个事件。

除了 onCollisionEnter 之外,还有以下几个不同时机触发的碰撞事件:

回调事件 触发时机 事件说明
onCollisionStay 持续接触时 当两个碰撞节点相互接触后,两个物体仍然黏在一起,则会触发这个回调,直到它们分开
onCollisionExit 离开碰撞时 当两个碰撞的物体分开时触发这个回调事件

所有碰撞回调事件都包含两个参数 (other, self),other 为另外一个碰撞节点,self 为自身节点

碰撞事件参数比较重要的字段是 world.aabbworld. preAabb

image

返回字段中的 aabb 包含了碰撞组件当前位置信息,preAabb 则是碰撞组件上一帧的位置信息。

它们都是 cc.Rect(矩形)类型,cocos creator 中的矩形是以左下角为原点,向上方和右方进行延伸,在碰撞系统中也是如此。

center 为矩形的中心点坐标,origin 为矩形原点(左下角那个点)位置。

其他的诸如 xMin 为矩形 x 坐标最小值,yMax 为矩形 y 坐标最大值,从变量名字就可以推断出来。

Size 为碰撞盒子的大小。

碰撞处理

到这一步,有人会疑惑,为什么触发了碰撞事件,物体还在原地?

这是因为 cocos creator 碰撞系统只对碰撞做检测,不会帮我们自动处理碰撞事件,因此需要在 onCollisionEnter 自己添加碰撞后执行的脚本。

要实现物理学上的碰撞效果(如受到重力下落,小球碰撞了会互相弹开),需要添加物理碰撞组件

讨论

还没有人评论~