陸 Cocos2d-xで物理エンジンを使い始める


目次

1 はじめに
2 物理エンジンを有効にする
3 物体を落とす
4 壁を作る
5 今後の予定

はじめに

本記事は、cocos2d-xおよびCocos Code IDEを導入し、絵(スプライト)を表示し、タッチに反応して絵を動かし、BGMや効果音を鳴らした人で、自分が作りたいのは物体が重力にしたがって落下するようなゲームだという人向けの、覚書です。cocos2-dxでは、chipmunkとBox2d、2つの物理エンジンが利用できますが、より簡単に利用できるchipmunkについて説明します。流れとしては、物理エンジンを有効にし、次に落下する物体を追加し、最後にその落下物を受け止める壁を追加します。

物理エンジンを有効にする

下のコードを使って説明を進めます。
2行目のcc.Scene:createWithPhysics()により、物理エンジンchipmunkがこのGameScene.luaの場面に適用されます。

GameScene.lua
local GameScene = class("GameScene",function()
    -- l)物理エンジンをこの場面に対し有効化
    return cc.Scene:createWithPhysics()
end)

function GameScene.create()
    local scene = GameScene.new()
    scene:addChild(scene:createLayer())

    return scene
end


function GameScene:ctor()
    self.visibleSize = cc.Director:getInstance():getVisibleSize()
    self.origin = cc.Director:getInstance():getVisibleOrigin()
    self.schedulerID = nil
end

function GameScene:playBgMusic()
    -- BGMを再生する
    local bgMusicPath = cc.FileUtils:getInstance():fullPathForFilename("background.mp3") 
    cc.SimpleAudioEngine:getInstance():playMusic(bgMusicPath, true)
    -- 効果音を先に読み込んでおく
    local effectPath = cc.FileUtils:getInstance():fullPathForFilename("effect1.wav")
    cc.SimpleAudioEngine:getInstance():preloadEffect(effectPath)
end

-- create layer
function GameScene:createLayer()
    local layer = cc.Layer:create()

    -- b)タッチイベントで呼ばれる関数
    local function onTouchBegan(touch, event)
        local location = touch:getLocation()

        -- 絵(スプライト)を追加
        local sprite = cc.Sprite:create("land.png")
        sprite:setPosition(location)
        layer:addChild(sprite)

        return true
    end

    -- c)タッチイベントで呼ばれる関数を登録
    local listener = cc.EventListenerTouchOneByOne:create()
    listener:registerScriptHandler(onTouchBegan, cc.Handler.EVENT_TOUCH_BEGAN )

    -- d)このレイヤーでのタッチイベント取得を有効化
    local eventDispatcher = layer:getEventDispatcher()
    eventDispatcher:addEventListenerWithSceneGraphPriority(listener, layer)

    return layer
end

return GameScene

上記コードのまま実行すると、タッチしたところに絵(スプライト)が追加されるだけで、落下しません。

物体を落とす

ではb)の中に下記m)を追記します。

GameScene.lua
...
    -- b)タッチイベントで呼ばれる関数
    local function onTouchBegan(touch, event)
        local location = touch:getLocation()

        -- 絵(スプライト)を追加
        local sprite = cc.Sprite:create("land.png")
        sprite:setPosition(location)
        layer:addChild(sprite)

        -- m)物体を作成し絵(スプライト)に取り付ける
        local physicsBody = cc.PhysicsBody:createBox(sprite:getContentSize())
        sprite:setPhysicsBody(physicsBody)

        return true
    end
...

m)にて、絵の大きさに合わせて四角い物体を作り、その物体を絵(スプライト)に取り付けています。変更を保存して実行し、画面をクリックすると絵が落下しはじめます。また、落下中互いに衝突します。

落下する物体は、そのまま画面外へ落下していきます。次は画面を壁で囲みます。

壁を作る

createLayer関数に下記n)、l)を追記します。

GameScene.lua
...
function GameScene:createLayer()
    local layer = cc.Layer:create()

    -- n)ゲーム画面の中央を基点とする接続点を作る
    local node = cc.Node:create()
    node:setAnchorPoint(0.5, 0.5)
    node:setPosition(self.visibleSize.width/2, self.visibleSize.height/2)
    -- l)その接続点に四角の物体をつける
    local body = cc.PhysicsBody:createEdgeBox(cc.size(self.visibleSize.width, self.visibleSize.height))
    body:setDynamic(false) 
    node:setPhysicsBody(body)
    layer:addChild(node)

    -- b)タッチイベントで呼ばれる関数
    local function onTouchBegan(touch, event)
        local location = touch:getLocation()

        -- 絵(スプライト)を追加
        local sprite = cc.Sprite:create("land.png")
        sprite:setPosition(location)
        layer:addChild(sprite)

        -- m)物体を作成し絵(スプライト)に取り付ける
        local physicsBody = cc.PhysicsBody:createBox(sprite:getContentSize())
        sprite:setPhysicsBody(physicsBody)

        return true
    end

l)のノード(Node)ですが、これはスプライト等の”何か”を一まとめにして扱うための集約機能です。たとえば、ノードに複数の絵を追加した後、ノードを動かすと、それら複数の絵が一緒に動きます。ここでは、今壁となる四角い物体は透明で絵がないので、変わりにノードを取り付け先として使っています。
m)で、ゲーム画面の大きさの四角い物体を作成し、上のノードに取り付けています。

以上が、物理エンジンを有効にして、落下物と壁を追加する簡単な説明です。

物理エンジンを使わない場合、自分ですべての絵を動かしつつ、すべての物体の衝突位置などを毎時チェックする処理を記述しなければならず、さらに、重力をともなう加速運動をあわせる実装は手間がかかります。しかし、chipmunkを利用する事で、ユーザは位置と物体の大きさ等を指定するだけで、後は自動的に物理エンジンが動かしてくれます。簡単なコードですが、この恩恵を十分感じることができます。

今後の予定

物理エンジンを使ったゲーム開発では、特定の物体同士が接触すると得点またはゲーム終了という振る舞いがよく用いられます。

次回は、物体接触時のイベント処理コードについて説明します。