【官方教程】使用Quick-Cocos2d-x搭建一个横版过关游戏(五)

    xiaoxiao2021-03-25  149

    在 上一章中我们讲到了怎么在游戏中添加血量条等元素,接下来我们来学下怎么在Quick中使用物理引擎。

    Quick中使用的物理引擎

    在Quick中通过CCPhysicsWorld类来使用物理引擎Box2D和Chipmunk(目前仅支持Chipmunk)。至于Box2D和Chipmunk的优劣问题,在这里我就不比较了,敢兴趣的同学可以自己度娘。因为Quick目前只支持Chipmunk,所以大家也不用想太多了。

    CCPhysicsWorld接口

    关于CCPhysicsWorld类使用,大家可以打开Quick里面的Smaples下的physics,里面有一个简单的物理引擎的使用例子。打开文件lib/cocos2d-x/external/chipmunk/cocos2dx_support下的CCPhysicsWorld.h文件,里面包含了CCPhysicsWorld类的所有定义。我们在Lua里使用时只需要直接调用里面的接口就可以了。在Quick的physics例子中创建一个PhysicsWorld是这样写的:

    -- create physics world self.world = CCPhysicsWorld:create(0, GRAVITY) -- add world to scene self:addChild(self.world)

    通过调用CCPhysicsWorld:create可以创建一个物理世界,create的两个参数分别为x轴上和y轴上的重力因子。当为负数时表示往左方或者是下方运动,当为正数时表示往右方或者上方运动,绝对值的数值越大,重力加速度越大。用过Chipmunk的同学知道,Chipmunk里有几种Body的类型。

    圆形 通过调用createCircleBody来创建一个圆形的Body矩形 通过调用createBoxBody来创建一个矩形的Body多边形 通过调用createPolygonBody来创建一个多边形的Body

    用法如下:

    -- 创建一个矩形碰撞区域 local leftWallBody = self.world:createBoxBody(0, WALL_THICKNESS, display.height) -- 创建一个圆形碰撞区域 local coinBody = self.world:createCircleBody(COIN_MASS, COIN_RADIUS)

    通过createXXXBody创建出来的Body要通过bind方法绑定到精灵上。为了更好的模拟物理环境,也可以通过以下接口来设置一些系数:

    body:setFriction(friction) 摩擦系数 0-1.0body:setElasticity(elasticity) 反弹系数 0-1.0body:setIsSensor(isSensor) 是否可以感应body:setVelocity(velocityX, velocityY) 速度body:setAngleVelocity(velocity) 角速度

    比如你的物体从一个斜坡上下滑时,你可以设置一个摩擦系数;如果你的物体是从高处掉落,那么你可以给他设置一个反弹系数。这些参数设置能够更真实的反应物理世界里的一些物理特性。 创建Body时需要注意的是函数的第一个参数,当mass的值<=0时表示是静态刚体,当mass的值大于0表示是普通刚体。

    CCPhysicsWorld碰撞检测

    在Quick中增加碰撞检测,有这么几个步骤:

    body:setIsSensor(true): 设置能感应Bodybody:setCollisionType(碰撞类型): 设置Body的碰撞类型,这个参数当有不同的刚体时,必须要设置不同的整型值。world:addCollisionScriptListener(事件响应函数,碰撞类型1,碰撞类型2): 碰撞类型1和碰撞类型2,是你想要检测的碰撞发生的两个物体。

    比如我们现在有两个物体,分别为类型1和类型2,我们可以这样写:

    world:addCollisionScriptListener(handler(self, self.onCollision), 1, 2) function ClassA:onCollision(eventType, event) if eventType == 'begin' then elseif eventType == 'preSolve' then elseif eventType == 'postSolve' then elseif eventType == 'separate' then end end

    碰撞响应函数会传入两个参数,一个为eventType,有下面几种可能的值:

    begin: 碰撞开始preSolve: 持续碰撞(相交),可以设置相交时独自的摩擦,弹力等postSolve: 调用完preSolve后,做后期处理,比如计算破坏等等separate: 分开,当删除一个shape时也会调用

    通过这几个事件,能够对物体之间的碰撞做出及时的反应。

    在游戏中添加碰撞

    给游戏添加元素

    为了更好的添加效果,我们还要给玩家角色和敌人对象添加被击中的效果,并且在被击中后用户要掉一部分血。怎么添加在前面的章节中已经讲解过,我们就不细说了,不明白的可以参看代码。

    添加碰撞代码

    首先,我们先创建一个CCPhysicsWorld对象,为了方便管理,我们新建了一个类PhysicsManager, 代码如下:

    local world = nil local PhysicsManager = class("PhysicsManager", function() -- 创建 return CCPhysicsWorld:create(0, 0) end) CollisionType = {} CollisionType.kCollisionTypePlayer = 1 CollisionType.kCollisionTypeEnemy = 2 function PhysicsManager:getInstance() if world == nil then world = PhysicsManager.new() end return world end return PhysicsManager

    这样就创建了一个PhysicsManager的单例对象。 接下来,我们再在每个玩家和敌人对象中添加碰撞的刚体,比如,我们在玩家对象中添加如下语句:

    local world = PhysicsManager:getInstance() self.body = world:createBoxBody(1, self:getContentSize().width/2, self:getContentSize().height) -- self.body:bind(self) self.body:setCollisionType(CollisionType.kCollisionTypePlayer) self.body:setIsSensor(true) self:scheduleUpdate(); self:addNodeEventListener(cc.NODE_ENTER_FRAME_EVENT, function() self.body:setPosition(self:getPosition()) end)

    这样就给Player添加了一个大小为Player的1/2宽度,高度为Player的高度的矩形刚体。这里要注意的是被注释掉的

    self.body:bind(self)

    Quick是更新body和精灵坐标的方式有两种,一种是通过将精灵绑定到body上,然后移动body;还有一种是通过移动精灵,在精灵内通过update来更新body的坐标。我们这游戏里采用了第二种方式。 在添加完碰撞检测后,我们再回到MainScene中,在MainScene:ctor中添加如下代码:

    -- 获取world并且添加到Scene中 local world = PhysicsManager:getInstance() self:addChild(world) -- 启动世界更新 world:start() -- 显示物理引擎调试层 self.worldDebug = world:createDebugNode() self:addChild(self.worldDebug) -- 添加碰撞检测函数 world:addCollisionScriptListener(handler(self, self.onCollision) , CollisionType.kCollisionTypePlayer, CollisionType.kCollisionTypeEnemy)

    为了在碰撞时检测事件,我们添加MainScene:onCollision函数:

    function MainScene:onCollision(eventType, event) if eventType == 'begin' then self.canAttack = true local body1 = event:getBody1() local body2 = event:getBody2() if body1:getCollisionType() == CollisionType.kCollisionTypePlayer and body2 then body2.isCanAttack = true end elseif eventType == 'separate' then self.canAttack = false local body1 = event:getBody1() local body2 = event:getBody2() if body1:getCollisionType() == CollisionType.kCollisionTypePlayer and body2 then body2.isCanAttack = false end end end

    这里添加个isCanAttack,这个是为了在接触时表示玩家可以砍敌人了,离开以后玩家是不能砍敌人的。我们稍微更改下MainScene:clickEnemy函数:

    function MainScene:clickEnemy(enemy) if self.canAttack then if self.player:getState() ~= "attack" then self.player:doEvent("clickEnemy") print("enemy:canAttack " .. tostring(enemy:getCanAttack())) if enemy:getCanAttack() and enemy:getState() ~= 'hit' then enemy:doEvent("beHit", {attack = self.player.attack}) end end else local x,y = self.player:getPosition() self.player:walkTo({x=x, y=y}) if self.player:getState() ~= 'walk' then self.player:doEvent("clickScreen") end end end

    这样一来,只有在碰撞时,玩家才能砍敌人。当然现在的敌人是不会动的,怎么解决敌人动的问题,就看大家的智慧了。嘿嘿!

    转载请注明原文地址: https://ju.6miu.com/read-4219.html

    最新回复(0)