拾参 Cocos2d-xでゲームを作るときに便利なLua関数達

#はじめに
Cocos2d-xおよびCocos Code IDEでLuaプロジェクト開発の基礎の基礎を終え、ゲームを作り始めると、数字の表示桁数を揃えたくなったり、時間を取得したくなったりします。これらを実現するのに便利なLua関数が存在します。ここでは、ゲーム開発に便利そうなLua関数をいくつか紹介します。

#参照するコード

```GameScene.lua
-- a0) 経過時間の表示桁数を５桁０詰めで定義
local FORMAT_TIME = "%05d"
local FORMAT_ANGLE = "%03d"

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()

end

function GameScene:createLayer()
    local layer = cc.Layer:create()
    
    -- b1)起動時間を取得
    local time0 = os.time()

    -- 背景
    local sprite_background = cc.Sprite:create("farm.jpg")
    sprite_background:setPosition(self.visibleSize.width/2, self.visibleSize.height/2)
    sprite_background:setScale(1.2)
    layer:addChild(sprite_background)

    -- 経過時間を表示するラベルを追加
    local score = 0
    -- a1)７桁０詰めで表示を作成
    local label = cc.Label:createWithSystemFont(string.format(FORMAT_TIME,0), "Arial", 50)
    label:setPosition(250,930)
    layer:addChild(label)
    
    -- 角度表示ラベルを追加
    local label_angle = cc.Label:createWithSystemFont(string.format(FORMAT_ANGLE,0), "Arial", 50)
    label_angle:setPosition(60,930)
    layer:addChild(label_angle)
    
    -- e1)ゲージを追加
    local gauge = cc.ProgressTimer:create(cc.Sprite:create("menu2.png"))
    gauge:setType(cc.PROGRESS_TIMER_TYPE_BAR)
    gauge:setBarChangeRate(cc.p(1,0))
    gauge:setMidpoint(cc.p(0,0))
    gauge:setPosition(200,700)
    layer:addChild(gauge)
    
    -- e2)ゲージを満タン
    local full = cc.ProgressTo:create(3,100)
    gauge:runAction(full)
    
    -- c1)点と線を描くための準備
    local drawnode = cc.DrawNode:create()
    layer:addChild(drawnode)

    -- タッチ開始時に呼ばれる関数
    local touchBeganLocation = nil
    local function onTouchBegan(touch, event)
        local location = touch:getLocation()
        touchBeganLocation = location

        -- c2)タッチされた地点に点を描く
        drawnode:drawDot(touchBeganLocation, 10, cc.c4f(1, 1, 1, 0.4))
        
        return true
    end
    
    -- タッチ中に呼ばれる関数
    local function onTouchMoved(touch, event)
        local location = touch:getLocation()
        
        -- c3)タッチが動くたびにクリアして点と線を書く
        drawnode:clear()
        drawnode:drawDot(touchBeganLocation, 10, cc.c4f(1, 1, 1, 0.4))
        drawnode:drawDot(location, 10, cc.c4f(1, 1, 1, 0.4))
        drawnode:drawSegment(touchBeganLocation, location, 5, cc.c4f(1.0, 1.0, 1.0, 0.4))
        
        -- d1) タッチ開始点と現在のタッチ点を結ぶ直線が水平方向となす角度を計算して表示
        --print( (180/3.14)*math.atan2(location.y - touchBeganLocation.y, location.x - touchBeganLocation.x) )
        local theta = (180/3.14)*math.atan2(location.y - touchBeganLocation.y, location.x - touchBeganLocation.x)
        label_angle:setString(string.format(FORMAT_ANGLE,theta))
        
        -- e3)ゲージを増減
        local change = cc.ProgressTo:create(0.2,math.abs(theta)*100/180)
        gauge:runAction(change)
    end

    -- タッチ終了時に呼ばれる関数
    local function onTouchEnded(touch, event)
        -- タッチが離れたら点と線を消し
        drawnode:clear()
        -- 角度表示を0にする
        label_angle:setString(string.format(FORMAT_ANGLE,0))
        -- ゲージを0にする
        local change = cc.ProgressTo:create(0.3,0)
        gauge:runAction(change)
    end
    
    -- タッチイベントで呼ばれる関数を登録し、このレイヤーでのタッチイベント取得を有効化
    local listener = cc.EventListenerTouchOneByOne:create()
    listener:registerScriptHandler(onTouchBegan,cc.Handler.EVENT_TOUCH_BEGAN )
    listener:registerScriptHandler(onTouchMoved, cc.Handler.EVENT_TOUCH_MOVED )
    listener:registerScriptHandler(onTouchEnded, cc.Handler.EVENT_TOUCH_ENDED )
    local eventDispatcher = layer:getEventDispatcher()
    eventDispatcher:addEventListenerWithSceneGraphPriority(listener, layer)


    -- 定周期に呼び出される関数
    local function func(dt)
        -- b2)７桁０詰めで起動してからの経過時間(秒)を表示
        label:setString(string.format(FORMAT_TIME,os.difftime(os.time(),time0)))
    end

    -- 定周期で呼び出す関数を登録する 関数名fun 周期1秒　また　そのIDをself.schedulerIDに保存しておく
    self.schedulerID = layer:getScheduler():scheduleScriptFunc(func, 1, false)

    -- 他画面への遷移時に上記IDの定周期イベントを解除
    local function onNodeEvent(event)
        if "exit" == event then
            if self.schedulerID then
                cc.Director:getInstance():getScheduler():unscheduleScriptEntry(self.schedulerID)
            end
        end
    end
    
    -- 他画面への遷移イベントを有効化
    layer:registerScriptHandler(onNodeEvent)
    
    return layer
end

return GameScene

```

![13.png](https://qiita-image-store.s3.amazonaws.com/0/90495/8fb946ec-cfd6-a60b-66f7-d16da2ba0384.png)



#数字の表示桁数を揃える
a1)で表示桁数を0詰め5桁に指定しています。
>string.format("%05d",0)

初期表示に指定している値は0一つですが、ラベル表示は00000となります。

#時間を読み出す
b1)で現在の時間を秒数に直したものを取得しています。
>os.time()

b2)で下記コードを使い、起動時の時間time0と現在の時間の差分を取得しています。
>os.difftime(os.time(),time0))

#線を引く
c1)で点と線を描画するための描画系を初期化し、
c2)でタッチ開始時の地点に点を描画し、
>drawnode:drawDot(touchBeganLocation, 10, cc.c4f(1, 1, 1, 0.4))

c3)タッチ開始点と現在タッチしている点を結ぶ線を描画しています。
>drawnode:drawSegment(touchBeganLocation, location, 5, cc.c4f(1.0, 1.0, 1.0, 0.4))

#2点間を結ぶ直線と水平方向の角度を計算する
d1)にて、タッチ開始点と現在タッチしている点を結ぶ線が水平となす角度を三角関数のアークタンジェントを使い計算したあと、ラジアン(rad)を度(°)にしています。
> (180/3.14)*math.atan2(location.y - touchBeganLocation.y, location.x - touchBeganLocation.x)

#増減するゲージを表示する
e1)でゲージを初期化しています。ここではプロジェクト作成時に追加されたmenu2.pngの絵を使っていますが、色のついた細長い棒状の絵を使うのが一般的です。
e2)で一旦1秒かけて100%に
e3)でd1)の角度情報にあわせてゲージを増減しています。

#今後の予定
特にありません。