Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
QMLでFlappyBirdを作ろう
Search
Yuto Tokunaga
September 03, 2015
Programming
0
1.5k
QMLでFlappyBirdを作ろう
creating Flappy Bird by QML
Yuto Tokunaga
September 03, 2015
Tweet
Share
More Decks by Yuto Tokunaga
See All by Yuto Tokunaga
SyaroNote
yuntan
0
1.9k
Haskellでbrainfckインタプリタを書きたかった話
yuntan
0
860
KC3 2014 懇親会LT 「Let It ʕ ◔π◔ʔGo」
yuntan
0
140
QMLとモバイル
yuntan
0
890
KC3 2013 懇談会LT 「QML + C++で楽しい!」
yuntan
1
190
Other Decks in Programming
See All in Programming
nekko cloudにおけるProxmox VE利用事例
irumaru
3
450
LLM Supervised Fine-tuningの理論と実践
datanalyticslabo
7
1.5k
Stackless и stackful? Корутины и асинхронность в Go
lamodatech
0
930
「とりあえず動く」コードはよい、「読みやすい」コードはもっとよい / Code that 'just works' is good, but code that is 'readable' is even better.
mkmk884
3
680
Amazon S3 NYJavaSIG 2024-12-12
sullis
0
110
tidymodelsによるtidyな生存時間解析 / Japan.R2024
dropout009
1
810
テストコード書いてみませんか?
onopon
2
200
StarlingMonkeyを触ってみた話 - 2024冬
syumai
3
280
アクターシステムに頼らずEvent Sourcingする方法について
j5ik2o
4
350
週次リリースを実現するための グローバルアプリ開発
tera_ny
1
110
fs2-io を試してたらバグを見つけて直した話
chencmd
0
240
KMP와 kotlinx.rpc로 서버와 클라이언트 동기화
kwakeuijin
0
180
Featured
See All Featured
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
28
2.1k
Become a Pro
speakerdeck
PRO
26
5k
Designing Experiences People Love
moore
138
23k
XXLCSS - How to scale CSS and keep your sanity
sugarenia
248
1.3M
Done Done
chrislema
182
16k
Facilitating Awesome Meetings
lara
50
6.1k
jQuery: Nuts, Bolts and Bling
dougneiner
61
7.6k
The Cult of Friendly URLs
andyhume
78
6.1k
[RailsConf 2023] Rails as a piece of cake
palkan
53
5k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
226
22k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
111
49k
The Invisible Side of Design
smashingmag
298
50k
Transcript
QML で Flappy Bird を 作ろう 1 / 59
QML の紹介 UI記述用言語 Component を組み合わせてUIを組み上げていく Animation, State などを 宣言的に 記述できる
プロパティバインディングによる状態の管理 書いたアプリは Windows, Mac, Linux, Android, iOS どこでも動 く QMLの日本語のイントロダクション QMLプログラミング入門へよ うこそ! — Getting started QML programming Japanese translation 2014.04.05 ドキュメント 2 / 59
QML 実行環境の用意 1. http://www.qt.io/download-open-source/ からQtのインス トーラをダウンロードし、インストールする. 2. https://goo.gl/NNWeq6 からZIPファイルをダウンロード し、解凍
3 / 59
QML編集環境の用意 Sublime TextではQMLパッケージをインストールするとシンタッ クスハイライトされます Qtをインストールされた方はqmlproject ファイルをQtCreatorで開 いてください 4 / 59
Flappy Bird を作ろう 5 / 59
こんなゲーム 参考: あまりの難易度で人気爆発のFlappy Bird。制作者によるゲ ーム3本が一気にトップ10入り | TechCrunch Japan 6 /
59
サンプルのダウンロード https://goo.gl/1hSiDG からダウンロード /qml/main.qml をqmlscene.exe にドラッグ・アンド・ドロップして起 動. /step[2-5]/ はスライドの各ステップに対応しています./qml/ は完
成形のソースコードが入っています. 自分で一から作りたい場合は/qml/ と同じ階層に新しくフォルダを 作成してください. 7 / 59
8 / 59
9 / 59
Flappy Bird のつくりかた 1. リソース(鳥とか障害物の画像)を用意する 2. アプリケーションの基本部分を作る 3. 鳥を飛ばす 4.
障害物を作る・当たりを判定する 5. 鳥を作りこむ 6. ゲームのシーンを作る 10 / 59
Step1 リソースの用意 assets フォルダに用意してあります. 11 / 59
Step2 アプリケーションの基本部分を作る QMLのアプリで一番基本の部分を作る.main.qml に以下の内容を書 く. import QtQuick 2.2 import QtQuick.Controls
1.3 ApplicationWindow { title: "Flappy Bird" width: 288 height: 512 visible: true Image { anchors.fill: parent source: "../assets/bg_day.png" } } 12 / 59
13 / 59
QMLの基本文法 コンポーネントをネストして画面を作り上げる コンポーネントにはプロパティがある コンポーネントには親子関係がある 基本的に子の要素は親の内側にある この場合Image がApplicationWindow の子になっている 14 /
59
anchors について アンカーを用いて親に対する子の位置を決める. 15 / 59
anchors.fill: parent 子が親を覆う anchors.centerIn: parent 子が親の中央に位置する anchors.right: parent.right 親の右端に子が位置する anchors.right:
item1.left item1の左に位置する(右にitem1がく る) 16 / 59
マージンについて コンポーネント同士の間隔を開けたい時,マージンを使う. 17 / 59
anchors.fill: parent; anchors.margins: 50 四方の間隔を50px空けて 親の中央に位置する anchors.right: parent.right; anchors.rightMargin: 50
間隔を50px空け て親の右端に位置する anchors.right: item1.left; anchors.rightMargin: 50 item1との間隔を 50px空ける 18 / 59
問題 19 / 59
import QtQuick 2.2 Rectangle { width: 500; height: 500 color:
"red" Rectangle { width: 200; height: 200 color: "blue" anchors { left: parent.left bottom: parent.bottom leftMargin: 50 bottomMargin: 100 } } } リサイズしてもアンカーで定義した位置関係が変わらないことが 確認できる 20 / 59
QMLにおけるコンポーネントの切り分け ゲームに含まれる全てのコンポーネントを一つのファイルに記述 すると管理が大変なので,複数のコンポーネントに切り分けてい く. main.qml と同じ階層にGame.qml を作成する. import QtQuick 2.2
Item { id: game Image { id: land anchors.bottom: parent.bottom width: sourceSize.width * 2 height: sourceSize.height * 2 source: "../assets/land.png" } } 21 / 59
Item は全ての目に見えるコンポーネントの派生元.全ての目に見 えるコンポーネントはItem から派生している. QMLではこのように,もともとあるコンポーネントから派生した コンポーネントを作り出すことができる(オブジェクト指向にお ける継承みたいな感じ).派生させるときに新たにプロパティを 追加したり,メソッドを追加したりもできる(オブジェクト指向 で継承させるときに変数や関数を追加するのと同じような感 じ).
コンポーネントを切り分けておくと,一回定義したコンポーネン トを繰り返し使うことができるようになる. 例)ボタン // Button.qml Rectangle { color: "gray"; width: 150; height: 90 Text { anchors.centerIn: parent; text: "label" } MouseArea { anchors.fill: parent; onClicked: ... } } 22 / 59
作成したGameコンポーネントをゲームに追加する.main.qml に以 下を子として追加する. Game { id: game anchors.fill: parent Keys.onEscapePressed:
close() focus: true } 23 / 59
Step3 鳥を飛ばす 1. 鳥を作る 2. ぴょんぴょんさせる 24 / 59
鳥を作ろう main.qml と同じ階層にEntity フォルダを作り,以下をEntity/Bird.qml として保存. import QtQuick 2.2 AnimatedSprite {
id: bird width: 34; height: 24 property real v: 0 source: "../../assets/bird_yellow.png" frameDuration: 600 frameWidth: 17 frameHeight: 12 frameCount: 3 } AnimatedSprite については後で説明します. 25 / 59
ぴょんぴょんさせる スペースキーが押されたらぴょんぴょんするようにしたい. 時間,位置,速度,加速度の変数を用意して,一定時間ごとにそ れらの変数を更新する. 速度に重力加速度を足す,位置に速度を足す スペースキーを押されたら速度を変化させる じつに原始的 26 / 59
変数を追加する QMLではプロパティが変数にあたる. 以下をGame に追加. property int t: 0 // 時間
property real g: 0.5 // 重力加速度 property real pyonV: 8 // スペースキーを押した時に加えられる速度 27 / 59
一定時間ごとに処理を行う QMLのTimer コンポーネントを利用する.これは指定した時間ごと に指定された処理を行う. Timer { id: timer running: true
interval: 33 // 33msecごとに処理を行う repeat: true onTriggered: { // 実行される処理 update() t++ // 時間の変数を更新 } } 28 / 59
鳥を落下させる 一定時間ごとに鳥を下方向に動かす. function update() { // fall down bird.v +=
g bird.y += bird.v } Bird { id: bird x: (parent.width - width) / 3 } y, v, g は下方向に正の値を取る. 29 / 59
ゲームを起動すると鳥が下に落ちていく様子が確認できる. 30 / 59
お気づきでしょうか,これはJavaScriptです. QMLにはJavaScript(ES5)のロジックを埋め込める.ゲームロジ ックはJavaScriptで記述していくことになる. QMLでは最初に紹介したアンカー以外にx y を指定してコンポー ネントを移動させることができる. QMLのid コンポーネントに名前を付けるにはid を使う.Bird
コンポーネン トにbird というIDを付けておくと,例えば位置Yはbird.y というよ うに参照できる. 先ほどBird コンポーネントを作った時にv という実数型のプロパテ ィを作った.ここではGame からBird のv を参照し値を変更してい る. 31 / 59
プロパティに型が付いているのはなぜ? QMLはC++のクラスをベースに作られている.また,今回は紹介 しないがC++で定義したコンポーネントを利用することができた りする.端的に言えばC++との連携のために型付けが必要になっ ている. 32 / 59
スペースキーで速度を変える Keys.onSpacePressed: pyon() function pyon() { bird.v = -pyonV }
キーボード入力に反応するにはKeys を使う. 33 / 59
Step4 障害物を作る・当たりを判定する パイプ(障害物)を作る パイプは上部と下部の間に隙間が開いていて,隙間には当たり判 定がない(というか隙間を通ればセーフという判定) ステージ上にパイプを適当に配置する パイプに鳥が衝突したらアウト 34 / 59
パイプを作る 隙間の上端と隙間部分の長さを指定することができるパイプのコ ンポーネント./step4/Entity/Pipe.qml を参照. ステージを作る パイプを何本かステージ上に用意しておく (clear ) 時間経過に伴い左にずれていく (update
) 画面の左端までパイプがずれて見えなくなったら右端に配置し直 す (update ) パイプの隙間は乱数で適当に設定する (setPipeRandom ) パイプと鳥が衝突していないか判定する (isClear ) パイプをくぐり抜けたか判定する (scoreUp ) /step4/Entity/Stage.qml を参照 35 / 59
Game にStage を配置して,これらの関数を呼び出す.start goOver を 追加,update を修正. 鳥の初期位置設定,ステージの初期化,フラグ変更 (start )
ステージ上の障害物や地面・天井に衝突すればゲーム終了 (update ) パイプの隙間を抜けたらスコアを上げる (update ) ゲームオーバーしたらフラグを戻し鳥を停止 (goOver ) /step4/Game.qml 参照 36 / 59
簡単な状態管理 isPlaying プロパティで鳥の飛行・静止を,score でスコアを管理す る property bool isPlaying: false property
int score: 0 isPlaying がtrue の時のみタイマーを動かしたい. 37 / 59
プロパティバインディング プロパティを別のプロパティと連携して値を連動させる機能. QMLの一番の目玉機能といってもよい. import QtQuick 2.2 Rectangle { width: 300;
height: 300 color: "red" Rectangle { width: parent.width / 2 height: parent.height / 3 color: "blue" } } ウィンドウをリサイズ(赤の四角の大きさを変える)と青の四角 の大きさが連動して変わることが確認できる. 38 / 59
Timer のrunning プロパティにisPlaying をバインディングする.start やgoOver でisPlaying を変更してやるとタイマーが連動する. Timer { id:
timer running: isPlaying ... } 今まで使っていた: はバインディングの意味.JavaScriptコード内 で用いている= (代入)とは意味合いが異なる. 39 / 59
でばっぐ console.debug を使おう console.debug("update score: " + score); qmlscene を実行しているコンソールでログが確認できる.
40 / 59
Step5 鳥を作りこむ スペースキーを押した後と落下中で羽ばたきの速さを変えたい ゲームオーバーしたら死んだみたいな感じにしたい 羽ばたいているときは1-3の画像を使う,死んだら4つ目の画像. 41 / 59
AnimatedSprite Bird で使っていたもの.フレームが連続して並んだ画像を読み込 んで,アニメーションする. SpriteSequence AnimatedSprite に状態を管理できる機能を加えたもの. 42 / 59
import QtQuick 2.2 SpriteSequence { id: bird; width: 34; height:
24 property real v: 0 Sprite { name: 'flying' source: "../../assets/bird_yellow.png" frameDuration: 600 frameWidth: 17 frameHeight: 12 frameCount: 3 } Sprite { name: 'drafting' source: "../../assets/bird_yellow.png" frameDuration: 50 frameWidth: 17 frameHeight: 12 frameCount: 3 } Sprite { name: 'die' source: "../../assets/bird_yellow.png" frameX: 51 frameWidth: 17 frameHeight: 12 frameCount: 1 } } 43 / 59
Sprite が各状態に相当する.bird.jumpTo('flying') のようにして状態 遷移する. property bool isUpdrafting: false function start()
{ ... bird.jumpTo('flying') } function pyon() { bird.v = -pyonV isUpdrafting = true // updraftCount = 0; bird.jumpTo('drafting') } function update() { ... if (isUpdrafting && bird.v > 0) { isUpdrafting = false bird.jumpTo('flying') } } function goOver() { ... bird.jumpTo('die') } 44 / 59
45 / 59
Step6 ゲームのシーンを作る このゲームでは3つのシーンを用意する. 1. タイトル画面.ゲームをスタートするためのボタンがある. 2. ゲーム画面.さっき作ったやつ. 3. スコア確認画面.スコアボードとタイトルに戻るボタン. 46
/ 59
こんな感じの構成にしたい. Game が各シーンを管理する. 子から親へとゲームの終了,タイトル画面に戻るなどの画面遷移 の要求を伝えるにはどうすればよいか? 47 / 59
シグナル・ハンドラ シグナルとは,QMLでイベントを通知するための仕組み.ハンド ラはイベントに反応して処理をするための仕組み. イベント(マウスでクリックされた,マウスが移動した,ウィン ドウのサイズが変わった,などなど)が発生した時に何らか処理 を行いたいが,プロパティバインディングではそれができない場 合,シグナル・ハンドラを使う. ハンドラにはJavaScriptのコードを指定できる. Button {
onClicked: console.debug("clicked!") } 48 / 59
プロパティの変化を通知するシグナル プロパティを定義した際,自動的にシグナルが定義される.例え ばStep4で作成したisPlaying プロパティにもシグナルが割り当てら れている.この場合onIsPlayingChanged というシグナルが作成されて いる. onIsPlayingChanged: console.debug("isPlaying changed")
問題 プロパティバインディングの説明で作成したRectangle のonWidthChanged シグナルにデバッグメッセージを出力するハンド ラを割り当てて,ウィンドウをリサイズしてみよう. 49 / 59
任意の出来事を通知するシグナル プロパティの定義とは別にシグナルを作成することもできる. // ゲームオーバーを通知するためのシグナル signal gameOver(int score) シグナルには引数を指定できる.対応するハンドラはonGameOver と いう名称になる.
onGameOver: { console.log("score: ", score); result.score = score; game.state = 'result'; } ハンドラ内で引数を利用することができる.プロパティの定義と 同様,引数には型付けが必要.(JavaScriptを使っていると不自然 に思うだろうけど) 50 / 59
タイトル画面 /step6/Scene/Title.qml 参照. スコアボード画面 /step6/Scene/Result.qml 参照. ゲームプレイ画面 /step6/Scene/Play.qml 参照.いままでGame.qml に書いていた内容をそ
のまま移動している. 51 / 59
ボタンを作りたいときはQtQuick.Controls に含まれるButton を使うの が楽.ただスタイルの変更がちょっとややこしかったりする. Button { width: 104; height: 58
onClicked: beginPlay() style: ButtonStyle { background: Image { source: "../../assets/play.png" } } } 52 / 59
状態の管理 その2 ゲームの現在の状態によって表示する画面を切り替えたい. プロパティバインディングとJavaScriptコードでの条件分岐でも 対応できるが,管理したい状態が増えると式が煩雑になったり, 同じような条件分岐があちこち点在するようになり,管理が大変に なる. color: mouseArea.containsMouse ?
"blue" : "red" (プロパティバインディングは非常に強力で,JavaScriptの式に も対応している.width: height + 30 などもそうだが,JavaScript の式中に含まれるプロパティの値が変わると,その式を再評価す る仕組みになっている.) 53 / 59
states state を用いた状態の管理 コンポーネントにもとから備わっているstates プロパティに各状態 を記述していく.State の中にはStateChangeScript PropertyChanges な ど,その状態に切り替わった時に実行する動作を記述できる.
初期状態はstate で定義する.game.state = 'play' とすることによ り,状態の切り替えを行える. state: 'title' states: [ State { name: "title" }, State { name: "play" StateChangeScript { script: play.start() } }, State { name: "result" } ] 54 / 59
最後にGame にTitle Play Resutl を配置して完了!お疲れ様でした. Title { id: title anchors.fill:
parent visible: game.state === 'title' onBeginPlay: game.state = 'play' } Play { id: play anchors.fill: parent visible: game.state !== 'title' onGameOver: { console.log("score: ", score); result.score = score; game.state = 'result'; } } Result { id: result anchors.fill: parent visible: game.state === 'result' onBackToTitle: game.state = 'title' } 55 / 59
おまけ スペースキーだけでなくクリックにも対応しよう! ランダムに違う鳥が出るようにしてみよう! ランダムに違うパイプが出るようにしてみよう! 背景をランダムに変更しよう! スコアボードを作ろう! スコアが上がるにつれ難しくなるようにしよう! 衝突判定を厳しくしよう! 効果音を付けよう! 他思いつくこと何でも
56 / 59
まとめ コンポーネント プロパティ アンカー プロパティバインディング 状態管理 シグナル・ハンドラ 57 / 59
ほかにも アニメーションの宣言的記述 音声・動画の再生 ピクセルシェーダー,頂点シェーダーの使用 ウェブページの表示 C++での拡張 3次元的表現 パーティクル HTML5 Canvas
などなど 58 / 59
References How to Make a Flappy Bird Game with V-Play
| V-Play 2.4 | V- Play Game Engine Mobile - Flappy Bird - Version 1.2 Sprites - The Spriters Resource QMLプログラミング入門へようこそ! — Getting started QML programming Japanese translation 2014.04.05 ドキュメント Qt QuickではじめるクロスプラットフォームUIプログラミング 59 / 59