物理シミュレーションで物体の衝突時の処理（Cocos Code IDE, Lua言語）


Cocos Code IDEを使ってLua言語で、物理シミュレーションの物体衝突処理のサンプルです。

|実行環境、IDE|Cocos2d-xバージョン|言語|
|:--|:--|:--|
|Cocos Code IDE Mac OS X 1.0.0-RC2|Cocos2d-x V3.2|Lua言語|

Code IDEとLuaでの物理シミュレーションの基本はこちらに投稿しました。参考にしてください。
[物理シミュレーション基本（Cocos Code IDE, Lua言語）](http://qiita.com/senchan05/items/633b0032cc825a874cdf)

## サンプルコード

プロジェクトを新規作成で作成します。（FileメニューのNew->Cocos Lua Project...から作成できます。）
プロジェクト名は適当につけてください。画面は縦画面(portrait)を指定します。

main.luaファイルを修正します。

```lua:main.lua/26行目
cc.Director:getInstance():getOpenGLView():setDesignResolutionSize(320, 480, 0) -- 縦画面用に(480, 320, 0)から(320, 480, 0)に変更
```

```lua:main.lua/31行目
--gameScene:playBgMusic() --使わないのでコメントアウト
```

GameScene.luaを書き換えます。

```lua:GameScene.lua
require "Cocos2d"
require "Cocos2dConstants"

local GameScene = class("GameScene",function()
    return cc.Scene:createWithPhysics()
end)

function GameScene.create()
    local scene = GameScene.new()

    scene:getPhysicsWorld():setDebugDrawMask(cc.PhysicsWorld.DEBUGDRAW_ALL) -- デバッグ表示用（任意）

    local visibleSize = cc.Director:getInstance():getVisibleSize()

    -- 画面と同じサイズの枠を作ります
    local node = cc.Node:create()
    node:setAnchorPoint(0.5, 0.5) -- アンカーポイントを設定しないとwarningが出る
    node:setPosition(visibleSize.width/2, visibleSize.height/2)
    scene:addChild(node)

    -- 物体を作成し、四角いエッジの形を割り当てます
    local body = cc.PhysicsBody:create()
    local rectangle = cc.PhysicsShapeEdgeBox:create(cc.size(visibleSize.width, visibleSize.height))
    body:addShape(rectangle)
    body:setDynamic(false) 
    node:setPhysicsBody(body)  

    -- スプライトを作る
    local sprite1 = cc.Sprite:create("box.png")
    sprite1:setPosition(100, 400)
    scene:addChild(sprite1)

    -- 同型の物体を割り当てる
    local physicsBody1 = cc.PhysicsBody:createBox(sprite1:getContentSize())
    sprite1:setPhysicsBody(physicsBody1)

    -- スプライトを作る
    local sprite2 = cc.Sprite:create("circle.png")
    sprite2:setPosition(80, 300)
    scene:addChild(sprite2)

    -- 同型の物体を割り当てる
    local physicsBody2 = cc.PhysicsBody:createCircle(sprite2:getContentSize().width/2)
    sprite2:setPhysicsBody(physicsBody2)

    -- カテゴリービットマスクと衝突判定ビットマスクを設定します
    physicsBody1:setCategoryBitmask(1)    -- 00000001
    physicsBody1:setContactTestBitmask(2) -- 00000010
    physicsBody2:setCategoryBitmask(2)    -- 00000010
    physicsBody2:setContactTestBitmask(1) -- 00000001

    -- 衝突時に呼ばれる関数
    local function onContactBegin(contact)
        local a = contact:getShapeA():getBody()
        local b = contact:getShapeB():getBody()

        print("衝突しました")

        return true
    end

    -- 衝突時のイベントで指定した関数を呼び出すようにする
    local contactListener = cc.EventListenerPhysicsContact:create()
    contactListener:registerScriptHandler(onContactBegin, cc.Handler.EVENT_PHYSICS_CONTACT_BEGIN)
    local eventDispatcher = scene:getEventDispatcher()
    eventDispatcher:addEventListenerWithSceneGraphPriority(contactListener, scene)
    
    return scene
end

return GameScene

```

四角い画像をbox.png，丸い画像をcircle.pngの名前で作成し、resディレクトリに追加しておきます。
![box.png](https://qiita-image-store.s3.amazonaws.com/0/45447/d88bf0ab-373e-748d-d3cc-324c2a7101c2.png "box.png")box.png
![circle.png](https://qiita-image-store.s3.amazonaws.com/0/45447/d413e1cd-2423-342c-037d-8c6744821bb0.png "circle.png")circle.png


実行すると、物体の衝突時にonContactBegin関数が呼ばれます。
![スクリーンショット 2014-09-23 10.43.30.png](https://qiita-image-store.s3.amazonaws.com/0/45447/80d0ce61-2967-4862-7ee9-a10e2896e086.png "スクリーンショット 2014-09-23 10.43.30.png")![スクリーンショット 2014-09-23 10.43.54.png](https://qiita-image-store.s3.amazonaws.com/0/45447/65f18407-5c98-7aba-3832-af13bf0a6ed5.png "スクリーンショット 2014-09-23 10.43.54.png")


## 物体の衝突時に関数を呼び出す

衝突判定を行うには、どの物体と衝突判定を行うかを設定しておく必要があります。

まずPysicsBody:setCategoryBitmask(int)で物体のカテゴリーを設定します。
次にPysicsBody:setContactTestBitmask(int)で衝突する対象となる相手のカテゴリーを設定します。setContactTestBitmaskは２つの物体でお互いに設定しないと衝突判定されません。

ビットマスクなので、ビット演算してContactTestBitmaskに含まれるカテゴリーが衝突対象となります。
例えばContactTestBitmaskが0011なら、CategoryBitmaskが0001や0010の物体と衝突判定されます。

```lua
-- カテゴリービットマスクと衝突判定ビットマスクを設定します
physicsBody1:setCategoryBitmask(1)    -- 00000001
physicsBody1:setContactTestBitmask(2) -- 00000010
physicsBody2:setCategoryBitmask(2)    -- 00000010
physicsBody2:setContactTestBitmask(1) -- 00000001

-- 衝突時に呼ばれる関数
local function onContactBegin(contact)
    local a = contact:getShapeA():getBody()
    local b = contact:getShapeB():getBody()

    print("衝突しました")

    return true
end

-- 衝突時のイベントで指定した関数を呼び出すようにする
local contactListener = cc.EventListenerPhysicsContact:create()
contactListener:registerScriptHandler(onContactBegin, cc.Handler.EVENT_PHYSICS_CONTACT_BEGIN)
local eventDispatcher = scene:getEventDispatcher()
eventDispatcher:addEventListenerWithSceneGraphPriority(contactListener, scene)
```

###うまく関数が呼ばれない時
衝突時に関数が呼ばれない時は、setCategoryBitmaskやsetContactTestBitmaskが間違っていることが多いので確認してください。

###呼ばれた関数内での処理
onContactBegin関数内で物体の処理を行うのですが、どちらの物体がaとb
に入るかはわかりませんので、関数内で判断して処理をする必要があります。

物体のCategoryBitmaskを得るには
`a:getCategoryBitmask()`

ノードを得るには
`a:getNode()`
のように取得できます。


## 補足
ここでの例では使用していませんが、特定の２つの物体の衝突判定を行うことができます。

### 特定の2つの物体の衝突時に関数を呼び出す

特定の２つの物体の衝突時に関数を呼び出すにはcc.EventListenerPhysicsContactWithBodiesを使います。

やはり、CategoryBitmask、ContactTestBitmaskを設定しておく必要があります。

```lua
local contactListener = cc.EventListenerPhysicsContactWithBodies:create(sprite1:getPhysicsBody(), sprite2:getPhysicsBody())
contactListener:registerScriptHandler(onContactBegin, cc.Handler.EVENT_PHYSICS_CONTACT_BEGIN)
local eventDispatcher = scene:getEventDispatcher()
eventDispatcher:addEventListenerWithSceneGraphPriority(contactListener, scene)
```

### 物体の衝突を設定する（特定の物体と衝突しないようにする）

これもここでの例では使用していませんが、物体同士がぶつかるかどうかを設定することができます。

初期状態ではすべての物体同士の衝突が起こるようになっていますので、物体同士がぶつかると跳ね返ります。

特定の物体とだけ衝突をコントロールするには、

まずPysicsBody:setCategoryBitmask(int)で物体のカテゴリーを設定します。
次にPysicsBody:setCollisionBitmask(int)で衝突する対象となる相手のカテゴリーを設定します。

ビットマスクなので、ビット演算してCollisionBitmaskに含まれるカテゴリーが衝突対象となります。

ただし、ここで衝突しないように設定しても、ContactTestBitmaskの設定とは関係ありませんので、物体がすり抜けたとしてもContactTestBitmaskによる衝突判定は別に行われます。