弐 Cocos2d-xで絵を表示する まずmain.luaが気になる


#目次
1 はじめに
2 絵の表示を始める前に
2.1 main.lua
2.2 GameScene.lua
2.3 シーンとレイヤー
3 絵(スプライト)を表示する
4 今後の予定


#はじめに
本記事は、cocos2d-xおよびCocos Code IDEの環境導入を終えた人で、ゲームの絵(スプライト)を表示したい人に向けた、覚書です。絵を表示するコードは簡単ですが、絵を表示する前、つまりゲーム画面に入る前に、ゲーム画面の大きさ等を設定しているコードがあります。始めに、その部分について説明してから、絵の表示に進んでいきます。

#絵の表示を始める前に main.luaを編集する
新規プロジェクトには、デバッグ情報の表示や、ゲーム画面の表示サイズおよび画面遷移の特殊効果について記述しているコードが含まれています。

Cocos Code IDE上のエクスプローラーウィンドウにてプロジェクトフォルダ→src→main.luaをダブルクリックして、main.luaを開きます。

```main.lua

cc.FileUtils:getInstance():addSearchPath("src")
cc.FileUtils:getInstance():addSearchPath("res")

-- CC_USE_DEPRECATED_API = true
require "cocos.init"

-- cclog
local cclog = function(...)
    print(string.format(...))
end

-- for CCLuaEngine traceback
function __G__TRACKBACK__(msg)
    cclog("----------------------------------------")
    cclog("LUA ERROR: " .. tostring(msg) .. "\n")
    cclog(debug.traceback())
    cclog("----------------------------------------")
    return msg
end

local function main()
    collectgarbage("collect")
    -- avoid memory leak
    collectgarbage("setpause", 100)
    collectgarbage("setstepmul", 5000)

    -- initialize director
    local director = cc.Director:getInstance()

    --turn on display FPS
    director:setDisplayStats(true)

    --set FPS. the default value is 1.0/60 if you don't call this
    director:setAnimationInterval(1.0 / 60)
    
    cc.Director:getInstance():getOpenGLView():setDesignResolutionSize(480, 320, 1)
    
    --create scene 
    local scene = require("GameScene")
    local gameScene = scene.create()
    gameScene:playBgMusic()
    
    if cc.Director:getInstance():getRunningScene() then
        cc.Director:getInstance():replaceScene(gameScene)
    else
        cc.Director:getInstance():runWithScene(gameScene)
    end

end


local status, msg = xpcall(main, __G__TRACKBACK__)
if not status then
    error(msg)
end
```

最下部のコードと上の方にあるcclogおよびTRACKBACK関数は、Luaコード実行時のエラー出力に使われます。
中ほどのmain関数が重要で、開発者向け情報の表示有効化、ゲーム画面の大きさの設定が行われています。

32行目のdirector:setDisplayStats(true)をコメントアウトして、Control+Sキーで変更を保存します。
>--director:setDisplayStats(true)

Luaでの1行コメントアウトは--(連続ハイフン)で、複数行コメントアウトは --[[ ]]です。

これにより、GL vertsやGL callsといった開発者向け情報が非表示になります。Cocos Code IDE上部のWin32 Runボタンを押すと、左下の数字表示がなくなっていることが確認できます。

![2-1 Cocos Code IDE no stat.png](https://qiita-image-store.s3.amazonaws.com/0/90495/2494a449-19b8-bd49-e88e-04aad2856181.png)


実行したまま、Cocos Code IDEにてmain.luaの37行目最後の()内を下記の様に変更し、Control+Sキーで変更を保存します。
>cc.Director:getInstance():getOpenGLView():setDesignResolutionSize(540, 960,cc.ResolutionPolicy.EXACT_FIT)

すると、先ほど実行したままにしていたウィンドウ内の表示が即更新されます。

![2-2 Cocos 540 960 exactfit.png](https://qiita-image-store.s3.amazonaws.com/0/90495/f9bfc2fc-29ac-cde5-f24b-417d69a8dffa.png)

これがライブコーディングと呼ばれる機能です。Luaコードの変更内容が、すぐに実行結果として確認できます。Cocos Code IDEを使う大きな理由の一つです。

ちなみに、前述のsetDisplayStats等、変更後保存しても実行中のウィンドウで即時反映されないものが一部あります。その時は実行ウィンドウを一旦閉じて、再度Win32 Runボタンを押せば表示が更新されます。

上記37行目の変更内容は、ゲーム画面の大きさを幅540高さ960にして、端末の画面いっぱいに余白なく拡大するという意味です。プロジェクト作成時、デバッグ(Simulator)の画面サイズを540*960に設定しました。上記コードにより、デバッグ時の画面サイズとゲーム画面サイズが合ったことになります。最後の引数EXACT_FITは、実行端末の液晶画面サイズに合わせてゲーム画面540*960を引き伸ばすという意味です。後々タブレット等の実機で実行した場合にその様子が確認できます。

実行したまま、続けて、main.luaの43行目に下の文章を追記します。
>local transition = cc.TransitionFade:create(1, gameScene)

45,47行目の()内のgameSceneをtransitionに変更します。
>cc.Director:getInstance():replaceScene(transition)
>cc.Director:getInstance():runWithScene(transition)

保存すると、すぐに実行ウィンドウの表示が一瞬暗くなり、また明るくなればOKです。上記コードは１秒間かけてフェードしながらgameSceneへ遷移するという意味です。

![2-3 Cocos Transition.png](https://qiita-image-store.s3.amazonaws.com/0/90495/5f7c444e-9423-4dd4-fd3d-7488de76770f.png)


ちなみに、同43行目を`cc.TransitionJumpZoom:create(1, gameScene)`としても面白いです。ゲームで見かける派手な画面切り替え効果が得られます。

ここまでの変更でmain.luaのmain関数は下記のようになっているはずです。


```main.lua
local function main()
    ...

    -- initialize director
    local director = cc.Director:getInstance()

    --turn on display FPS
    --director:setDisplayStats(true)

    --set FPS. the default value is 1.0/60 if you don't call this
    director:setAnimationInterval(1.0 / 60)

    cc.Director:getInstance():getOpenGLView():setDesignResolutionSize(540, 960, cc.ResolutionPolicy.EXACT_FIT)
    
    --create scene 
    local scene = require("GameScene")
    local gameScene = scene.create()
    gameScene:playBgMusic()

    local transition = cc.TransitionFade:create(1,gameScene)
    
    if cc.Director:getInstance():getRunningScene() then
        cc.Director:getInstance():replaceScene(transition)
    else
        cc.Director:getInstance():runWithScene(transition)
    end
```

#絵の表示を始める前に GameScene.luaを編集する
GameScene.luaにはサンプルコードが入っているので、内容を以下のコードに置き換えます。

```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 a layer
function GameScene:createLayer()
    local layer = cc.Layer:create()

    return layer
end

return GameScene
```

GameScene.luaの内容を上記内容に変更した後、保存し、実行ウィンドウの内容が真っ黒になればOKです。これで、サンプルコードの不要部分を消して、自分のコードを追加する準備が整いました。

![2-4 Cocos Skelton gameScene.png](https://qiita-image-store.s3.amazonaws.com/0/90495/1ba22177-af2e-1bc1-5aa4-5ca996919def.png)


#絵の表示を始める前に GameScene.luaにおけるシーンとレイヤーについて
ここで、絵を表示する場所となるシーンとレイヤーについて概要を理解しておきましょう。

GameScene.luaで一つの場面（ゲームシーン）を構成しています。
シーンとは、スタート画面、ゲーム画面といった画面遷移を伴う際に切り分けられる場面の単位です。
シーンには、背景、主人公、得点などを表示する層（レイヤー）を複数追加できます。
レイヤーは、背景レイヤー、主人公レイヤー、得点レイヤーという風に使い分けが可能です。
本稿では、単純なゲーム開発を想定し、かつ理解し易さに重きを置いて、今後１つの層(レイヤー)に背景、主人公、得点表示等を配置していきます。

では実際のコードでシーンにレイヤーが追加されているところを見ていきます。
先ほどのGameScene.luaの8行目でGameSceneの場面にレイヤーを1つ追加しています。
> scene:addChild(scene:createLayer())

GameScene.luaの27行目以降にそのレイヤーについて記述があります。空のレイヤーを作成して、リターンしています。

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

    return layer
end
...
```

つまり、空のレイヤーが追加されたシーンを持っているのが現在のGameScene.luaコードとなります。
（それゆえ、現在の実行結果は表示が真っ暗なのです）

#絵(スプライト)を表示する
では、このレイヤーに、絵を追加して表示させてみましょう。下記のように30行目、createLayer関数内にコードを追加してください。これにより、絵が(100,100)の位置に表示されます。

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

    local sprite = cc.Sprite:create("land.png")
    sprite:setPosition(100,100)
    layer:addChild(sprite)

    return layer
end
...
```

変更を保存して実行し、下記のように表示されればOKです。

![2-5 Cocos 1st sprite.png](https://qiita-image-store.s3.amazonaws.com/0/90495/f3695732-d391-6d64-9ee0-648403488e68.png)

land.pngは、サンプルコードを実行するためにresフォルダに入っていたサンプルファイルの一つです。また、表示結果から、画面左下が原点(0,0)となっている事に気づかされます。また、cocosではresフォルダに入っているファイル名をそのまま記述するだけで表示できることが分かります。

AndroidやiOSで絵を表示しようとすると、Viewという部品について理解が必要だったり、画像ファイルのパス指定について知識が必要だったり、表示タイミングによってはViewのライフサイクルの関係から表示されなかったりする等、ビルドから実行まで挫折しやすい点が複数あります。それと比較すると、cocosでは簡単に絵が表示できます。

ここまで絵と呼び、最後に表示したものが、cocosおよびゲーム開発の用語でよく出る、スプライトです。上のコードにより、画像ファイルから１つのスプライト(高速重ね表示用に読み込んだ画)を作成し、レイヤーに表示するという、ゲーム開発の初歩が踏み込めた事になります。

#今後の予定
絵を表示すると、絵を動かしたり、タッチに応答させたくなります。

次回は、表示した絵（スプライト）を動かすアクション、タッチイベントについて説明します。