#はじめに
@tkyajiと申します。12/23のCocos2d-x AdventCalendarを担当します。
(12/23中に投稿できなかったことをお詫びします)
現在Cocos2d-xのSquirrel Bindingを細々と開発中のため、本日はそれを紹介させていただこうと思います。
なお、Cocos2d-xのバージョンは3.3をベースにしています。
#Cocos2d-Squirrel
Cocos2d-xのSquirrel BindingをGithubで公開しています。
cocos2d-squirrel
まだ HelloWorld が動く程度で、対応できていないところが多々あります。
色々書いていく前に、まずは Hello World のコードを見ていただきたいと思います。
// 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)
// create a scene. it's an autorelease object
dofile("src/HelloWorld.nut")
local scene = HelloWorld.createScene()
// run
director.runWithScene(scene)
class HelloWorld extends cc.Layer {
static function createScene() {
// 'scene' is an autorelease object
local scene = cc.Scene.create()
// 'layer' is an autorelease object
local layer = HelloWorld.create()
// add layer as a child to scene
scene.addChild(layer)
// return the scene
return scene
}
constructor() {
this.init()
}
function init() {
local visibleSize = cc.Director.getInstance().getVisibleSize()
local origin = cc.Director.getInstance().getVisibleOrigin()
/////////////////////////////
// 2. add a menu item with "X" image, which is clicked to quit the program
// you may modify it.
// add a "close" icon to exit the progress. it's an autorelease object
local closeItem = cc.MenuItemImage.create("res/CloseNormal.png",
"res/CloseSelected.png",
HelloWorld.menuCloseCallback)
closeItem.setPosition(cc.Vec2(origin.x + visibleSize.width - closeItem.getContentSize().width / 2,
origin.y + closeItem.getContentSize().height / 2))
// create menu, it's an autorelease object
local menu = cc.Menu.create(closeItem)
menu.setPosition(cc.Vec2.ZERO)
this.addChild(menu, 1)
/////////////////////////////
// 3. add your codes below...
// add a label shows "Hello World"
// create and initialize a label
local label = cc.Label.createWithTTF("Hello World", "res/fonts/Marker Felt.ttf", 24)
// position the label on the center of the screen
label.setPosition(cc.Vec2(origin.x + visibleSize.width / 2,
origin.y + visibleSize.height - label.getContentSize().height))
// add the label as a child to this layer
this.addChild(label, 1)
// add "HelloWorld" splash screen"
local sprite = cc.Sprite.create("res/HelloWorld.png")
// position the sprite on the center of the screen
sprite.setPosition(cc.Vec2(visibleSize.width / 2 + origin.x, visibleSize.height / 2 + origin.y))
// add the sprite as a child to this layer
this.addChild(sprite, 0)
}
function menuCloseCallback(pSender) {
cc.Director.getInstance().end()
exit(0)
}
}
見ての通り、Squirrel Binding では、できるだけC++と同じ感じで書けるようにしています。
#そもそもSquirrelって何?
Squirrelはドマイナーなスクリプト言語です。TIOBEのプログラミング言語ランキングでは、常に100位圏外です。
しかし、非常に使いやすく、覚えやすい言語です。
Luaより遅く、サイズも多少大きくなりますが、その分機能が多く使い易いという感じです。
Luaと比べると、
- C/C++やJavaに近い構文
- データ型として配列がある(もちろんインデックスは0から)
- クラスがあって継承もできる
- テーブルがJSON風に書ける(キーは文字列にする必要がある)
といった特徴があり、Luaの使いづらいところを解消してくれます。
※元々SquirrelはLuaの不満を元に作られた言語と言われているようです。
#Squirrel Binding の特徴
Squirrel Bindingは、Lua Bindingと比較して、以下の特徴があります。
- クラス名や関数名は短縮せずにそのまま
- Vec2やSizeのようなデータクラスや構造体もClassとしてBinding
- std::functionはSquirrelの関数、std::vectorは配列、std::mapはテーブルにBinding
- retain / release を書かなくても良いように、メモリ管理を自動化(未対応)
#使い方
まずはgit cloneして、ファイルをまるごと持ってきます。
externalディレクトリも含めて全部入った状態になっているので、
あとは通常通りcocosコマンドを実行していくだけです。
-l オプションで sq を指定すると、Squirrelのプロジェクトになります。
$ git clone https://github.com/tkyaji/cocos2d-squirrel.git
$ cd cocos2d-squirrel
$ ./setup.py
$ source FILE_TO_SAVE_SYSTEM_VARIABLE
$ cocos new MyGame -p com.your_company.mygame -l sq -d NEW_PROJECTS_DIR
$ cd NEW_PROJECTS_DIR/MyGame
実行する際も、通常通りcocos run
で実行可能です
cocos run -p ios
cocos run -p android
なお、ios / android 以外は未対応です。
#タッチイベント
タッチイベントは以下のように書きます。
local listener = cc.EventListenerTouchOneByOne.create()
listener.onTouchBegan = function(touch, event) {
print("onTouchBegan")
return true
}
listener.onTouchMoved = function(touch, event) {
print("onTouchMoved")
}
listener.onTouchEnded = function(touch, event) {
print("onTouchEnded")
}
listener.onTouchCancelled = function(touch, event) {
print("onTouchCancelled")
}
local dispatcher = cc.Director.getInstance().getEventDispatcher()
dispatcher.addEventListenerWithSceneGraphPriority(listener, sprite)
#スケジュール(タイマー)
スケジュール(タイマー)処理は、以下のように書きます。
this.schedule(function(time) {
print(time)
}, "update_key1")
#オブジェクト指向
Squirrelにはクラスがあります。継承もできます。(多重継承なし、抽象クラスなし)
これにより、上で書いた HelloWorld のサンプルのように、C++とほぼ同じように書くことができます。
class HelloWorld extends cc.Layer
このように、Cocos2d-xの各クラスを継承することも可能です。
ただし、ここで1つ注意が必要です。継承しているのはC++のクラスではなく、
Bindingして生成したSquirrelのクラスだということです。
サンプルのHelloWorldクラスでは、initメソッドを定義していますが、
このメソッドはcreateメソッドの中からは呼ばれません。
initメソッドが呼び出されるまでの流れを書くとこんな感じです。
No | HelloWorld (squirrel) |
Layer (squirrel) |
Layer (C++) |
comment |
---|---|---|---|---|
1 | HelloWorld.create | HelloWorldクラスのcreateメソッドを呼ぶ | ||
2 | Layer::create | HelloWorldクラスにcreateメソッドは存在しないので、 親のLayerクラスのcreateを呼ぶ |
||
3 | Layer::create | LayerクラスはC++のLayerクラスをBindingしたものなので、 C++のLayerクラスを実行 |
||
4 | Layer::init | C++のLayer::createメソッドの中で、this->initメソッドを実行 |
Layer::initはC++の中で実行される処理のため、Squirrelの処理には関与しないのです。
そのため Squirrel Binding で create の初期化処理を書く場合は、constructorから呼び出されるようにしてください。
グルーコードの中で、create実行後にそのクラスのconstructorを実行するようにしています。
#メモリ管理について
ご存知の通り、Cocos2d-xでは、retain / releaseメソッドでリファレンスカウンタを増減し、
メモリ管理を行う必要があります。
そしてこれは Lua Binding でも必要な訳ですが、Luaでメモリ管理が必要というのはかなり残念です。
Squirrel Bindingでも、現在は retain / release が必要な状態となっていますが、
これは不要になるように対応する予定です。
余談ですが、Squirrelのインスタンスが破棄される際に、
それに紐づくC++のインスタンスを破棄する、ということは、簡単に実現可能です。
(Luaでも簡単にできる)
面倒なのはCocos2d-xのリファレンスカウンタで、うまく対応しないと、
SquirrelとC++で循環参照のような状態になります。
#おわりに
実用的なレベルになるのはまだ先になると思いますが、こんな感じで開発を進めています。
Squirrelって結構いいな、と思ってもらえると嬉しいです。
明日は@hachupさんです。
Cocos2d-xの海外情報ということで、なかなか見ることの無い貴重な情報が得られそうですね。
以上です。