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
macOS でできる リアルタイム動画像処理
biacco42
9
2.3k
とにかくAWS GameDay!AWSは世界の共通言語! / Anyway, AWS GameDay! AWS is the world's lingua franca!
seike460
PRO
1
790
カラム追加で増えるActiveRecordのメモリサイズ イメージできますか?
asayamakk
4
2k
What’s New in Compose Multiplatform - A Live Tour (droidcon London 2024)
zsmb
1
450
リアーキテクチャxDDD 1年間の取り組みと進化
hsawaji
1
200
Quine, Polyglot, 良いコード
qnighy
4
630
Java ジェネリクス入門 2024
nagise
0
700
Contemporary Test Cases
maaretp
0
100
2024/11/8 関西Kaggler会 2024 #3 / Kaggle Kernel で Gemma 2 × vLLM を動かす。
kohecchi
4
760
[PyCon Korea 2024 Keynote] 커뮤니티와 파이썬, 그리고 우리
beomi
0
120
讓數據說話:用 Python、Prometheus 和 Grafana 講故事
eddie
0
390
破壊せよ!データ破壊駆動で考えるドメインモデリング / data-destroy-driven
minodriven
17
4.3k
Featured
See All Featured
GraphQLの誤解/rethinking-graphql
sonatard
67
10k
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
93
16k
Rails Girls Zürich Keynote
gr2m
93
13k
Build The Right Thing And Hit Your Dates
maggiecrowley
33
2.4k
Agile that works and the tools we love
rasmusluckow
327
21k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
250
21k
Navigating Team Friction
lara
183
14k
Embracing the Ebb and Flow
colly
84
4.5k
The Illustrated Children's Guide to Kubernetes
chrisshort
48
48k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
26
1.4k
Designing Experiences People Love
moore
138
23k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
27
820
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