Flap◯ Birdを作ってみる。その2
鳥のアニメーションを表示する
予め lv1_bird_normal.png, lv1_bird_flyup.png, lv1_bird_flydown.png を用意します。
shoeboxを使って、画像を結合します。
別レイヤーを用意してそこに実装します
auto birdSprite = Sprite::create(); birdSprite->setPosition(0,0); // 中央に表示したい。相対参照なので(0,0) auto birdCache = SpriteFrameCache::getInstance(); birdCache->addSpriteFramesWithFile("images/birds.plist"); vector<string> birds = { "lv1_bird_normal.png", "lv1_bird_flyup.png", "lv1_bird_flydown.png", }; auto birdAnimation = Animation::create(); for(string str : birds){ SpriteFrame *sprite = birdCache->getSpriteFrameByName(str); birdAnimation->addSpriteFrame(sprite); } birdAnimation->setDelayPerUnit(0.25f); // 遅延 birdAnimation->setRestoreOriginalFrame(true); // 画像を元に戻す auto birdAnimate = Animate::create(birdAnimation); birdSprite->runAction(RepeatForever::create(birdAnimate)); // 繰り返す
鳥に重力を入れる
createScene関数のcreateWithPhysicsから下3行で重力の方向を入れてます。
また、body->setMassで重さ入れてます
#include "GameLayer.h" using namespace std; USING_NS_CC; static const Vec2 GRAVITY = Vec2(0, -50); // 重力 static const int BIRD_ROTATION_ANGLE = -10; // 鳥の角度 static const float BIRD_MASS = 5.0f; // Scene* GameLayer::createScene(){ auto scene = Scene::createWithPhysics(); auto world = scene->getPhysicsWorld(); world->setGravity(GRAVITY); // DEBUG mode scene->getPhysicsWorld()->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL); // 'layer' is an autorelease object auto layer = GameLayer::create(); // add layer as a child to scene scene->addChild(layer); // return the scene return scene; } bool GameLayer::init(){ if(!Layer::init()){ return false; } Vec2 origin = Director::getInstance()->getVisibleOrigin(); // 0座標 // 鳥のアニメーションの動き auto birdSprite = Sprite::create(); birdSprite->setPosition(0,0); auto birdCache = SpriteFrameCache::getInstance(); birdCache->addSpriteFramesWithFile("images/birds.plist"); vector<string> birds = { "lv1_bird_normal.png", "lv1_bird_flyup.png", "lv1_bird_flydown.png", }; auto birdAnimation = Animation::create(); for(string str : birds){ SpriteFrame *sprite = birdCache->getSpriteFrameByName(str); birdAnimation->addSpriteFrame(sprite); } birdAnimation->setDelayPerUnit(0.25f); // 遅延 birdAnimation->setRestoreOriginalFrame(true); // 画像を元に戻す auto birdAnimate = Animate::create(birdAnimation); birdSprite->runAction(RepeatForever::create(birdAnimate)); // 繰り返す auto material = PHYSICSBODY_MATERIAL_DEFAULT; material.restitution = 0.0f; material.density = 1.0f; material.friction = 1.0f; auto body = PhysicsBody::createBox(birdSprite->getContentSize(), material); body->setRotationEnable(true); body->setMass(BIRD_MASS); body->setContactTestBitmask(true); birdSprite->setPhysicsBody(body); this->addChild(birdSprite,2); return true; }
タッチイベント
上の一番最後のthis->addChildのところから
// タッチイベント auto touchEventListener = EventListenerTouchOneByOne::create(); touchEventListener->setSwallowTouches(true); // タッチイベントの上のレイヤーに影響を与えない // ラムダ関数 すべての外部変数は、もし使われれば暗黙的にコピーされる touchEventListener->onTouchBegan = [=] (Touch* touch, Event* event){ log("call touch event"); birdSprite->getPhysicsBody()->applyImpulse(Vect(0, 180.0f), Vect(0, birdSprite->getContentSize().height * (-1))); // 角度を設定. birdSprite->setRotation(BIRD_ROTATION_ANGLE); return true; // もしかしたらこちらのほうがいいかもしれない. // // TODO: 落ちてる時の角度を戻す // birdSprite->runAction(MoveTo::create(0.16f, Vec2(birdSprite->getPositionX(), birdSprite->getPositionY()+BIRD_ADD_POSITION_Y))); // // TODO: 一番上に行った時の制御 -> いるかな? // return true; }; this->getEventDispatcher()->addEventListenerWithSceneGraphPriority(touchEventListener, birdSprite);
鳥をずっと下に行かないようにする
このままだと鳥がずっと下にいくので、地面で受け止めるようにします。
setDynamicをfalseにすることで、重力の影響を受けないようにしてます。また、setContactTestBitmaskで衝突するように設定してます。
// 画面下の動き auto ground = Sprite::create("lv1_background_ground.png"); ground->setPosition(Vec2(0, (-1) * (origin.y + visibleSize.height/4))); ground->setScale(0.85714f, 1.0f); auto groundPhysicsBody = PhysicsBody::createBox(ground->getContentSize()); // 重力の影響を受けないようにする. groundPhysicsBody->setDynamic(false); groundPhysicsBody->setContactTestBitmask(true); groundPhysicsBody->setTag(1); ground->setPhysicsBody(groundPhysicsBody); this->addChild(ground, 1, 1);
Flyway Rollupをつくった
https://github.com/hachi-eiji/flyway-rollup
仕事でFlywayを使ってるのですが、いくつが問題が出てきて
- 開発が進むとファイルが増える(V100とかまでできた)
- flywayのlocationsを安定版(本番で稼働中)と開発用(自分が開発中)でディレクトリを分けた場合に、
他の人が安定版ディレクトリにpushした後に,flyway:migrateを叩いてもversion番号が進んでいるため安定版の内容が反映されない
という問題があって、毎回flyway:init → migrateを実行してると、initで消されると困るデータ(管理画面から入力されているデータ)が出てきました。
ということで、
- 最新の安定バージョンの番号のファイルにこれまで流したスクリプトファイルをマージ
- schema_versionテーブルの中からdevelopment ディレクトリの中にあるものだけ削除する.
というのを作ってみました。想定している動きとしては
一定期間でstableディレクトリをrollupする(Jenkinsなどでやらせたい)
- 開発者は各featureブランチへマージ
- flyway devCleanを実行するとschema_versionの該当レコードは消える
- lyway migrateを実行するとrollupしたスクリプト + developmentのところは実行される -> 開発可能状態
現状の課題は、rollupしたファイルのchecksumがschema_versionテーブルのchecksum列とずれること。flyway3.0のデフォルト挙動は最初にvalidateを行ってからmigrateを実行するのでエラーが起きます。
他の人達はどうやってるんだろうなぁ
Flap◯ Birdを作ってみる。その1
会社から上記ゲームをつくってみなさいというお題が出されたのでcocos2d-xで挑戦中。Unityはなんか自分の感覚に合わなかったので...
まずはインストール
- cocos2d-x 自体のインストール
- http://www.cocos2d-x.org/download からダウンロード
- Androidのダウンロード https://developer.android.com/sdk/index.html からAndriod SDKと NDKをダウンロードする
- ant install(brew install ant)
- setup.pyを実行する。パスを聞かれるので適宜入力する
お約束のHelloWorld
cocos new -p com.hachiae.cocos.helloword -l cpp HelloWorld cocos run -p ios
Andriodいれたものの案の定、エミュレータの起動が遅すぎるので一旦iOSで動作確認。
とりあえずここまではできた。
インストール時にチュートリアル的なものがあるのかなーと探してたけど探しきれなかったので、ゲーム作りながら必要な物を調べていく方針に変更。
となると、タスクを洗い出そうということで、洗い出しました。
- #1: タップすると鳥を浮き上がらせる
- #2: 障害物の当たり判定と通過判定を行う
- #3: ゲームオーバー時に得点を表示する
- #4: 背景をスクロールして動いている感じに見せる
- #7: 土管画像を表示する
- #5: ステージを無限に見せる
- #6: 背景画像を表示する
背景画像の表示
cocosコマンドを実行して作成されたHelloWorldScene.cppに記載していく。
cocos2d-xのx,y座標の開始位置は左下から開始というところを念頭に置きつつ,positionで中央座標を設定。
大学時代にC++を勉強してたけどなんかだいぶ違うw
Size visibleSize = Director::getInstance()->getVisibleSize(); // 画面解像度 Vec2 origin = Director::getInstance()->getVisibleOrigin(); // 0座標 auto backGround = Sprite::create("background.png"); backGround->setPosition(Vec2(origin.x + visibleSize.width/2, origin.y + visibleSize.height/2)); this->addChild(backGround, 0);
鳥の動いているアニメーションを表示
ShoeBoxを使って複数の画像(ここではbird000.png〜bird.002.png)を1つの画像にまとめた後にアニメーションの設定をおこなう。
// 鳥のアニメーションの動き auto birdCache = SpriteFrameCache::getInstance(); birdCache->addSpriteFramesWithFile("birds.plist"); auto birdSprite= Sprite::create(); birdSprite->setPosition(Vec2(origin.x + visibleSize.width/2, origin.y + visibleSize.height/2)); this->addChild(birdSprite,1); auto birdAnimation = Animation::create(); for (int i = 0; i < 3; i++) { auto str = __String::createWithFormat("bird00%d.png",i); SpriteFrame *sprite = birdCache->getSpriteFrameByName(str->getCString()); birdAnimation->addSpriteFrame(sprite); } birdAnimation->setDelayPerUnit(0.25f); birdAnimation->setRestoreOriginalFrame(true); // 画像を元に戻す auto birdAnimate = Animate::create(birdAnimation); birdSprite->runAction(RepeatForever::create(birdAnimate)); // 繰り返す
色んなところから情報あつめて来て理解しながらやると、やっぱり開発速度遅くなるなー
GitHubEnterpriseをどうやって仕事に使っているか
で、GitHubKaigiから1周間も経ってしまったのですが書いておきます。
スマホ向けのソーシャルゲーム(Webブラウザ)を提供しているので、頻繁にリリースが発生はしますが、定期リリース日というのが決まっているので、git-flowで開発しています。今のところこれが合ってるのかなーと
あとは個人リポジトリがあるので、そこにdotfile毎日のメモ書きやシェルを残したりしています。最初はEvernoteでもいいのかなーと思ってたのですが、Vimの重力に魂を引かれた身としては、Vimバインド使えないとちょっと面倒だったり、シェルとかプログラムだとやっぱりテキストじゃないと正直つらいっす
仕事と絡んでるところとしては、今は毎日午前中に一人カイゼン時間というのを設けてます。出社後にNewRelic/Muninを見てバグ/処理が遅い部分を潰すようにしています。ゆるいルールは
- なるべく午前中に終わらせる、終わらなかったら翌日へ。
- 一人で完結できるようにする
- できるだけテストコードを書く
ということにしてます。
長期間の開発をやっていると飽きが出てくるので気分転換がてらにやってます。
ここでもGitは大活躍で、
- ブランチ切る
- 開発/テスト書く。細かいものなので仕様わかる/テスト書きやすい
- 終わらなかったら他の仕事のブランチをcheckout
SVNの時はサーバ側へcommitするしかないので、本当にこれは革命だよなーと。ダメだったら翌日revetすればいいだけだし。
GitHub Kaigi 感想文
6/1(日)に GitHub Kaigiに参加してきました。運営のみなさん素晴らしい会議ありがとうございました!その中でいくつか全体的な感想と印象に残ったものをまとめます
全体的な感想
id:naoyaさんもクロージングで言っていたように、昔コードレビューやリモートワークやろうとしたけど挫折したけど、今はそれが簡単にできるようになったというのはすごいなと。自分も前職(SIer時代)でRedmineやコードレビューを導入しようと思ったけど、結局Redmineしか導入できなかった(しかも数名のチーム限定)という残念な経験をしたことがあります。(あとはVSSからSubversionに乗り換えたかった)
もちろんGit自体もいいものだと思ってるのですがGitHubがそれを後押ししてるのだと感じてます。ちょうどRuby on Railsが爆発的人気になったからRubyもさらに人気言語になったと同じような感じでしょうね。参加者500人(さらに参加待ちが300人とか?)というのが、それを物語っているイベントでした。
特に印象に残ったセッション
はてなブログチームの開発フローとGitHub
Redmindメイン→GitHub Issues→GitHub Issues+ホワイトボードのカンバンという変遷が「なるほど」と思わされました。ワークフローがある程度固まるとなかなか変えづらいのですが、はてなというエンジニア集団かつ少人数チームなのでドンドン変えていけるのかなーと羨ましかったです。うちのチームはサーバサイドとフロントエンドでリポジトリが別々なので、なかなかGitHub Issuesにするのは難しくJIRAとGitHubEnterpriseを紐付けてます。(個人的は見るものが少ないほうが嬉しいのでIssuesのほうがすき) はてなブログチームは複数リポジトリとかで運用してないのかしらん
pplog.net の作り方 ( 〓ω〓) ゆるふわ development on GitHub
「ポエム駆動開発」この一言につきますね。サービスのコンセプトをぶらさずに明示しておくというのが、言うは易く行うは難しですね。ここに書いてないのは枝葉だから切り捨ててもいいよねと。以前どこかで見たことがあるのですが、最初にリリースノートを書く(Amazonだったかな?)という表現をポエムぽく。うちのプランナーさんにも見てほしいです、はい。
個人的にどうGitHubEnterpriseを使ってるかは次のエントリに書きます
Agile Samurai Base Camp 2014 Re:TDDで学んだことを反映してみる
Agile Samurai Base Camp 2014 Re:TDDに参加してきました - 個人的なまとめでかいたようにJUnit+Grooy+Spockでテストのテンプレを書いてみました。
コードはこちら
https://github.com/hachi-eiji/java-spock-practice
正直Groovyは初めて書いた(ネットで落ちてる資料を調べただけ)のでぶっちゃけGroovyぽくないと思いますのでそこら辺は勘弁。今所属しているプロジェクトでほぼ同じコードのプルリクエスト投げていてレビュー中。
なぜ試してみようと思ったかというとテストコードが非常に膨れ上がってたのです。
1つのサービスクラスのテストをつくろうとした時に8割がモックの設定ばかりで非常に楽しくない。かつ、同じメソッドの境界値試験をするときにそれぞれの境界値で複数メソッドを作らないといけない。楽しくないから生産性あがらないことが多いなぁと思ってました。
そんなときにAgile Samurai Base Camp 2014 Re:TDDに参加してきて、JUnit+Grooy+Spockのデモ拝見した時にこれだったら少しは楽になるかなと。ちょうどJavaを使っててテストコードだけだったらユーザに迷惑かけないから勝手につくちゃおうと。朝会でちょっとこれ試したいからーと宣言してやっちゃいました。(もちろん本来の仕事もしてましたよ)
気をつけたのは2点。
- このやり方を強制しない
- 既存のテストクラスを何点か移植してきて、サンプルコードとコメントを結構注意深く書いた
- 強制しないとはいえやっぱり使ってほしいし、何よりテストを楽に書いてほしい。なので、最初に書くところでハードルになってはいけない(これもBase Campで再認識したところ)と思ってだいぶ細かく書きました(あとでGitHubにあげよう)。例えば、given:はテスト実行前に呼ばれるsetUpと同じでwhereはこうでとか。モックを使ったときとか。
Agile Samurai Base Camp 2014 Re:TDDに参加してきました
ブログ書くまでが勉強会ですよ。ということで。
細かいところは http://togetter.com/li/657283 にまとめてくださってるそちらを見ていただければと思います。講演資料とかもリンクがついてます
質問コーナー(お悩み相談室になってた)で直接 @t_wada さんに質問する機会があったので、「TDDはテストを書いてなぜRedから始める必要があるのか?最初にコード書いてからテスト書いてもいいのでは?」という質問をしました。自分の中ではハードルの1つだったんですよ。
回答は次のとおりでした。
テストコード自体が間違っている可能性がある。最初に小さいテストコードと仮実装して細かく作って回したほうが、テストコード自体の間違いに気づくため。
ただし「最初に書く」というのは非常に難しい。大切なのはテストコードがある状態なので後に書いても大丈夫。最初のうちはテストケースを後で書いて、慣れてきたらテストファーストで書く
あとは懇親会とかで話をしてた時に出てきた話としては
- TDDに巻き込むためには最初のサンプルが大切。トップダウンでTDDにしよういっても、どうテストを書いていいのかわからないことが多い。
- リファクタリングタスクを作らない。タスクにすると、工数を確保するための説明責任が発生するのでリファクタリングも実装フェーズに入れる
- TDDを導入するときは無理しない。自分が折れるから。(ここは質問や懇親会でも話が出てきて折れる人が多いんだなぁと実感)
- 実際のコード行とテストコードの行数が1:1ぐらいになるように作ってみる.(実際は1:3ぐらいになるかも)テストコード量が1:10とかの比率になる時はテストコード内部で重複コードがあるので見なおしてみる
- ただし、1:10になるというのが悪いとは思わないとおっしゃってた方もいるので、一概には言えないのかもしれない
- テストもプログラムの一部なのでそこを意識してメンテしていく(テストコード自体のリファクタリングとか)
- 複雑なものはコントローラーのEnd-To-Endでテストする
- うちの会社は横のつながりが弱いのでなにか共有する方法作ったほうがいい
他に気になったのはJUnit+GroovyとSpockの組み合わせ。調べる時間が必要だけど、だいぶシンプルにテストコードが書けそうな気がしています。次の開発のあまり難しくないところで試してみたい
TDDのスペシャリストである@t_wadaさんと直接お話しできたのは非常に嬉しかったし、事例を聞けたのは非常にためになりました。何か実践してBootcampに資産を持って無事に帰ってくる事ができれば幸いです。