【リリース】Light Music Game
今年頭くらいからぼちぼち作っていたLight Music Gameがリリースされました。
避けゲーと音ゲーが融合したゲームで、
同じ色に当て続けると音がなり音楽のように聞こえます。
避けゲーの厳しい死にゲー要素と音ゲーの音楽を使った没入感がウリです。
よければDLお願いします。
Light Music - Google Play の Android アプリ
Light Music Gameを App Store で
[cocos2dx 3.3rc]Android開発環境構築 ~Mac~
新しくMacを買い替えて、cocos2dxの開発環境をセットアップしなおしたので、メモ
前提
・cocos2dxは3.3rc
・mac book pro
インストール先を作成
mkdir ~/opt
ダウンロード
Android NDK
Android NDK | Android Developers
上記をダウンロードして、~/optに展開
シンボリックリンクを張りました。
$ cd ~/opt $ ln -s android-ndk-r10c/ android-ndk
Android SDK
Android SDK | Android Developers
上記をダウンロードして、~/optに展開
ant
僕はHomebrewを使ってインストールしました。
$ brew install ant $ ant -version Apache Ant(TM) version 1.9.4 compiled on April 29 2014
~/optはこんな感じになりました(NDK -> android-ndk、SDK -> adt-bundle)
$ cd ~/opt $ ls -l drwxrwxr-x@ 4 staff 136 7 2 12:18 adt-bundle lrwxr-xr-x 1 staff 17 11 3 14:39 android-ndk -> android-ndk-r10c/ drwxr-xr-x 26 staff 884 11 3 14:38 android-ndk-r10c
環境設定
cocos2dxのディレクトリに入って、./setup.pyを実行し、Pathを設定する
.bash_profileの設定は下記のようになりました。
# Add environment variable COCOS_CONSOLE_ROOT for cocos2d-x export COCOS_CONSOLE_ROOT=~/Desktop/cocos2d-x-3.3rc0/tools/cocos2d-console/bin export PATH=$COCOS_CONSOLE_ROOT:$PATH # Add environment variable NDK_ROOT for cocos2d-x export NDK_ROOT=~/opt/android-ndk export PATH=$NDK_ROOT:$PATH # Add environment variable ANDROID_SDK_ROOT for cocos2d-x export ANDROID_SDK_ROOT=~/opt/adt-bundle/sdk export PATH=$ANDROID_SDK_ROOT:$PATH export PATH=$ANDROID_SDK_ROOT/tools:$ANDROID_SDK_ROOT/platform-tools:$PATH # Add environment variable ANT_ROOT for cocos2d-x export ANT_ROOT=/usr/local/Cellar/ant/1.9.4/libexec/bin export PATH=$ANT_ROOT:$PATH
プロジェクト作成&実行
先ほどの設定を有効にして、プロジェクト作成
$ source ~/.bash_profile $ cocos new HelloWorld -p com.akichim.HelloWorld -l cpp -d ~/
エミュレータの設定してないと、実行時にエラーになります
$ cd ~/HellowWorld $ cocos run -p android ... ... building apk Android platform not specified, searching a default one... Can't find right android-platform for project : "/Users/nownabe/projects/HelloCocos/proj.android". The android-platform should be equal/larger than 10
$ android
今回はandroid5を丸っとインストールしました。(ほかのosバージョンでも良いです)
確認。
$ android list target Available Android targets: ---------- ---------- id: 1 or "android-21" Name: Android 5.0 Type: Platform API level: 21 Revision: 1 Skins: HVGA, QVGA, WQVGA400, WQVGA432, WSVGA, WVGA800 (default), WVGA854, WXGA720, WXGA800, WXGA800-7in Tag/ABIs : android-tv/armeabi-v7a, android-tv/x86, default/armeabi-v7a, default/x86, default/x86_64
エミュレータ作成。最初のコマンドでyesを入力して、gpuをyesにしないとunfortunately has stoppedでエラーになります。
$ android create avd -n cocos -t 1 -b default/armeabi-v7a Do you wish to create a custom hardware profile [no]yes .... DPad support: Whether the device has DPad keys hw.dPad [yes]: GPS support: Whether there is a GPS in the device. hw.gps [yes]: GPU emulation: Enable/Disable emulated OpenGLES GPU hw.gpu.enabled [no]:yes GSM modem support: Whether there is a GSM modem in the device. hw.gsmModem [yes]: Keyboard support: Whether the device has a QWERTY keyboard. hw.keyboard [no]: ....
エミュレータ起動。
$ emulator -avd cocos
サンプルプロジェクト実行。
$ cd ~/HellowWorld $ cocos run -p android
以上で実行できました!
mysqlのロックまとめ
サマリー
・レコードがない状態でロックをとるとギャップロックが発生し、キーにしたもののレコードの間で共有ロックをとる
- >レコードが存在しない可能性がある場合は、ロックをとらない
・分離レベル
mysql> SELECT @@tx_isolation; +-----------------+ | @@tx_isolation | +-----------------+ | REPEATABLE-READ | +-----------------+
・テーブル構造
CREATE TABLE `user_ticket` ( `id` bigint(20) UNSIGNED NOT NULL, -- シーケンシャルid `user_id` int(10) UNSIGNED NOT NULL, -- チケット送りもと `to_user_id` int(10) UNSIGNED NOT NULL, -- チケット送り先 `is_received` tinyint(3) UNSIGNED NOT NULL, -- 受け取ったかどうか PRIMARY KEY (`id`), KEY `i1` (`user_id`, `to_user_id`, `is_received`), KEY `i2` (`to_user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=sjis;
・レコード
for i in {1..100}; do mysql -e "INSERT INTO user_ticket VALUES(${i}, ${i}, 101 - ${i}, 0);"; done; for i in {201..300}; do mysql -e "INSERT INTO user_ticket VALUES(${i}, ${i}, 301 - ${i}, 0);"; done;
mysql> select * from user_ticket limit 10; +----+---------+------------+-------------+ | id | user_id | to_user_id | is_received | +----+---------+------------+-------------+ | 1 | 1 | 100 | 0 | | 2 | 2 | 99 | 0 | | 3 | 3 | 98 | 0 | | 4 | 4 | 97 | 0 | | 5 | 5 | 96 | 0 | | 6 | 6 | 95 | 0 | | 7 | 7 | 94 | 0 | | 8 | 8 | 93 | 0 | | 9 | 9 | 92 | 0 | | 10 | 10 | 91 | 0 | +----+---------+------------+-------------+ 10 rows in set (0.00 sec)
・PRIMARY KEY のロック検証
trxA
mysql> begin; mysql> select * from user_ticket where id = 1 for update;
trxB
mysql> begin; # 同じ値ならロック競合 mysql> select * from user_ticket where id = 1 for update; ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction # 違うなら競合しない mysql> select * from user_ticket where id = 2 for update; +----+---------+------------+-------------+ | id | user_id | to_user_id | is_received | +----+---------+------------+-------------+ | 2 | 2 | 99 | 0 | +----+---------+------------+-------------+
・PRIMARY KEY のギャップロック
trxA
mysql> begin; mysql> select * from user_ticket where id = 105 for update; Empty set
trxB
mysql> begin; mysql> select * from user_ticket where id = 100 for update; +-----+---------+------------+-------------+ | id | user_id | to_user_id | is_received | +-----+---------+------------+-------------+ | 100 | 100 | 1 | 0 | +-----+---------+------------+-------------+ mysql> select * from user_ticket where id = 105 for update; Empty set # id 101 - 200ロック mysql> INSERT INTO user_ticket VALUES(101, 101, 1, 0); Lock wait timeout exceeded; try restarting transaction # 間にレコードがあるので、301 以上にロックはかかってない mysql> INSERT INTO user_ticket VALUES(301, 101, 1, 0); Query OK, 1 row affected
indexの場合
trxA
mysql> begin; mysql> select * from user_ticket where user_id = 1 and to_user_id = 100 and is_received = 0 for update;
trxB
mysql> begin; # user_id同じだとロックウェイト mysql> select * from user_ticket where user_id = 1 for update; ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction # to_user_idだけで同じ値でもロックウェイト mysql> select * from user_ticket where to_user_id = 100 for update; ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction # 先頭のuser_idが違えば、残り同じでもロック競合しない mysql> select * from user_ticket where user_id = 2 and to_user_id = 100 and is_received = 0 for update; Empty set # to_user_id違えば、ロック競合しない mysql> select * from user_ticket where to_user_id = 1 for update; +-----+---------+------------+-------------+ | id | user_id | to_user_id | is_received | +-----+---------+------------+-------------+ | 100 | 100 | 1 | 0 | +-----+---------+------------+-------------+ # user_idが同じだとロックウェイト mysql> INSERT INTO user_ticket VALUES(500, 1, 500, 1); ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction # 先頭のuser_idが違えば、残り同じでも競合しない mysql> INSERT INTO user_ticket VALUES(501, 10, 100, 0); Query OK, 1 row affected
trxA
mysql> begin; mysql> select * from user_ticket where user_id = 200 for update;
trxB
mysql> begin; mysql> INSERT INTO user_ticket VALUES(102, 10, 100, 0); # user_idの100 - 200の間でギャップロックしてる mysql> INSERT INTO user_ticket VALUES(103, 150, 100, 0); ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction # 間でなければ入る mysql> INSERT INTO user_ticket VALUES(102, 10, 100, 0); Query OK, 1 row affected
パズロラ風操作でぷよぷよ風に消す
作ってみました。
良くない書き方などあれば、教えてもらえると助かります。
ver3.0で動きます。
https://github.com/akichim21/cocos2dx-ver3-public/tree/master/PuzzleMock
Scene遷移の順番について(onEnter,onExit, onEnterTransitionDidFinish)
FirstSceneから初めて、クリックしたらSecondSceneにかわり、またクリックするとSecondSceneにかわる時のメソッドの順番を調べました。
FirstScene.h
#include "cocos2d.h" class FirstScene : public cocos2d::CCLayer { public: FirstScene(); ~FirstScene(); static cocos2d::CCScene* scene(); virtual bool init(); virtual bool ccTouchBegan(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent); virtual void onExit(); virtual void onEnter(); virtual void onEnterTransitionDidFinish(); CREATE_FUNC(FirstScene); };
FirstScene.cpp
#include "FirstScene.h" #include "SecondScene.h" using namespace cocos2d; CCScene* FirstScene::scene() { CCLOG("==========================================="); CCLOG("%s: FirstScene", __FUNCTION__); CCScene* scene = CCScene::create(); FirstScene* layer = FirstScene::create(); scene->addChild(layer); return scene; } bool FirstScene::init() { if (CCLayer::init()) { CCLOG("%s: FirstScene", __FUNCTION__); CCLabelTTF* label = CCLabelTTF::create("First Scene", "Marker Felt", 64); label->setColor(ccGREEN); CCSize size = CCDirector::sharedDirector()->getWinSize(); label->setPosition(CCPointMake(size.width / 2, size.height / 2)); this->addChild(label); this->setTouchEnabled(true); return true; } return false; } FirstScene::FirstScene() { CCLOG("%s: FirstScene", __FUNCTION__); } FirstScene::~FirstScene() { CCLOG("%s: FirstScene", __FUNCTION__); } bool FirstScene::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent) { CCLOG("%s:FirstScene", __FUNCTION__); CCDirector::sharedDirector()->replaceScene(SecondScene::scene()); return true; } void FirstScene::onExit() { CCLOG("%s:FirstScene", __FUNCTION__); CCLayer::onExit(); } void FirstScene::onEnter() { CCLOG("%s: FirstScene", __FUNCTION__); CCLayer::onEnter(); CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, 0, true); } void FirstScene::onEnterTransitionDidFinish() { CCLOG("%s: FirstScene", __FUNCTION__); CCLayer::onEnterTransitionDidFinish(); }
SecondScene.h
#include "cocos2d.h" class SecondScene : public cocos2d::CCLayer { public: SecondScene(); ~SecondScene(); static cocos2d::CCScene* scene(); bool init(); virtual bool ccTouchBegan(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent); virtual void onExit(); virtual void onEnter(); virtual void onEnterTransitionDidFinish(); CREATE_FUNC(SecondScene); };
#include "FirstScene.h" #include "SecondScene.h" using namespace cocos2d; CCScene* SecondScene::scene() { CCLOG("==========================================="); CCLOG("%s: SecondScene", __FUNCTION__); CCScene* scene = CCScene::create(); CCLayer* layer = SecondScene::create(); scene->addChild(layer); return scene; } bool SecondScene::init() { if (CCLayer::init()) { CCLOG("%s: SecondScene", __FUNCTION__); CCLabelTTF* label = CCLabelTTF::create("Second Scene", "Marker Felt", 64); label->setColor(ccGREEN); CCSize size = CCDirector::sharedDirector()->getWinSize(); label->setPosition(CCPointMake(size.width / 2, size.height / 2)); this->addChild(label); this->setTouchEnabled(true); return true; } return false; } SecondScene::SecondScene() { CCLOG("%s: SecondScene", __FUNCTION__); } SecondScene::~SecondScene() { CCLOG("%s: SecondScene", __FUNCTION__); } bool SecondScene::ccTouchBegan(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent) { CCDirector::sharedDirector()->replaceScene(FirstScene::scene()); return true; } void SecondScene::onExit() { CCLOG("%s: SecondScene", __FUNCTION__); CCLayer::onExit(); } void SecondScene::onEnter() { CCLOG("%s: SecondScene", __FUNCTION__); CCLayer::onEnter(); CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, 0, true); } void SecondScene::onEnterTransitionDidFinish() { CCLOG("%s: SecondScene", __FUNCTION__); CCLayer::onEnterTransitionDidFinish(); }
ログ:
Cocos2d: ===========================================
Cocos2d: scene: FirstScene
Cocos2d: FirstScene: FirstScene
Cocos2d: init: FirstScene
Cocos2d: onEnter: FirstScene
Cocos2d: onEnterTransitionDidFinish: FirstScene
Cocos2d: ccTouchBegan:FirstScene
Cocos2d: ===========================================
Cocos2d: scene: SecondScene
Cocos2d: SecondScene: SecondScene
Cocos2d: init: SecondScene
Cocos2d: onExit:FirstScene
Cocos2d: ~FirstScene: FirstScene
Cocos2d: onEnter: SecondScene
Cocos2d: onEnterTransitionDidFinish: SecondScene
SecondScene::initの後にFirstScene::onExitとデストラクタが呼び出されており、
シーンの狭間でFirstSceneとSecondSceneの両方がメモリにのっかてしまう問題って解消したんだろうか?時間あればもう少し実験する。
inspired by

cocos2dで作る iPhone&iPadゲームプログラミング
- 作者: Steffen Itterheim,畑圭輔,坂本一樹,加藤寛人,高丘知央,株式会社クイープ
- 出版社/メーカー: インプレスジャパン
- 発売日: 2011/06/24
- メディア: 単行本(ソフトカバー)
- 購入: 9人 クリック: 625回
- この商品を含むブログ (27件) を見る
cocos2d-xを縦持ちの画面にする
cocos2d-xはデフォルトで横持ちだが、縦の画面にしたい時の設定。
ios/RootViewController.mm
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
-returnUIInterfaceOrientationIsLandscape( interfaceOrientation );
+returnUIInterfaceOrientationIsPortrait( interfaceOrientation );
}
実機はこれだけでいいらしいが、シミュレーターは変わらないので以下も設定
ios/Info.plist
Supported interface orientations
-Item 0 => Landscape (left home button)
-Item 1 => Landscape (right home button)
+Item 0 => Portrait (bottom home button)
+Item 1 => Portrait (top home button)
Landscape => Portaitに 変えればおけ!