Quick-Cocos2d-x初学者游戏教程(十一) ------------------ 物体碰撞检测

Quick-Cocos2d-x初学者游戏教程(十一)

本章主要讲解物体碰撞检测之间的原理,以及具体的实现方法。

碰撞检测

本游戏使用物理引擎的一个重要目的是为了让碰撞检测更方便,使用物理引擎可以进行精确的碰撞检测,而且执行的效率也很高。

在 Quick 3.3final 版本中,所有事件均有事件派发器统一管理,物理引擎的碰撞事件也不例外。它由 cc.EventListenerPhysicsContact 的实例来监听。

监听事件分类

碰撞监听事件有以下几种:

  • cc.Handler.EVENT_PHYSICS_CONTACT_BEGIN
    它是碰撞刚发生时触发的事件,并且在此次碰撞中只会被调用一次。我们可以通过返回 true 或者 false 来决定物体是否发生碰撞。
    需要注意的是,当这个事件的回调函数返回 flase 时,EVENT_PHYSICS_CONTACT_PRESOLVEEVENT_PHYSICS_CONTACT_POSTSOLVE将不会被触发,但EVENT_PHYSICS_CONTACT_SEPERATE必定会触发。
  • cc.Handler.EVENT_PHYSICS_CONTACT_PRESOLVE
    它在碰撞接触后的每帧都会调用。在该事件的回调函数中我们可以计算碰撞处理的一些属性,比如弹力,速度等。同样它可以通过返回 true 或者 false 来决定物体是否发生碰撞。
  • cc.Handler.EVENT_PHYSICS_CONTACT_POSTSOLVE
    发生在碰撞计算完毕的每个步骤(帧),你可以在此做一些碰撞的后续处理,比如安全的移除某个物体等。
  • cc.Handler.EVENT_PHYSICS_CONTACT_SEPERATE
    发生在碰撞结束两物体分离时,同样只会被调用一次。

下面我们来看看添加碰撞监听的代码,如下所示:

local contactListener = cc.EventListenerPhysicsContact:create()   -- 1
contactListener:registerScriptHandler(onContactBegin, cc.Handler.EVENT_PHYSICS_CONTACT_BEGIN)     -- 2
contactListener:registerScriptHandler(onContactSeperate, cc.Handler.EVENT_PHYSICS_CONTACT_SEPERATE)   -- 3
local eventDispatcher = cc.Director:getInstance():getEventDispatcher()   -- 4
eventDispatcher:addEventListenerWithFixedPriority(contactListener, 1)    -- 5
  1. 创建物体碰撞检测事件监听器对象(contactListener);
  2. 设置监听器的碰撞开始函数 onContactBegin;
  3. 设置监听器的碰撞分离函数 onContactSeperate;
  4. 监听器设置完毕,需要把它加入到引擎导演的事件分发器中。所以这里获取游戏的事件分发器(eventDispatcher);
  5. 将检测事件监听器(contactListener)添加到事件分发器(eventDispatcher)中,这样就可以触发碰撞检测事件。addEventListenerWithFixedPriority 方法是指定固定的事件优先级注册监听器,事件优先级决定事件响应的优先级别,值越小优先级越高。

掩码属性

在讲解 onContactBegin 和 onContactSeperate 函数之前,这里我们需要清楚地一点。即在默认情况下,物理引擎中的物体是不会触发碰撞回调事件的。也就是说,上面代码中的 onContactBegin 和 onContactSeperate 方法永远都不会调用到。

为什么啦? O(∩_∩)O~呵呵,咱不卖关子。因为每个 cc.PhysicsBody 都具有三个掩码属性,两个刚体能不能碰撞,能不能发送接触事件信息,都依靠于这三个参数的值。

所以,为了更好地解决刚才遇到的问题,下面我们先来了解下刚体的这三个 mask 属性。

  • CategoryBitmask:32位整型,刚体的类别掩码。它定义了一个物体所属类别,每一个物体在场景中都能被分配到多达32位不同的类别。默认值为0xFFFFFFFF。
  • ContactTestBitmask:32位整型,刚体的接触测试掩码。当两个物体接触时,用一个物体的类别掩码与另一个物体的接触测试掩码做“逻辑与”运行,如果结果为非零值,引擎才会新建 PhysicsContact 对象,发送碰撞事件。那么才发送碰撞事件消息。
    ContactTestBitmask 的设计是为了优化性能,并不是所有物体之间的碰撞我们都关心,所有这个 ContactTestBitmask 的默认值为0x00000000。
  • CollisionBitmask:32位整型,刚体的碰撞掩码。当两个物体接触后,用一个物体的碰撞掩码与另一个物体的类别掩码执行“逻辑与”运算,如果结果为非零值,那么该物体能够对另一个物体的碰撞发生反应。这里的“碰撞反应”会表现为一个物体受到另外物体的碰撞,而改变运动方向。默认值为0xFFFFFFFF。

总结:

  1. CategoryBitmask 是其它两个掩码比较的基础。
  2. CategoryBitmask & ContactTestBitmask 决定是否发送事件消息。
  3. CategoryBitmask & CollisionBitmask 决定是否产生刚体反弹效果。
  4. ContactTestBitmask 和 CollisionBitmask 互相之间没有联系。

注:每个 mask 都有对应的 get 和 set 接口来获取或者修改mask。
另外,发生碰撞和发送事件消息是不同的概念,前者是直观地一种表现-碰撞反弹,后者是一种消息机制,就是说是否调用碰撞事件的回调函数。

回到我们的游戏,根据需求我们配置了一份各类 Node 的掩码属性表,如下所示:

节点 类别掩码 接触测试掩码 碰撞掩码
玩家:Player 0111 1111 1001
心心:Heart 0001 0100 0001
鸟:Bird 0010 0010 1000
飞艇:Airship 0100 0100 1000
地面 1000 0001 0011
天界 1000 0000 0001

首先,以玩家和心心为例,我们希望它们发生碰撞,并希望发送事件消息。所以,玩家的类别掩码0111 逻辑&与 心心的碰撞掩码0001结果为0001(不为0),发生碰撞;且玩家的类别掩码0111 逻辑&与 心心的接触测试掩码0100结果为0100(不为0),发送事件消息。反之,心心的类别掩码0001 & 玩家的碰撞掩码1001结果为0001(不为0),发生碰撞;且心心的类别掩码0001 逻辑&与 玩家的接触测试掩码1111结果为0001(不为0),发送事件消息。所以,玩家和心心两个物体相互接触时,它们会发生碰撞反弹,同时会发出事件消息。

我们再举一例,这次以玩家和鸟为例,我们希望它们不发生碰撞,但希望发送事件消息。所以,玩家的类别掩码0111 逻辑&与 鸟的碰撞掩码1000结果为0000(为0),不发生碰撞;且玩家的类别掩码0111 逻辑&与 鸟的接触测试掩码0010结果为0010(不为0),发送事件消息。反之,鸟的类别掩码0010 & 玩家的碰撞掩码1001结果为0000(为0),不发生碰撞;且鸟的类别掩码0010 逻辑&与 玩家的接触测试掩码1111结果为0010(不为0),发送事件消息。所以,玩家和鸟两个物体相互接触时,它们不发生碰撞反弹,但会发出事件消息。

设置属性

为了更方便碰撞检测,我们给各个节点设置一个标签。标签的定义我们可以放在 config.lua 文件中。定义如下所示:

GROUND_TAG   = 1
HEART_TAG    = 2
BIRD_TAG     = 3
AIRSHIP_TAG  = 4
PLAYER_TAG   = 5

根据以上的掩码属性和标签属性,我们在游戏项目中要设置好这些属性,如对于 Player,我们要在绑定刚体的地方加上如下的一段代码,即 Player:ctor()方法中:

body:setCategoryBitmask(0x0111)
body:setContactTestBitmask(0x1111)
body:setCollisionBitmask(0x1001)

self:setTag(PLAYER_TAG)

其他节点同理,这里就不一一添加了。

碰撞实现

下面我们给出碰撞检测的整段函数,程序中我们把它封装在了 addCollision 方法中,如下所示:

function GameScene:addCollision()

    local function contactLogic(node)
        -- 4
        if node:getTag() == HEART_TAG then
            -- 给玩家增加血量,并添加心心消除特效,下章会加上
            node:removeFromParent()
        -- 5
        elseif node:getTag() == GROUND_TAG then
            -- 减少玩家20点血量,并添加玩家受伤动画,下章会加上

        elseif node:getTag() == AIRSHIP_TAG then
            -- 减少玩家10点血量,并添加玩家受伤动画

        elseif node:getTag() == BIRD_TAG then
            -- 减少玩家5点血量,并添加玩家受伤动画
        end
    end

    local function onContactBegin(contact)   -- 1
        -- 2
        local a = contact:getShapeA():getBody():getNode()
        local b = contact:getShapeB():getBody():getNode()
        -- 3
        contactLogic(a)
        contactLogic(b)

        return true
    end

    local function onContactSeperate(contact)   -- 6
        -- 在这里检测当玩家的血量减少是否为0,游戏是否结束。
    end

    local contactListener = cc.EventListenerPhysicsContact:create()
    contactListener:registerScriptHandler(onContactBegin, cc.Handler.EVENT_PHYSICS_CONTACT_BEGIN)
    contactListener:registerScriptHandler(onContactSeperate, cc.Handler.EVENT_PHYSICS_CONTACT_SEPERATE)
    local eventDispatcher = cc.Director:getInstance():getEventDispatcher()
    eventDispatcher:addEventListenerWithFixedPriority(contactListener, 1)
end

之前提到过的 onContactBegin 和 onContactSeperate 函数我们都写在了 addCollision 方法中,而 contactLogic(node) 方法则是碰撞检测的逻辑函数。

下面我们依次来看看这些代码,请顺着编号的顺序来看:

  1. onContactBegin 方法是碰撞开始时触发的回调函数。只有当场景中两个碰撞的物体的CategoryBitmask & ContactTestBitmask 不为0时才调用。
  2. 获取发生了碰撞的两个节点。
  3. 调用 contactLogic 方法检测碰撞逻辑。因为两个物体发生碰撞时,可能是A碰了B,也可能是B来碰了A,所以这里我们调用了两次 contactLogic 方法。
  4. 在 contactLogic 方法中,当被检测节点的标签为 HEART_TAG,即心心时,我们将在这里给玩家增加血量,添加心心消除特效,并且从屏幕中移除被撞的心心。
  5. 当被检测节点的标签为地面(GROUND_TAG)、飞艇(AIRSHIP_TAG)、鸟(BIRD_TAG)时,减少玩家相应地血量,并添加玩家受伤动画。其中给玩家增加血量,添加心心消除特效等我们下章再讲。
  6. onContactSeperate 方法是碰撞分离时触发的方法,在这里检测当玩家的血量减少是否为0,游戏是否结束。

在 GameScene 的 ctor 中调用 addCollision 方法后,你就可以实现碰撞检测了,此时只有玩家与心心碰上了才有反应。

本章说了太多细节,所以由于篇幅的原因,本章就此结束,咱们下章继续。

转载请注明出自:http://shannn.com/archives/435

时间: 2024-11-05 02:24:49

Quick-Cocos2d-x初学者游戏教程(十一) ------------------ 物体碰撞检测的相关文章

Quick-Cocos2d-x初学者游戏教程(二) -------------------- Quick内部的代码结构及相应的原理

Quick-Cocos2d-x初学者游戏教程(二) 上一章我们已经了解了Quick的一些基础知识,所以本章我们将开始深入到Quick内部,了解它内部的代码结构,同时在解析的过程中学到相应的原理,并学会如何修改.添加相应的代码文件,比如实现屏幕的分辨率适配. 前面我们创建了一个叫做parkour的游戏项目,其意思就是本人本来打算要做一个跑酷游戏的,但是因为这几天玩了一款叫做<el>的飞行游戏,非常有意境,并且几乎零差评,所以请允许我任性一下,善变的我不想做跑酷游戏了,而是想要挑战下这种类型的游戏

Quick-Cocos2d-x初学者游戏教程(三) ---------------------------- 解析quick新建项目的代码文件

Quick-Cocos2d-x初学者游戏教程(三) 2.main.lua 在src目录下,除了 config.lua 文件外,还有一个 main.lua 文件,这个 main.lua 是 Quick 项目的通用入口文件,它类似于 Cocos2d-x 中的 AppDelegate.h/cpp 文件,同时也类似于一般 Windows 工程中的 main 文件. 打开 main.lua 文件,其内容如下所示: 1 2 3 4 5 6 7 8 9 10 function __G__TRACKBACK__

Quick-Cocos2d-x初学者游戏教程(一)--------------------Quick的一些基础知识

本文转自Quick-Cocos2d-x初学者游戏教程(一) 前言 虽然之前已经写过了很多 Cocos2d-x 相关的教程和文档,但本次却是我第一次接触 Quick,第一次接触 Lua,所以此次的教程本人将站在一个初学者的角度(看到这里是不是想白眼我了,哈哈,别切啊!尽管第一次,但我身边可是有很多 Quick 大神的,廖大大也在旁边办公室,没准撒个娇大神就把他知道的全部要点倾囊相授了啦!),全方位的解析 Quick 的学习过程,并同大家一起学习如何利用 Quick-Cocos2d-x 开发一款属于

Quick-Cocos2d-x初学者游戏教程(十三) ---------- 完善游戏功能

Quick-Cocos2d-x初学者游戏教程(十三) 本章将是本教程的最后一章,在这章我们将完善游戏功能,即给游戏添加粒子特效,音乐音效,和玩家的受伤动画等等. 添加受伤动画 首先,我们来添加玩家受伤动画. 玩家受伤动画是 Player 与障碍物或地面碰撞的时候播放的一个动画效果,它是一个独立的帧动画,帧序列图片如下: PS:该动画添加到玩家上是非常丑的,所以如果各位有更好地资源可以不用它.我的美术御用妹子前两周出车祸把手伤了,没人给我画,所以才迫不得已找了这个丑图来替代,看不下去的见谅! 我们

Quick-Cocos2d-x初学者游戏教程(八)----------------- 物理引擎和角色

Quick-Cocos2d-x初学者游戏教程(八) 续上章载入 TiledMap 背景后,接下来的这章我们将开始引入物理引擎相关的东西,并且会开始创建我们的游戏角色.游戏地图中各类障碍物和奖励品的创建则会留到下一章. 构建物理世界 首先,物理引擎是干什么的应该不用我说吧?好吧,还是说一下(百度的):物理引擎通过为刚性物体赋予真实的物理属性的方式来计算运动.旋转和碰撞反映.所以用它来模拟真实世界的飞行.掉落等功能是具有得天独厚的优势的,这也是为什么我们的游戏要使用它的原因. 然后,我们要怎样使用物

Quick-Cocos2d-x初学者游戏教程(四) --------------- 开发初探(添加背景,标题,动作,按钮)

Quick-Cocos2d-x初学者游戏教程(四) 前面我们已经大概的讲解完了Quick的框架和代码结构,接下来,本章开始我们将正式进入到游戏的开发.当然在开发的过程中,如果遇到值得一提的知识点和概念,我们还是会为大家详细讲解的. 哈哈,这章的内容我加它为——开发初探,因为我们将先来实现一些基础的内容.本章将实现的效果如下图所示: 菜单场景 从前面章节讲解的的知识点中,我们知道每个新建的 Quick 项目里都已经默认创建好了一个 mainScene 场景,所以下面我们将利用这个现成的场景,把它改

Quick-Cocos2d-x初学者游戏教程(六) --------------------- 游戏逻辑

Quick-Cocos2d-x初学者游戏教程(六) 上一章我 们介绍了开发中会用到的辅助工具,并创建了 GameScene 场景,接下来这章我们将继续 GameScene 的传(bai)奇(bi).不过在开始编写 GameScene 场景的代码之前,我们还是先来明确一下游戏的功能和实现方法.这样可以帮我们更好的理解并设计逻辑.下面是总结出的结论: 在 GameScene 场景中,我们将创建一个飞行的娃娃角色,这个角色是游戏的唯一主角.游戏初始状态下,这个角色有满满的生命值,但随着时间的推移,生命

Quick-Cocos2d-x初学者游戏教程(五) --------------------- 辅助工具和跳转场景

Quick-Cocos2d-x初学者游戏教程(五) 上一章我们创建了游戏的菜单场景,并讲解了一些基础元素的创建,接下来这章,我们会先让大家了解一些接下来游戏开发中需要用到的辅助工具,然后再教大家创建另一个游戏场景,并跳着到该场景中来. 工具介绍 在开始真真的写代码之前,其实早该讲讲以下这些辅助工具的.这些工具可以让我们更好更方便的实现程序中的某些功能,比如地图编辑工具.粒子编辑工具 等.尽管这些工具在我之前的教程中已经不厌其烦的讲了好多次了,但是为了做到真正的初学者教程,本小节还是先来简单的介绍

Quick-Cocos2d-x初学者游戏教程(十) ---------------- 添加游戏障碍物

Quick-Cocos2d-x初学者游戏教程(十) 在我们的游戏中,我们除了添加奖励品外,还需要添加一些必要的障碍物来丰富游戏逻辑,增加游戏难度,所以本章我们将继续上章的内容——添加游戏障碍物.游戏中,障碍物是不止一种,这里有飞行的鸟,有上下移动的飞艇. 创建障碍物-飞艇 其实创建飞艇的逻辑和前面创建心心的逻辑是一样的,只不过这里我想让飞艇不停的上下移动,一方面做点带感的效果出来,另一方面也可以增加游戏难度. 看过之前教程的童鞋,现在应该懂得怎样创建这样的一个飞艇了吧.所以下面我们直接给出它的定