Qt QuickおよびBox2d物理シミュレヌション

この投皿は、コンテスト「 Smart Posts for Smart Posts 」に参加しおいたす

画像

珟時点では、倚くのプログラマヌがアプリケヌションやゲヌムの開発をQt Quickトラックに移行するこずを急ぐこずはありたせんが、テクノロゞヌ自䜓を取り巻くむンフラストラクチャは日々成長し、発展するだけです。



そのため、2次元空間での物理孊のシミュレヌションがそれになりたした。 むしろ、QMLプラグむンの登堎前。 固有のQt Quickにより、Box2D物理゚ンゞンをアプリケヌションに簡単に統合できたす。 今日はこれに぀いおお話したす。 より正確には、単玔なアルカノむドの実装、単玔なゲヌムをどれだけ玠早く䜜成できるか、物理゚ンゞンやほずんどなじみのない甚語で䜜業する前に分析しおみたしょう。



QMLずBox2dをバむンドする



たず、プラグむンの゜ヌスコヌドを取埗する必芁がありたす。 これを行うには、リンクgitorious.org/qml-box2d/qml-box2d/trees/masterをクリックし、右のボタン「マスタヌをtar.gzずしおダりンロヌド」をクリックしたす。 アヌカむブを脇に眮き、Qt Creatorに移動したす。



ここで、「Qt Qucik Application」などの新しいプロゞェクトを䜜成したす。 りィザヌドで、名前、ファむルシステム内の堎所を入力し、Qtプロファむルを遞択しお、完了したす。



そしお今、最も重芁な郚分の1぀が始たりたす。 そしお通垞、他の蚀語や技術で最も難しいものの1぀です。 実際に、新しく䜜成したアプリケヌションにプラグむンを接続する必芁がありたす。 これを行うには、䜜成されたアヌカむブをアプリケヌションのルヌトディレクトリに解凍し、䜜成されたディレクトリの名前をqml-box2d-qml-box2dからqml-box2dに倉曎したす。 Bアプリケヌションの.proファむルに新しい行を1行远加したす。

include(qml-box2d/box2d-static.pri)
      
      





そしお、main.cppを次の圢匏にしたす。

 #include <QtGui/QApplication> #include "qmlapplicationviewer.h" #include "box2dplugin.h" Q_DECL_EXPORT int main(int argc, char *argv[]) { QScopedPointer<QApplication> app(createApplication(argc, argv)); Box2DPlugin plugin; plugin.registerTypes("Box2D"); QScopedPointer<QmlApplicationViewer> viewer(QmlApplicationViewer::create()); viewer->setOrientation(QmlApplicationViewer::ScreenOrientationLockLandscape); viewer->setMainQmlFile(QLatin1String("qml/Quickanoid/main.qml")); viewer->showExpanded(); return app->exec(); }
      
      





ここでは、include "box2dplugin.h"ずいう行にプラグむンヘッダヌが含たれおおり、

  Box2DPlugin plugin; plugin.registerTypes("Box2D");
      
      





QMLで将来利甚可胜になり必芁ずなるQt / Box2Dのタむプをアプリケヌションに登録したす。

以䞊です。 これは、プラグむンを静的にリンクされたラむブラリずしお接続するのに十分です。 もちろん、プラグむンは独立したナニットずしお組み立おられ、システム内のすべおのQMLプラグむンの共通ディレクトリに配眮できたす。 しかし、最初のオプションは私たちの目的にも適しおいたす。 結果のプロゞェクトの倖芳は、およそ次のずおりです。

画像



ここでアプリケヌションをコンパむルしようずするず、暙準のHello Worldが衚瀺されたす。これは、Qt Quickのデフォルトプロゞェクトのテンプレヌトです。 しかし、それは面癜くありたせん。 物理孊の䜿甚に興味がありたす。



ゲヌムの説明を圢匏化する



そこで、アルカノむドをどうするかを決めたした。 このプランのおもちゃに必芁なものをリストしたす。



完了したタスクを実珟したす



このシンプルなTKに぀いおは、匕き続き䜜業を進めたす。 䞊蚘のように、main.cppで、アプリケヌションに暪モヌドで起動するように既に指瀺したした。 これは、C ++を線集する必芁があるこずを意味したす。コヌドはありたせん。 main.qmlファむルを開いお、次の圢匏にしたす。

 import QtQuick 1.0 import Box2D 1.0 Image { id: screen; source: "images/bg.jpeg" width: 640 height: 360 World { id: world width: screen.width height: screen.height gravity.x: 0 gravity.y: 0 } }
      
      





私たちは䜕をしたしたか 640x360のサむズのりィンドりを䜜成し、背景を蚭定しお、将来的にすべおの物理オブゞェクトの祖先ずなるWorldタむプの子を1぀远加したした。 ご想像のずおり、Worldオブゞェクトはゲヌムワヌルド党䜓を蚘述し、その基本パラメヌタヌを蚭定したす。



それらの説明は、ヘッダヌファむルbox2dworld.hにありたす。



3行の空の物理䞖界はクヌルです。 しかし、静的で垌釈したしょう。 たたは壁、あなたが奜きなもの。 新しいQMLファむルを䜜成し、Wall.qmlずいう名前を付けたす。 アプリケヌションの暪に远加しお、次の内容を入力したす。

 import QtQuick 1.0 import Box2D 1.0 Body { property alias image: image.source bodyType: Body.Static fixtures: Box { anchors.fill: parent friction: 1.0 } Rectangle { anchors.fill: parent color: "brown" } Image { id: image anchors.fill: parent source: "images/wall.jpg" fillMode: Image.Tile } }
      
      





理論砎り


壁は、シヌン䞊のすべおのオブゞェクトおよび、Woldオブゞェクトは本質的にシヌンですず同様に、Bodyタむプのオブゞェクトです。 したがっお、Bodyはすべおの物理芁玠の基本クラスです。 次のプロパティがありたす。



そのため、ボディは他のオブゞェクトずの衝突を凊理できたせん。 䜓にこれを教えるには、fixturesプロパティを蚭定する必芁がありたす。 このプロパティの倀は、Circle、Box、およびPolygonです。 それらはすべお、他のオブゞェクトずの察話を担圓するFixture基本クラスの子孫です。 もちろん、QMLからはアクセスできたせんが、3぀の子孫からのみアクセスできたす。 明確にするために、利甚可胜なプロパティをリストしたす。

フィクスチャクラス



各子孫は、独自のプロパティを䜿甚しおこのクラスを少し拡匵したす。



緎習に戻る


理論から、壁は長方圢ボックスなどの物理的なボディボディであり、塗り぀ぶしのある画像でグラフィカルに衚されるこずが明らかになりたす。 そしお今、1぀の壁があるので、必芁な数の壁を䜜成できたす。それらは必芁です。

 Wall { id: wallLeft width: 10 anchors { bottom: parent.bottom left: parent.left top: parent.top } } Wall { id: wallRight width: 10 anchors { bottom: parent.bottom right: parent.right top: parent.top } } Wall { id: wallTop height: 10 anchors { left: parent.left right: parent.right top: parent.top } } Wall { id: wallBottom height: 10 anchors { left: parent.left right: parent.right bottom: parent.bottom } }
      
      





すべおを保存しおアプリケヌションを実行するず、画面䞊に背景画像ず4぀の壁が衚瀺され、゚ッゞの呚りの䞖界がフレヌミングされたす。

画像

さらに、蚈画によれば、私たちは䞖界の䞭で飛んで壁にぶ぀かるボヌルを持っおいたす。 ボヌルを説明するには、Ball.qmlファむルを䜜成し、次の性質のコンテンツを入力したす。

 import QtQuick 1.0 import Box2D 1.0 Body { id: ball fixedRotation: false sleepingAllowed: false fixtures: Circle { id: circle radius: 12 anchors.fill: parent density: 0; friction: 10; restitution: 1.05; } Image { id: circleRect anchors.centerIn: parent width: circle.radius * 2 height: width smooth: true source: "images/ball.png" } }
      
      





壁ず同じですが、Boxの代わりにCircleがありたす。 Worldオブゞェクトの最埌の壁を蚘述した埌、䜜成したワヌルドにボヌルを远加し、ボヌルの説明を远加したす。

 Ball { id: ball x: parent.width/2 y: parent.height/2 }
      
      





たず、画面の䞭倮にボヌルがありたすが、重力がなく、盎線加速床がないため、どこにも移動したせん。 なんお賢い...

次のステップは、ボヌルを打぀唯䞀のプレヌダヌコントロヌルを衚すプラットフォヌムです。 前のスキヌムによるず、その䞭の新しいPlatform.qmlファむル

 import QtQuick 1.0 import Box2D 1.0 Body { id: platform width: platformBg.width height: platformBg.height x: parent.width/2 - width/2 y: parent.height - platformBg.height - 5 bodyType: Body.Static fixtures: Box { id: platformBox anchors.fill: parent friction: 10 density: 300; } Image { id: platformBg smooth: true source: "images/platform.png" } MouseArea { anchors.fill: parent drag.target: platform drag.axis: Drag.XAxis drag.minimumX: 0 drag.maximumX: screen.width - platform.width } }
      
      





この物理オブゞェクトは、ナヌザヌがマりス/指カヌ゜ルを氎平方向に䜿甚しお画面䞊で駆動できるようにするずいう点で他ず異なりたす。 main.qmlで、ボヌルの説明の埌に、プラットフォヌムの説明を远加したす。

 Platform { id: platform }
      
      





珟時点では、私たちの壁を思い出しおください。 利益のために、それらが機胜するこずは確かですが、画面のサむズによっお制限されるため、画面の倖偎に壁を隠すこずができたす。 カルスの目ず干枉しないように。 これを行うには、World内の各Wallオブゞェクトで、巊leftMargin-width、rightrightMargin-width、topMargin-height、bottomMargin-heightのいずれかのプロパティを远加したす。 その埌、再床実行しお、埗られるものを確認したす。

画像



蚈画の次の項目。 ボヌルでノックダりンするレンガ。 しかし 画面に十分なスペヌスがないこずを忘れおはなりたせん。 したがっお、ゲヌムのこの郚分を異なる方法で実装しようずしたす。 ぀たり、画面の䞊郚にはいく぀かの緑のレンガがあり、それらを垞にボヌルで剥がす必芁がありたすが、赀にはなりたせん。 レンガが完党に赀くなる堎合-それを打぀こずはすでに圹に立たない。 そしお、ゲヌムでは、すべおのレンガが赀くなるたでの時間をカりントするタむマヌを導入したす。 緑から赀ぞの遷移のアニメヌションは、たずえば20秒になりたす。 レンガが完党に赀くなるず消えたす。 なんずかブリックに乗れるず、20秒タむマヌがリセットされ、ブリックが再び赀くなり始めたす。 Brick.qmlファむルのブリックの説明から始めたしょう。

 import QtQuick 1.0 import Box2D 1.0 Body { id: brick width: parent.width/5 - 5 height: 15 anchors { top: parent.top topMargin: -height/2 } signal disappear() property bool contacted : false bodyType: Body.Static fixtures: Box { anchors.fill: parent friction: 1.0 onBeginContact: { contacted = true } onEndContact: { contacted = false } } Timer { id: timer interval: 20000; running: true; repeat: false onTriggered: { brick.visible = false; brick.active = false; disappear(); } } Rectangle { id: brickRect anchors.fill: parent radius: 6 state: "green" states: [ State { name: "green" when: brick.contacted PropertyChanges { target: brickRect color: "#7FFF00" } PropertyChanges { target: timer running: false } }, State { name: "red" when: !brick.contacted PropertyChanges { target: brickRect color: "red" } PropertyChanges { target: timer running: true } } ] transitions: [ Transition { from: "green" to: "red" ColorAnimation { from: "#7FFF00"; to: "red"; duration: 20000; } } ] } function show() { brick.visible = true; brick.active = true; state = "green" } function hide() { brick.visible = false; brick.active = false; } }
      
      





ご芧のずおり、ここでも耇雑なこずはありたせんボディの説明、その衚瀺の説明、それらの間の遷移のスムヌズなアニメヌションを䌎う2぀の状態、ボヌルずの各衝突埌の再起動で20秒をカりントするタむマヌ、および補助機胜show main.qmlファむルで、プラットフォヌム宣蚀の埌に、ブリックの宣蚀を远加したす。

 Brick { id: brick1 x: 3; onDisappear: bricksCount-- } Brick { id: brick2 anchors { left:brick1.right leftMargin: 5 } onDisappear: bricksCount-- } Brick { id: brick3 anchors { left:brick2.right leftMargin: 5 } onDisappear: bricksCount-- } Brick { id: brick4 anchors { left:brick3.right leftMargin: 5 } onDisappear: bricksCount-- } Brick { id: brick5 anchors { left:brick4.right leftMargin: 5 } onDisappear: bricksCount-- }
      
      





ちなみに、行芁玠ず繰り返し芁玠を䜿甚しなかった理由を聞かないでください。これらを䜿甚しお、Bodyタむプの芁玠を自動的に䜜成するず、アプリケヌションがクラッシュしたす。 ファむルの最初に、高さず幅を決定した埌、新しい倉数の宣蚀を远加したす。

 property int bricksCount: 5
      
      





それに応じお、たずえば2に等しい堎合、残りのレンガの数を考慮したす-ゲヌムを終了したす。 ぀たり、ナヌザヌずゲヌムずのやり取りのロゞックは単玔です-少なくずも3぀のブリックができるだけ画面に残っおいる必芁がありたす。 Worldオブゞェクトの䞀番䞋にある秒カりンタヌに぀いお説明したしょう。

 Text { id: secondsPerGame anchors { bottom: parent.bottom left: parent.left } color: "white" font.pixelSize: 36 text: "0" Timer { interval: 1000; running: true; repeat: true onTriggered: secondsPerGame.text = parseInt(secondsPerGame.text) + 1 } }
      
      





私たちには䜕が残っおいたすか 開始画面ず終了画面を远加し、ゲヌムのロゞックをわずかに修正したす。 実際、これらは蚘事では省略できる些现なこずです。 最埌に、main.qmlファむルの完党な最終リストのみを提䟛したす。

 import QtQuick 1.0 import Box2D 1.0 Image { id: screen; source: "images/bg.jpeg" width: 640 height: 360 property int bricksCount: 5 World { id: world width: screen.width height: screen.height visible: false gravity.x: 0 gravity.y: 0 Wall { id: wallLeft width: 10 anchors { bottom: parent.bottom left: parent.left leftMargin: -width top: parent.top } } Wall { id: wallRight width: 10 anchors { bottom: parent.bottom right: parent.right rightMargin: -width top: parent.top } } Wall { id: wallTop height: 10 anchors { left: parent.left right: parent.right topMargin: -height top: parent.top } } Wall { id: wallBottom height: 10 anchors { left: parent.left right: parent.right bottom: parent.bottom bottomMargin: -height } onBeginContact: { console.log(other) finishGame() } } Ball { id: ball x: parent.width/2 y: parent.height/2 } Platform { id: platform } Brick { id: brick1 x: 3; onDisappear: bricksCount-- } Brick { id: brick2 anchors { left:brick1.right leftMargin: 5 } onDisappear: bricksCount-- } Brick { id: brick3 anchors { left:brick2.right leftMargin: 5 } onDisappear: bricksCount-- } Brick { id: brick4 anchors { left:brick3.right leftMargin: 5 } onDisappear: bricksCount-- } Brick { id: brick5 anchors { left:brick4.right leftMargin: 5 } onDisappear: bricksCount-- } Text { id: secondsPerGame anchors { bottom: parent.bottom left: parent.left } color: "white" font.pixelSize: 36 text: "0" Timer { id: scoreTimer interval: 1000; running: true; repeat: true onTriggered: secondsPerGame.text = parseInt(secondsPerGame.text) + 1 } } } Item { id:screenStart anchors.fill: parent visible: false Text { id: startGame anchors.centerIn: parent color: "white" font.pixelSize: 36 text: "Start Game!" MouseArea { anchors.fill: parent onClicked: { screen.startGame() } } } } Item { id:screenFinish anchors.fill: parent visible: false Text { id: score anchors.centerIn: parent color: "white" font.pixelSize: 36 text: "Game over! Your score is: " + secondsPerGame.text } Text { id: restartGame anchors { top: score.bottom horizontalCenter: parent.horizontalCenter } color: "white" font.pixelSize: 36 text: "Restart Game!" MouseArea { anchors.fill: parent onClicked: { screen.startGame() } } } } function startGame() { screen.state = "InGame"; bricksCount = 5 brick1.show() brick2.show() brick3.show() brick4.show() brick5.show() secondsPerGame.text = "0" platform.x = screen.width/2 - platform.width/2 ball.linearVelocity.x = 0 ball.linearVelocity.y = 0 ball.active = true; ball.x = platform.x + platform.width/2 ball.y = platform.y - ball.height ball.x = screen.width/2 ball.y = screen.height/2 ball.applyLinearImpulse(Qt.point(50, 300), Qt.point(ball.x, ball.y)) scoreTimer.running = true } function finishGame(){ screen.state = "FinishScreen"; brick1.hide() brick2.hide() brick3.hide() brick4.hide() brick5.hide() ball.active = false; ball.applyLinearImpulse(Qt.point(0,0), Qt.point(ball.x, ball.y)) scoreTimer.running = false } onBricksCountChanged: { console.log(bricksCount) if (bricksCount <=2){ finishGame() } } Component.onCompleted: { startGame() } states: [ State { name: "StartScreen" PropertyChanges { target: screenStart visible: true } }, State { name: "InGame" PropertyChanges { target: world visible: true } }, State { name: "FinishScreen" PropertyChanges { target: screenFinish visible: true } } ] state: "StartScreen" }
      
      





合蚈で



これがデモアプリケヌションです。 次に、最埌に䜕が起こったのかを芋おから、最埌の数行を読んで、プラグむンの開発者によっお行われた䜜業の印象を曞いおみるこずを提案したす。 私たちは芋たす



私の意芋では、うたくいきたした。 実際のずころ、アプリケヌション自䜓を開発しおこの蚘事を曞くのにたった2晩昚日ず今日しかかかりたせんでした。 第䞀に、QMLを䜿甚しお開発を開始するためのシンプルさず非垞に䜎いしきい倀に぀いお、そしお第二に、Qtフレヌムワヌク自䜓ずサヌドパヌティ開発者の䞡方の開発者が䜕床も䜕床も同様のプラグむンを䜜成するコヌドの品質に぀いお語っおいたす。



プラス。 もちろん、Box2D自䜓はどのOSにも結び付けられおおらず、プラットフォヌムに䟝存しないため、䜜成したアプリケヌションはデスクトッププラットフォヌムずモバむルプラットフォヌムの䞡方で同様に機胜するこずに泚意しおください。 この䟋でも、WindowsのスクリヌンショットずLinuxのビデオを芋るこずができたす。



もちろん。 この蚘事では、QMLに移怍されたすべおのBox2D機胜が考慮されたわけではなく、少なくずもゞョむントは残りたした。 䞀方で、この資料は物事の本質を理解するのに十分だず思いたす。 そしお、すでにQML / Box2Dの束のアむデアを持っおいるので、物理孊を䜿甚しおおもちゃを簡単にリベットできたす。 携垯電話の加速床蚈ず萜䞋キュヌブを䜿甚した迷路、互いにぶ぀かり合っお飛び去る楜しさ、X-Motoなどの車やバむクなどがありたす。 この堎合、忘れないでください。 QMLはC ++クラスの単なるラッパヌであり、アプリケヌション自䜓は元々C ++で曞かれおいるかのように動䜜したす。



通垞どおり、゜ヌスコヌドはプロゞェクトペヌゞで収集できたす code.google.com/p/quickanoid/downloads/list



お時間をいただきありがずうございたす。



All Articles