伍 Cocos2d-xでBGMを再生する 効果音を鳴らす 得点表示をピョンっとする

#目次
1 はじめに
2 ゲーム開始でBGMを再生する
3 絵を消すときに効果音を鳴らす
4 得点を表示する更新する
4 今後の予定

#はじめに
本記事は、cocos2d-xおよびCocos Code IDEを導入し、絵(スプライト)を表示し、タッチに反応して絵を消した人で、そろそろゲームのBGMや効果音を追加したい、あと得点の表示もやり始めたいという人向けの、覚書です。BGMを再生停止するにはシンプルオーディオエンジンという機能を使い、同じ機能を使って効果音の前読み込みとすばやい再生ができます。また、得点のようなテキスト表示を画面に追加するにはラベルという機能を使って実現します。まず、BGMを再生処理を追加し、その後、タッチした絵を消す所で効果音を再生するコードを加え、最後に得点表示に関するコードを追加する、という順番で説明していきます。

#ゲーム開始でBGMを再生する
下記コードから説明を開始します。

```GameScene.lua
local GameScene = class("GameScene",function()
    return cc.Scene:create()
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()
    --[[
    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()

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

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

        -- e)タッチ点に絵があるか確認
        if cc.rectContainsPoint(sprite:getBoundingBox(), location) then
            -- a)スプライトをフェードアウトしてから削除
            local fade = cc.FadeOut:create(1)
            local remove = cc.RemoveSelf:create()
            local sequence = cc.Sequence:create(fade,remove)
            sprite:runAction(sequence)
        end

        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
```

中ほどにあるplayBgMusic関数の中を下記のようにコメントアウトを外して下さい。変更を保存して、実行するとBGMが再生されます。

```GameScene.lua
...
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
...
```
`background.mp3`は、プロジェクト作成時に自動的にresフォルダに追加された、サンプルコード用の音楽ファイルです。ほかのmp3ファイルをresフォルダにドラッグアンドドロップし、"background.mp3"の部分をそのmp3ファイル名に変更すれば、BGMを変更できます。
`cc.SimpleAudioEngine:getInstance():playMusic(bgMusicPath, true)`で、BGMの再生を開始しています。また、第2引数でループ再生を有効にしています。
ちなみに、このGameScene.luaのplayBgMusic関数は、プロジェクト作成時に生成されたmain.luaから、GameSceneに遷移する直前に呼ばれます。このタイミングで再生したくない場合は、適宜コードを別の場所に移してください。

その下で効果音を読み込んでいますが、これは効果音の音データを先に読み込んでおくことで、その再生時にすぐに鳴らせるようにするためのものです。

#絵を消すときに効果音を鳴らす
効果音の先読みを行ったところで、実際に絵を消すタイミングで鳴らしてみます。GameScene.luaのe)の中、a)の手前にh)を追加します。変更を保存して実行すると、絵をタッチすると効果音が鳴り同時に絵が消えます。

```GameScene.lua
...
        -- e)タッチ点に絵があるか確認
        if cc.rectContainsPoint(sprite:getBoundingBox(), location) then
            
            -- h)効果音を鳴らす
            local effectPath = cc.FileUtils:getInstance():fullPathForFilename("effect1.wav")
            cc.SimpleAudioEngine:getInstance():playEffect(effectPath)

            -- a)スプライトをフェードアウトしてから削除
            local fade = cc.FadeOut:create(1)
            local remove = cc.RemoveSelf:create()
            local sequence = cc.Sequence:create(fade,remove)
            sprite:runAction(sequence)
        end
...
```

タッチ時に音を鳴らす場合は上記のコードでOKですが、絵を消した後で音を鳴らす場合は、アクションのCallFuncを使って実現できます。

```GameScene.lua
...
        -- e)タッチ点に絵があるか確認
        if cc.rectContainsPoint(sprite:getBoundingBox(), location) then
            -- a)スプライトをフェードアウトしてから削除
            local fade = cc.FadeOut:create(1)
            -- i)シーケンスを利用して絵を消した後で効果音を鳴らす
            local function func(node)
                -- h)効果音を鳴らす
                local effectPath = cc.FileUtils:getInstance():fullPathForFilename("effect1.wav")
                cc.SimpleAudioEngine:getInstance():playEffect(effectPath)
            end
            local callFunc = cc.CallFunc:create(func)
            local remove = cc.RemoveSelf:create()
            local sequence = cc.Sequence:create(fade,callFunc,remove)
            sprite:runAction(sequence)
        end
...
```

絵を動かしその動きに合わせて効果音を鳴らす、といった場合にアクションは非常に有効です。たとえば、絵がタッチされたらパチッとして音を鳴らし、その絵をフワッと消しながら、そこにキラッとしたパーティクルを出しながらキラキラッという音を鳴らすという、ゲームでよく見られる振る舞いを実現するのに活躍します。

音に関する簡単な説明は以上です。

#得点を表示する更新する
ゲーム画面に数字や文字を表示するにはラベルを使います。GameScene.luaのb)の手前に下記のとおりi)を追加します。

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

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

    -- i)得点表示を追加
    local score = 0
    local label = cc.Label:createWithTTF(score,"fonts/Marker Felt.ttf", 50)
    --local label = cc.Label:createWithSystemFont(score, "Arial", 50)
    label:setPosition(400,900)
    layer:addChild(label)

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

Marker Felt.ttfは、プロジェクト開始時にres/fontsフォルダに自動的に追加された、サンプルコード用のカスタムフォントファイルです。システムフォントで得点表示を作りたい場合は、コメントアウトしてある`cc.Label:createWithSystemFont`の方を使います。
変更を保存して実行すると、画面右上に0が表示されます。

![5-1 label.png](https://qiita-image-store.s3.amazonaws.com/0/90495/50de08f8-35cf-4a2f-d3b1-c65f847d7646.png)

では、得点表示が追加されたので、絵をタッチすると得点が増えるように変更します。GameScene.luaのe)の中のh)の前にj)を追加します。

```GameScene.lua
...
        -- e)タッチ点に絵があるか確認
        if cc.rectContainsPoint(sprite:getBoundingBox(), location) then
            
            -- j)得点表示を更新
            score = score + 1
            label:setString(score)
            
            -- h)効果音を鳴らす
...
```

![5-2 after updating label.png](https://qiita-image-store.s3.amazonaws.com/0/90495/8557c1da-d03a-903d-4fca-e971ab50a383.png)

さらに、得点表示を更新するタイミングで得点表示をピョンとジャンプさせて見ます。k)を追加し、変更を保存して実行すれば、絵をタッチすると得点表示がジャンプして得点が更新されます。

```GameScene.lua
...
        -- e)タッチ点に絵があるか確認
        if cc.rectContainsPoint(sprite:getBoundingBox(), location) then
            
            -- k)得点表示をジャンプ
            local jump = cc.JumpBy:create(1,cc.p(0,0),20,1)
            label:runAction(jump)
            
            -- j)得点表示を更新
            score = score + 1
            label:setString(score)
            
            -- h)効果音を鳴らす
...
```

`cc.JumpBy:create(1,cc.p(0,0),20,1)`は、ジャンプさせるアクション命令です。1秒かけて、現在の位置(100,100)+(0,0)の位置に向かって（つまり現在の位置）、20の高さで、一回ジャンプするという意味です。

今まで、絵(スプライト)にアクションを適用してきましたが、ラベルやパーティクルにもアクションを適用することができます。これにより、数値表示を更新するタイミングでその表示部をアクションにより目立たせることができます。

以上が、得点表示を追加して更新する場合のラベルの簡単な説明です。

#今後の予定
BGMの再生、絵の表示、タッチで絵を消去、その時に効果音を鳴らし、得点を追加するという基本的な部分をやってきました。これだけでも、タッチで動く簡単なゲームを作ることができます。しかしながら、一部のゲーム、たとえばツムツムのような、物体が重力加速運動しつつ他の物体と衝突するようなゲームを作りたい場合は、物理エンジンを利用すると手間をかけないで実現できます。

次回は、物理エンジンの簡単な使い方について説明予定です。