如何在cocos2dx实现碰撞检测,物理世界Scene::createWithPhysics()
精灵和碰撞
在没有接触到游戏引擎自带的物理引擎之前,应该怎么检测两个屏幕上的精灵或者说图片是否接触和碰撞,通常会通过坐标,检测两个精灵的坐标是否相交,这样的方法放在游戏引擎里就很蠢。游戏引擎里提供了封装的物理引擎API,通过这些API能更好的模拟显示世界中的物理效果。
如何在引擎的物理世界中进行碰撞
一般分为4步
1.创建物理场景
2.给需要碰撞的精灵设置刚体
3.设置物理属性(摩擦力,密度,重力等待…)
4.设置碰撞相关的掩码
创建游戏中的物理世界
// 一般创建一个场景的时候,这样创建的是一个普通场景
Scene* scene = Scene::create();
// 当需要使用物理引擎的API时第一件事就是通过createWithPhysics()
//创建场景时创建一个物理场景
auto gameScene = Scene::createWithPhysics();
// 通过物理场景对象设置重力的大小以及方向
gameScene->getPhysicsWorld()->setGravity(Vec2(0, -98));
为精灵设置刚体
在创建完物理场景之后,这个场景已经具备和现实世界类似的物理属性(摩擦力,密度,加速度等等…),只不过这些物理属性需要自己设置,不然就都默认为没有,要设置精灵的物理属性必须先给对应的精灵添加刚体
// 假设先创建一个精灵
this->hero = Sprite::create("sprite.png");
this->addChild(this->hero);
// 要让我们的精灵能进行碰撞,需要给对应的精灵添加一个刚体,
// Body也是身体的意思,createBox()是添加一个方块形刚体,
// 引擎中封装了很多简单的刚体还比如createCircle()创建一个圆形刚体
// 下面创建一个方形刚体
auto heroBody = PhysicsBody::createBox(
// 参数1:刚体的形状,因为我准备这张图片也是方形,所以之间
// getContentSize()获得图片大小设置成刚体形状
this->hero->getContentSize(),
// 参数2:刚体的物体特性(密度,弹性系数,摩擦系数)
PhysicsMaterial(10, 0.1, 0.1)
);
//把物理世界的刚体对象和游戏世界中的组件关联起来
this->setPhysicsBody(heroBody);
//
添加刚体时有一种情况需要注意
class Hero : public Node{
public:
Sprite* hero;
}
// 这是一种特殊的情况,通过继承Node节点在内部实现精灵,这时候如果要给精
// 灵添加刚体,需要和外面的节点绑定,而不是里面的sprite
// 以上情况,添加刚体
this->hero = Sprite::create("flying1.png");
this->hero->setPosition(Vec2::ZERO);
this->addChild(this->hero);
// 添加一个刚体
auto heroBody = PhysicsBody::createBox(
// 参数1:刚体的形状
this->hero->getContentSize(),
// 参数2:刚体的物体特性(密度,弹性系数,摩擦系数)
PhysicsMaterial(10, 0.1, 0.1)
);
//把物理世界的刚体对象和游戏世界中的组件关联起来
**我们把这个刚体对象和这个Node对象关联,而不是Node对象中的子节点Hero对象!**
this->setPhysicsBody(heroBody);
设置物理属性
通过设置物理属性,可以模拟现实世界中的摩擦力,密度,加速度等等…
// 通过场景对象设置场景里的重力
gameScene->getPhysicsWorld()->setGravity(Vec2(0, -98));
// 给精灵设置加速度
sprite->getPhysicsBody()->setVelocity(Vec2::ZERO);
等待
给刚体设置碰撞相关的掩码
这部分相对复杂一点,做完以上步骤后,还需要进行最后一步设置碰撞相关的掩码
碰撞监测有3个相关的掩码:
CategoryBitmask 表示物体的类别的掩码,简称“类别掩码”
ContactTestBitmask 关于是否调用回调函数(onContactBegin)的掩码(要不要搞事情),简称“测试掩码”
CollisionBitmask 关于是否自动弹开(是否可碰撞)的掩码,简称“碰撞掩码”
A和B允许碰撞的条件:
(不允许碰撞,是指A和B接触后,将“穿过”、“即使近在眼前,也会擦肩而过”, 允许碰撞,是指A和B接触后,因碰撞而弹开。)
要双方都能互相碰撞就能进行碰撞,对应现实世界里力的作用时相互的
( (A的CategoryBitmask & B的CollisionBitmask ) != 0) &&
(B的CategoryBitmask & A的CollisionBitmask) != 0) )
A和B碰撞后是否调用onContactBegin的条件:
(A的CategoryBitmask & B的ContactTestBitmask != 0) &&
(B的CategoryBitmask & A的ContactTestBitmask != 0)
同样回调函数需要双方都同意,才能调用
设置方法:
1.类别掩码
一共有32位,在一个场景中就可以表示32个不同的种类(第n种,则第n位为1)
2.碰撞掩码
一共32位,第n位,表示是否允许第n类对象和自己发生碰撞,如果允许它和自己碰撞,该位就设置1;否则就设置为0
允许和某类发生碰撞,不能一厢情愿,需要双方允许。
拒绝和某类发生碰撞,可单方面决定。
3.测试掩码
一共32位,第n位,表示第n类对象和自己发生碰撞时,是否回调onContactBegin方法。
如果允许,该位就设置1, 否则就设置0。
允许和某类发生碰撞回调,不能一厢情愿,需要双方允许。(相当于秀恩爱,需要双方许可,不能一厢情愿)
拒绝和某类发生碰撞回调,可单方面决定。
是否允许回调,与是否允许碰撞,两者之间完全没有关系。
下面通过一组数据来模拟设置和碰撞相关的掩码,这三类掩码都是通过一个int类型数据表示,一个int4个字节,一个字节8位,也就是32位,假设要为下面一张表的精灵设置三种类别掩码。
先设置类别掩码,下表一共有6个精灵,那么主角的类别掩码就占在第6的位置
记为0000 0000 0000 0000 0000 0000 0010 0000
前面的0忽略,就是100000 通常会写成十六进制 0x1F
把剩下的精灵依次占位 010000 001000 000100 000010 000001
接下来设置是否可碰撞的掩码,和是否调用回调函数的掩码
那主角举例,要和所有其他精灵碰撞那就掩码就设置为:011111
要碰撞后调用回调函数也设置为:011111
假设都不碰撞就是 000000
具体参考下面表格:

实例代码
给精灵的刚体设置相应掩码
// 设置类别掩码
heroBody->setCategoryBitmask(0x20);
// 设置碰撞掩码
heroBody->setCollisionBitmask(0x1F);
// 设置回调掩码
heroBody->setContactTestBitmask(0x1E);
关于回调函数
一旦发生碰撞会依次调用下面这些函数,从onContactBegin代表开始碰撞,onContactPreSolve表示持续碰撞中,onContactSeperate表示结束碰撞
通过设置对应的回调方法,可以处理相应的情况

// 先获得物理碰撞监听器
auto contactListener = EventListenerPhysicsContact::create();
// 通过CC_CALLBACK_1设置回调函数,回调函数的返回值和参数可以通过按住ctrl点击监听器后面的onContactBegin函数找到源码的funtion对象查看,
//比如std::function<bool(PhysicsContact& contact)> onContactBegin,返回值就是bool类型,参数是PhysicsContact& contact类型
contactListener->onContactBegin = CC_CALLBACK_1(GameScene::onContactBegin, this);
contactListener->onContactSeperate = CC_CALLBACK_1(GameScene::onContactSeperate, this);
contactListener->onContactPreSolve = CC_CALLBACK_2(GameScene::onContactPreSolve, this);
// 通过导演获得事件分发器
auto dispatcher = Director::getInstance()->getEventDispatcher();
// 通过addEventListenerWithSceneGraphPriority()分发到当前对象
dispatcher->addEventListenerWithSceneGraphPriority(contactListener, this);
回调函数的内部实现
// 举例这是开始碰撞时调用的回调函数,因为回调函数需要双方同意才能触发,
//这时候双方都会被传入回调函数,那么怎么对对应的精灵做相应的处理呢
bool GameScene::onContactBegin(PhysicsContact& contact) {
// 通过参数contact可以调用getShapeA()方法得到触点A,获得A的刚体,在获得它的节点
auto nodeA = contact.getShapeA()->getBody()->getNode();
// 那么另一个就是触点B,也进行对应的处理
auto nodeB = contact.getShapeB()->getBody()->getNode();
// 这时候可以定义一个传入节点的方法,把得到的节点A,B都传入通过对应的Tag值进行不同的处理
dispose_Contact(nodeA);
dispose_Contact(nodeB);
return true;
}
// Tag值就是通过宏定义给精灵或者节点添加的标识
#define HERO_TAG 1
#define HEART_TAG 2
#define BIRD_TAG 3
#define AIRSHIP_TAG 4
#define GROUND_TAG 5
#define SKY_TAG 6
// 对相关的精灵设置
setTag(HERO_TAG);
void GameScene::dispose_Contact(Node* node) {
// 通过getTag获得节点Tag值从而知道是那个精灵,通过if判断做处理(多态:父类指针可以指向子类对象)
auto tag = node->getTag();
if (tag == HEART_TAG) {
}
else if(tag == GROUND_TAG)
{
}
else if (tag == AIRSHIP_TAG) {
}
else if (tag == BIRD_TAG) {
}
}
更多推荐
所有评论(0)