Cocos2d-x 勉強第12回「Luaを試してみる」

木内智史之介(シャッチョー)
ミンカさんけっこんしてくださいおねがいします(ズザー
SEGAさん、DIVAの筐体ください(ズザー

Luaとは?

細かい話はすっ飛ばすとして、要はスクリプト言語の一種らしい。
スクリプト言語特有(?)の型付けの弱い言語で、型のない言語の中では最速を誇っているようです。

cocos2d-xから、このLuaを用いて開発できるように調整してくれているのが「Lua binding」でござる。

luaで開発するメリット

一番はやはり「生産性の高さ」だろうと思います。
cppで開発すると、どうしてもヘッダーと実装の二重構造がつきまとう上に、型の縛りが強くて、 あれこの関数にはstd::stringだっけ、charにキャストしなくちゃいけないんだっけなどと、プログラムの本質とは遠いところに意識を強く引っ張られてしまうのは、 僕のような怠惰なプログラマにとっては非常にやる気を削がれる部分なのだ。

あと、luaスクリプトの更新だけであれば「ビルド」という手順を飛ばすことができる。
これも生産性をあげる効果としてとてもでかい!

luaで開発するデメリット

気になるのは「速度」に関する問題かな?

#20 まさか、Cocos2d-x 使っているのに C++ 書いてるわけないよね?

こちらの記事によると、申し分ない速度が出るようだけれども、具体的な数字が出ていないために未知数なことは否めない。
あと気になる点としては、どうもluaスクリプトは「Resources」という扱いらしく、 つまり、アプリの中のファイル構成をのぞければ容易に改変が可能という問題があります。
これに関しては、「あらかじめ暗号化や難読化を施しておく」という対応が可能らしいので、その対応は必須となりそうです。

プロジェクトの作成

プロジェクトの作成は下記のようなコマンドで行います。

1
$ ./create-multi-platform-projects.py -p study012lua -k jp.co.befool.study012lua -l lua

試しにビルドしてみてびっくり。なんとcppにおけるサンプルとは力の入れようが違うッ…!
どういうことなんだ、これは。。
スクリプト言語を噛む事でも、負けず劣らずの事ができますよ!という強いアピールなのかしら?
とにかく、心強いにも程があります。

みよ、このリッチな内容を!

丁度いいので、このサンプルコードの中を確認していく事にします。

luaサンプル確認の旅

c++からluaの呼び出し

1
2
3
4
5
6
7
// luaエンジンの取得、登録
auto engine = LuaEngine::getInstance();
ScriptEngineManager::getInstance()->setScriptEngine(engine);

// luaスクリプト実行
std::string path = FileUtils::getInstance()->fullPathForFilename("hello.lua");
engine->executeScriptFile(path.c_str());

コメントの書き方

1
2
3
4
5
-- 1行コメント
--[[
複数行コメント
ほげほげほげ
]]--

細かい諸々

1
2
3
4
5
6
7
8
9
-- グローバル変数の定義
foo = "bar"

-- スコープ内変数の定義
local foo = "bar"

-- 他ファイルの読み込み
-- 拡張子は省略できる模様。
require "hello2"

ログの出し方

1
2
3
cclog = function(...)
    print(string.format(...))
end

おうっ、、、標準出力に出すことがログに出すことなのか。。
これはWEB系言語にはない感覚。

sceneの作成

1
2
local scene = cc.Scene:create()
cc.Director:getInstance():runWithScene(scene)

layerの作成

1
2
local layer = cc.Layer:create()
scene:addChild(layer)

スプライトの作成

1
2
3
local sprite = cc.Sprite:create("miku.jpg")
sprite:setPosition(x, y)
layer:addChild(sprite)

サウンドの再生

1
2
3
4
5
6
7
-- BGM
local bgMusicPath = cc.FileUtils:getInstance():fullPathForFilename("background.mp3")
cc.SimpleAudioEngine:getInstance():playMusic(bgMusicPath, true)

-- 効果音
local effectPath = cc.FileUtils:getInstance():fullPathForFilename("effect.wav")
cc.SimpleAudioEngine:getInstance():preloadEffect(effectPath)

タッチメニューの作成

1
2
3
4
5
6
local menuPopupItem = cc.MenuItemImage:create("default.png", "on-touch.png")
menuPopupItem:setPosition(0, 0)
menuPopupItem:registerScriptTapHandler(callbackHandler)
menuPopup = cc.Menu:create(menuPopupItem)
menuPopup:setPosition(x, y)
layer:addChild(menuPopup)

タッチイベントの検知

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
-- タップ開始
local function onTouchBegan(x, y)
    cclog("onTouchBegan: %0.2f, %0.2f", x, y)
    return true
end

-- タップ移動
local function onTouchMoved(x, y)
    cclog("onTouchMoved: %0.2f, %0.2f", x, y)
end

-- タップ終了
local function onTouchEnded(x, y)
    cclog("onTouchEnded: %0.2f, %0.2f", x, y)
end

-- タップ処理ハンダラ
local function onTouch(eventType, x, y)
    if eventType == "began" then
        return onTouchBegan(x, y)
    elseif eventType == "moved" then
        return onTouchMoved(x, y)
    else
        return onTouchEnded(x, y)
    end
end

-- タップ処理登録
layer:setTouchEnabled(true)
layer:registerScriptTouchHandler(onTouch)

アニメーション

1
2
3
local animation = cc.Animation:createWithSpriteFrames({frame0,frame1}, 0.5)
local animate = cc.Animate:create(animation);
sprite:runAction(cc.RepeatForever:create(animate))

毎フレーム処理

1
2
3
4
5
local function tick()
    local x, y = sprite:getPosition()
    spriteDog:setPositionX(x + 1)
end
cc.Director:getInstance():getScheduler():scheduleScriptFunc(tick, 0, false)

ガベージコレクト関係

1
2
3
-- 正直これがなにを指定しているのかよくわからん
collectgarbage("setpause", 100)
collectgarbage("setstepmul", 5000)

まとめ

今日は、cocos2d-xのluaのサンプル内容を駆け足で確認していきました。
ざらっと見た感じ、大体の事はluaでも実現可能のように思えます。

変数の型に関して意識しないでいいのは本当に素晴らしいですね!
あと、headerファイルと実装で分離しないのもやはり生産性を高めるのは間違いなさそうです。
c++で書く必要もなさそうな処理に関してはluaを活用していくことで、生産性を2倍にも3倍にもできるような気がします。

ただ、冒頭でも触れたように、luaスクリプトは「リソースファイル」という扱いで、plainなテキストでアプリに同封されますので、 悪意のあるユーザーは改変が簡単にできてしまうでしょう。難読化、できれば暗号化できるのが望ましいです。

次回はそのあたりを調べてみようと思います。