東京工業大学 ロボット技術研究会

東京工業大学の公認サークル「ロボット技術研究会」のブログです。 当サークルの日々の活動の様子を皆さんにお伝えしていきます。たくさんの人に気軽に読んでもらえると嬉しいです。
新歓特設ページ        ロボット技術研究会 HP        ロボット技術研究会 twitter公式アカウント

ゲーム制作

「ロボット技術研究会」通称「ロ技研」は、その名前の通りロボットの制作や研究はもとより、電子工作や機械工作、プログラミングなどの幅広い分野にわたるものつくり活動を行っています。

カテゴリ一覧: loading

オリジナルSRPG「うえぽんでこれーしょん!」を出展します

こんばんは、14のまーぼうです!
コミケ出展記事第3弾!という事で、僕のゲーム紹介記事となります。当日はロ技研のブースじゃなくて、1日目西へ16aのサークル「麻婆飯店」にて500円で頒布します!


続きを読む

面白いゲーム、やる? やらない?

おはようございます、こんにちは、こんばんわ。塩谷 凛(@SALT_ORDER)です。

クリスマスとかいうクソイベントも終わり、今年も残りわずか。
あとは紅白見てそば食うだけ、なんて思っている人なんていませんよね?

そうです。コミケです!!
やっぱりコミケに行って、同人誌や同人ゲーム買わないと、新年は迎えられません!
そんなわけでここで私の作った面白い(?)ゲームを紹介させてもらいます。

まず日時と場所ですが、「29(木)西ほ41b」のCG^2で500円で売らせてもらいます。

そしてゲームのタイトルは……

 『SALT ORDER -first order-』です。

jacket

表紙から察してもらえたと思いますが、ジャンルはアクションです。


ー主な登場人物ー
ソルト
  sal博士のパソコンに住んでいる、少しツンデレなAIの女の子。

博士
  docちょっぴりえっちな博士。

メビウス
 EDGE3ソルトが作ったロボット(ソフト)。今作の主役。


ーあらすじー
博士の大切な研究データが盗まれてしまった!
データを取り戻すべく、ソルトの指示のもと、メビウスを動かし、敵の拠点に乗り込もう!


ーストーリーモード(メインモード)ー

  asdf

様々な敵を倒しながら、各ステージにある敵の電脳空間情報が詰まったデータチップをゲットしよう。
見てのとおり、メビウスはレーザーを撃つことができるぞ!

複数のワールドがあり、各ワールドの最後にはボスが待ち構えているぞ!

また、各ステージに隠されたサイバークリスタルという結晶をゲットすると、レーザーの威力、移動速度やジャンプ力などをアップグレードすることができるぞ!


ーK-8(ケイト)のアイテムショップー

  it

サイバークリスタルでアップグレードするパラメータを間違えたり、どうしてもクリアできないときはここでアイテムを買おう!
コインは次に説明するミニゲームで稼ごう!


ーミニゲームー

  mg

ケイトのアイテムショップでアイテムを買うのに必要なコインをここで稼ごう!
上の三つのミニゲームは欲張れば欲張るほど、失敗しやすくなるけど、欲張らずにはいられない
単純だけど、面白いミニゲーム。(操作はマウスです。)


ーステージメーカーー

  sm

自分だけのステージを作ろう!(操作はマウスです。)


ーキーコンフィグー
このゲームはゲームパッド対応(キーボード操作も可)で、操作ボタンを自分好みにカスタマイズできるぞ!



……ざっと説明できたかな?
少しでも興味を持ってもらえたら、うれしいです。

CG^2では、このゲーム以外にも面白いゲームを売りますし、すごい部誌も売る予定です。
コミケに来た際にはぜひ。


・まとめ
「29(木)西ほ41b」『SALT ORDER -first order-』500円で売るので来てね!



RPGツクールMVでのシューティングゲーム作成について

こんにちは、13の折重です。今回はrogy Advent Calendar 2016の16日目として記事を書きます。
凄い更新が遅くなってしまって申し訳ないです。アドベントカレンダーは日付が変わった瞬間に投稿するのが普通っぽいですよ奥さん

 screenshot
こんな感じのを作ります

の前に宣伝とか

Advent Calenderに登録した時書くことが決まってなかったので記事概要みたいなところに超ナメたこと書いてますね、すいません。実際何も考えてなかったわけではなく逆に色々考えてて、一回書く内容変えたりしてます。じゃあ前まで何書こうと思っていたのかというと、今までのゲーム制作についての色々です。それは結局別の機会に書くことにしたので今回はこっちを書くことにしました。

とここで宣伝。
来るコミックマーケット91にて、我々ロボット技術研究会もサークルとして参加し、有志による記事をまとめた部誌を発行することにしました。そしてそこに僕も「学園祭におけるゲームプレイの観察」と題して寄稿しております。興味がある方は是非買って読んでみてください。スペースは木曜日(12/29) 西地区 "ほ"ブロック41bです。

ここから本題

今回の記事について

今回はシューティングゲームを作る過程で、RPGツクールMVの仕様とプラグイン作成について紹介していこうと思います。
RPGツクールMVはRPGツクールシリーズの最新版で、誰でも気軽にRPGを制作することができます。更にMVからはHTML5をベースにすることによりブラウザで動作するゲームを簡単に作れるようになりました。また、今回扱ったようにプラグインによる拡張でシューティングゲームを作成できるなど、非常に広い汎用性を持ちます。
ゲームエンジンならばUnityなど他のものもあるのではないかと言われるかもしれませんが、RPGツクールはRPGであれば特別な知識などなくても簡単に作成できますし、先ほども言ったようにHTML5がベースなのでwebなどでの公開も簡単で迅速(当然公開場所は自分で用意しなくても公式に用意されています)、更にJavaScriptでプラグインが作成できるため拡張性も高くweb関係の機能の実装とも親和性が高い、という利点があります。今回のようなシューティングゲームを作るようなのはできなくもないだけで極端な使い方ですが、基本普通のRPGを作りそこに独自の機能を追加したいというのには非常に向いていると思います。
シューティンゲームにした理由ですが、バイトでRPGツクールを使って色んなものを動的に生成したり意味不明な使い方を色々したんで、もしかしたらシューティング作れるんじゃないのかって思ったことと、僕が好きなジャンルだからなのと、なんとなくゲーム制作初心者向けってイメージがあるからです。シューティング作れればあらゆるジャンルのゲームが作れますよ、多分。

前述したように筆者はRPGツクールMVを使用しはじめたのが今年の4月から(しかもバイトは月数回)、ついでにJavaScript経験もそれに毛が生えた程度なので不格好なコードもあるかもしれませんが、これからRPGツクールを使いたい人、現在使っている人の助けに少しでもなれれば幸いです。あとバイトを引き継ぐ人が決まったら読んでもらいたいです(主目的)

前提

前置きが多いですね、申し訳ないです。
今回の記事ではJavaScriptについての説明はしません。必要なJavaScriptの知識は各自で勉強してください。すいません。
またクラスがどうとか言っていますがprototypeを使用してクラスっぽい実装をしています。継承って言ったらプロトタイプチェーンを利用してメソッドを親クラスから引き継いでるんだと思ってください。なんか最近JavaScriptにclass構文が実装されたそうですが、RPGツクールでの実装が全てprototypeになっているのでそれに倣います。もしかしたら次のRPGツクールでは実装が変わっているかもしれませんね。というかクラスに限らずRPGツクールの元からの実装に倣っている部分が結構あります。

オリジナルシーン、ウインドウの作成

シーンは、RPGツクールではrpg_scene.js内で定義されており、Scene_TitleやScene_Map、Scene_Battleなどがあります。今回はシューティングゲームということでScene_Shootinを作成したいのですが、基本的にはRPGでのマップ画面とあまり変わらないのでScene_Mapを継承したScene_Shootingクラスを作成することで実装します。
Scene_Shooting.jsとうファイルをpluginsフォルダの中に作り、以下を記述します。

Scene_Shooting.jsに追加

ここまでで保存し、いったん動かしてみます。プラグインを使うにはRPGツクールのエディタ側でツール→プラグイン管理からプラグインを追加しなければならないので忘れないようにしましょう。動かしてもScene_Mapと全く同じ機能を持ったScene_Shootingが代わりに呼ばれるだけなので見た目は全く変わりません。
では少し変更を加えていきます。まず既存の関数をオーバーライドし書き換えることで不要な画面遷移やウインドウ表示などをしないようにしてしまいましょう。

Scene_Shooting.jsに追加 

ここで実行してもまだWindow_SideMenuクラスを作成していないためエラーが出ます。
というわけでウインドウを作っていきます。シューティングゲームにおいてスコアや残機を表示するウインドウです。
ウインドウはrpg_window.jsというファイル内にたくさん定義してあり、それをシーンに追加していく形で表示します。ウインドウ自体が表示する画像や文字などの内容や大きさの情報を持っており、シーンからはウインドウ作成時に表示位置を指定するのみです。新しいウインドウを作成したいと思ったらそれを実現するウインドウクラスを新しく作成してあげましょう。場合によってはそのウインドウを表示する用の新しいシーンも必要になるかもしれませんね。
rpg_window.jsには色々なウインドウのベースとしてwindow_BaseやWindow_Selectableなどがあります。今回は選択肢は必要ないのでWindow_Baseを継承し新たにWindow_SideMenuクラスを作成していきます。

Scene_Shooting.jsに追加

というわけでここまでで自作のシーンとウインドウを追加することができるようになりました。これだけでもかなり制作の自由度は上がるかと思います。

自機の作成とSpriteの追加

次はプレイヤー周りをいじっていきます。と言ってもやることはあまり変わらないです。 
プレイヤーに関する記述はrpg_object.js内に書かれています。rpg_object.jsはその名の通りゲーム内に出てくるオブジェクトについての記述がなされています。マップに表示されるプレイヤーに関してはGame_Playerクラスに書かれています。

まずGame_Player.jsというファイルを作成します。この中でGame_Playerクラスの関数を改変していきます

Game_Player.jsに追加 

RPGでの移動はマス目単位ですがシューティングではその必要はないので毎フレーム移動し進行方向のマス目の状態を確認する部分なども削ります。これだけだと超高速移動をしてしまうので移動距離の計算部分も変更します

 Game_Player.jsに追加 

これでシューティングゲームのように動きました。最後に向きを上向きに固定します。移動処理を書き換えたタイミングでキャラの向きの更新の処理も消してしまったので初期化する時方向を上向きにしてあげれば大丈夫です。

Game_Player.jsに追加  

これで自機を動きはできました。え、なんかデフォルトだと仲間が3人くらい表示されてる?忘れるなかれこれはRPGツクール、そこらへんの設定はエディタ側でできます。ツール→データベース→システムから初期パーティを変更してしまいましょう。

自機をシューティングっぽく動かすことができたので、次は弾を出せるようにしていきたいと思います。ですがその前に、ここでRPGツクールの描画の仕組みについて少し触れます
RPGツクールは画像の描画にpixi.jsというライブラリを使用しています。rpg_core.js内のSpriteというクラスがpixi.jsを用いた画像描画を行う上でのラッパーのような役割をしており、rpg_sprite内にあるSprite_CharacterやSprite_ActorクラスなどがSpriteクラスにゲーム内で扱う上で必要な機能や情報を目的ごとに追加したものになっています。更にrpg_sprite.js内にはSpritesetというSpriteをまとめて管理するためのクラスがあります。各シーンはこのSpritesetを持っており、ここにSpriteを追加していくことで画面に描画するものを管理しています。
実はRPGツクールはマップのロード時に必要なオブジェクトの画像なんかは全てロードしてしまい、まだ発生しないイベントなんかも画像非表示になっているだけでマップには存在していたりします。RPGなので当然と言えば当然なのですが、そのためオブジェクトの動的生成は少しやりずらかったりします。色々がんばるとできます。 
今回Scene_ShootingはScene_Mapを継承して作成したため、Spriteset内のtilemapにSpriteを加えていきます
 
Scene_Shooting.jsに追加

これを呼ぶことによって描画対象を追加することができます。

弾と弾幕の作成

弾クラスと、それを管理する弾幕クラスを作成することにより実装することにします。自機や敵キャラはそれぞれ1つの弾幕クラスを持つようにします。弾については、Game_CharacterBaseクラスを継承したGame_Bulletクラスを弾クラスとして作成します。RPGツクールではマップに表示されているキャラや扉なんかのオブジェクトは基本的にrpg_objectにあるGame_CharacterBaseというクラスを継承しています。Game_CharacterBaseでは画像や座標などの情報を持っており、座標を変更すると勝手に描画位置も変わるなど、ゲーム中の視認できる物体を扱いやすくしています。弾には無駄な情報も多いですが楽なので利用します。
まずGame_Bullet.jsを作成しクラスを作成します。プラグイン追加も忘れずに。

Game_Bullet.jsを追加 

基本的には初期化関数やアップデート関数をオーバーライドしているだけですが、initialize()内でsetParams()を、refresh()内でsetImageData()を読んでいます。これらは弾の当たり判定の範囲や画像データなど弾固有の情報を初期化するものです。後ほどGame_Bulletクラスを継承して新たにクラスを作成する時にまた説明します。

これで一応弾を出すことができるようになりましたが、まだ生成部分の処理を作っていないので画面は何も変わっていません。試しに入力をされたらプレイヤーが弾を出すようにしてみましょう。

Game_Player.jsに追加
 
既存のupdate関数を以下のように変更

これでZキーを押すとなんか変な光みたいな画像が表示されたと思います。まだ移動処理なども書いてないのでその場から動きませんが。SceneManagerはRPGツクールのシーンを管理しており、グローバルに定義されているのでどこからでもアクセスできます。内部に現在のシーンを保持しているのでそこからAddSprite関数を呼び出しましょう。
次はこれらの管理をする弾幕クラスを作成していきます。これは特に実体もなく画像もいらないので全て自作してしまいます。ひとまずの実装を書きます。プラグイン追加も忘れずに。

Shot.jsに追加 


addBullet関数でShotクラス内のArray配列であるbulletsに弾を入れ、同時にスプライトも登録してしまいます。
これは弾幕全体のスーパークラスであり、これを継承して更にPlayer_ShotクラスとEnemy_Shotクラスを作成します。generate関数やcollide関数、controlBullet関数は継承した先でオーバーライドします。
というわけで、Player_Shotを作成して自機についての処理を完成させてしまいましょう。

Game_Player.jsに追加


update関数を以下のように書き換え

これで先ほど表示された光が前方に向かって飛んでいくようになったと思います。ただし弾が画面外に行くようになってしまいました。現状画面外にある弾がいつまでも表示されているので、これを削除するようにしたいと思います。

Scene_Shooting.jsを変更
update関数に以下を追加

これで弾が消えるようになりました。以降衝突などで弾を消したいときは弾のqctiveの値をfalseにしてあげれば勝手に消えます。
 
現状Game_Bulletクラスに書いたてきとうなパラメーターの弾を生成していますが、自機用にGame_PlayerBulletクラスを作成しそれを生成するようにしましょう

Game_Bullet.jsに追加

Game_Player.jsを変更
generateEvent関数を以下のように書き換え

はい、画像が変わったと思います。弾の種類を増やす時はsetImageData()で弾の画像を、setParams()で弾の情報を書き換えるだけで増やすことができます。

ここまでで自機の実装と、基本的な機能の実装ができました。

次は敵と敵弾幕を実装...と言いたいのですがぶっちゃけここからはただのゲーム作りになってしまうので説明は割愛します。時間もないですし......
せっかくなので実装した部分までのソースコードはgithubに公開しておきます、記事の最後の方にあります。微妙にブログ用に修正したのと違いますが。敵の実装とかがすでに適当なのであまり参考にしない方がいいです。あとあくまで説明用に作ったものなのでゲーム部分はかなり適当です。当たり判定とかもっと色々できそうですね。関数は用意してありますしシューティング作りたいと思った方は自分で実装してみてください。
あとはエフェクトとかもつけてもいいかもしれませんね。弾から当たり判定抜いたものを作るだけで実装できそうです。というか弾のrangeを0にするだけでエフェクトと言い張れなくもなさそうです。

補足

RPGツクールはF2キーを押すことでFPSを表示させることができます。デフォルトは50FPSでいじる方法があるのかは知りませんが、それで見た感じかなり弾を出してもFPSは維持されていました。画像にこだわったりしたらブラウザで動作するかなり本格的なシューティングゲームが作れるのではないでしょうか。
他にもデバッグ用の機能が色々あるっぽいのでとりあえずファンクションキーとか押しまくってみるといいと思います。

では結構な長さになってしまいましたが読んでいただきありがとうございました。
明日はJpREOTTOくんによるまったり🍵とした内容を書くばい(放課後~)です。
 
Github
https://github.com/OrishigeMasato/RPG_ShootingPlugins
 

Vulkanフレームワークを作ってみた


こんにちは、isskです。情報工学科3年です。年齢は秘密です。
ロボットのサークルにいるのですが、私はゲームを作っています。

これはrogy Advent Calendar 10日目の記事です。

【アップデート】消し忘れていた部分を消しました。「感想」と「Q&A」を追加しました。

vulkan_lava

"Vulkanを使っただけでゲームが面白くなるわけではない。何故使うんですか?"
"そこに(火)山があるから──。"
-レベル4の回答より- 

まえがき

最近私は趣味でVulkanを勉強しています。
Vulkanと言うのは最近公表された"Low-Overhead API"の一つです。
並列化に強いとのウワサです。
OpenGLとドが付くほど勝手が違うので驚いています(詳細は下に)。

そして、学習の一貫として、フレームワークを作りました
  vulkan-study-framework

(Windowsに限って一応実行できますが、設計とか実装自体は明らかに途中段階です)

で、リアルタイムパストレーシング(レイトレを拡張したようなものです)をしてみました。

(Win + Alt + Rがいつまでも録画してくれなかった)
この記事ではこれを実現するためのVulkanの記述について解説していこうと思っています。
自分へのノートも兼ねています。

注意

筆者はDirectXを知りません。
筆者は浅学なので、以下の解説は間違っている事があります。
誤記を見つけたら後学の為、また閲覧者の誤解を与えないために訂正下さい。お願いします。

Vulkanの特徴

C言語で書かれたダッシュ島です。

語句解説

読む途中でこの語句分からない、となったらここを確認すると助けになるかもしれません。

インスタンス

Vulkanの初期化をする構造体を示しており、 Vulkanアプリケーションの状態は全てインスタンスに格納されるので、インスタンスを作成しないとVulkanに情報を渡せません。

物理デバイス

レンダリングに関する計算に使用するハードウェア(GPU)の事を表す構造体のことです。

論理デバイス

簡単に言うと人間に対する物理デバイスのインターフェースです。

サーフェス

レンダリング結果を人間から見える様に表示する対象物です。レンダリング結果が見える必要が無いならサーフェスを作らないという選択もできます。(オフスクリーンレンダリングと言うらしいです)

レンダーターゲット

レンダリングの対象である画像バッファの事を指します。

SPIR-V

中間言語です。GLSLがこれに事前にコンパイルされます。コンパイルされたSPIR-VバイナリをVulkanがシェーダとして扱います。OpenGLでも拡張で使えるんですよ。

スワップチェーン

レンダーターゲットを提供するインフラです。Vulkanではレンダーターゲットの円形状の集合体のようです。ダブルバッファリングとかを実現します。DirectXでは馴染みらしいですね。

イメージビュー

イメージをどの様に扱うか決定するものです。(このイメージはデプステクスチャとして扱う、等)

グラフィックスパイプライン

与えられた頂点情報やテクスチャ情報をレンダーターゲット内のピクセルにするための一連の操作の流れです。

コマンドバッファ・コマンドプール

コマンドバッファは描画命令などを記録するオブジェクトです。コマンドバッファはコマンドプールから割り当てられます。直接作らないの意です。

レンダリングパス

完全に新しい概念です。わかってません。 これはあるアタッチメントがどのように処理されるか、を取り決め、これらを集めて依存性を持たせているもの、という理解をしています。(ちょっと自信ないです、、、、)。 また、これの恩恵を得る機会はこの記事には多分ありません。

フレームバッファ

誤解を恐れない発言をすると、イメージビューをアタッチメントそのものとして持たせ、レンダリングパスにあるアタッチメントの説明と紐付するものです。(OpenGLのフレームバッファの意味少し広い版らしいんですけど、自分はOpenGLを使っていた頃フレームバッファをただのテクスチャの集まり程度にしか考えていなかったので、難しいですね、、、)レンダリングパスのアタッチメントに設定された計画通りの動作を行うようになります。

Vulkanを使ってみる

「御託はいいからコードを見せろ!」
「おk!わかった!まずVulkanのインスタンスを作成するぞ!」
もう既にイヤんなってきますね。
自分は頑張って↑のような書き方で三角形をウィンドウに表示(Hello Triangle)させたら800行くらいのコード量になりました。Hello Worldに800行かかるんです。
先に述べた通り、Vulkanは C言語のAPIなので、いくつか面倒な事象が発生してしまいます。
構造体にコンストラクタ等は無く、構造体によって代入するものが確定している sTypeフィールド等を一々書かなければならないのが非常に面倒くさい上にミスの温床となっています。
その時、初期化を忘れたフィールドがあると見つけるのが困難な問題になりがちです。
引数に配列を渡す場合、たとえその配列がSTLのベクタであっても配列の要素数(.size())と配列のポインタ(.data())を分けて渡さないと行けません。
いろいろつらいですね。
そこで紹介するのがVulkanのC++によるラッパーであるvulkan.hppです。 大分すっきりしたコードになりましたね。Vulkanの構造体をvk::名前空間のクラスに全てラップして、sTypeはコンストラクタで正しいものに初期化が行なわれています。sTypeの事故はまず起こりません。
コンストラクタが存在するので初期化忘れをすることは先ずなくなりました。
STLをデフォルトでサポートしています。
Vulkanの列挙体は全てenum classに置き換わっています。enumそのものが間違ってたらコンパイル時に弾かれるようになります。(|演算子とかはちゃんと使えますよ)
vk::ArrayProxy<T>というクラス(std::vectorとstd::arrayをコンストラクタの引数に渡せる)があって、これが配列の要素数と配列のポインタを持っているので、引数に要素数とポインタを一々渡す必要がなくなりました。
Optional<T>が実装されているので使えます。
vkCreateImage(device,...)のような、論理デバイスが行うであろう事をする関数は全てvk::Deviceクラスのメソッドになっています(インスタンスとか物理デバイスも同様)。
良いことづくめだし、そもそも Vulkan SDKが標準でこのヘッダファイルを持っているのですから、C++を用いるに当たって使わない手は無いでしょう。

Vulkanでリアルタイムパストレーシングするまで

実はVulkanで上の動画のレンダリング結果を表示させる前に、OpenGLで同じことを実現していました。 ですから、先に作っておいたシェーダのコードをVulkanで扱えるSpir-Vバイナリにコンパイル出来るように微修正して、

1.4つの頂点とUV座標をアトリビュートとして描画できるようにする
2.Uniform Buffer Objectとしてデータをフラグメントシェーダに渡せるようにする

事をすれば、後は既存のシェーダを渡すだけで実現できます。
あまりに記事が長くなってしまうので、コードを載せる際は少しはしょらせて頂きます。

Vulkanを初期化する

・はじめに
Vulkanではほぼすべての操作を コマンドとしてキューに入れて実行させるようになっています。
これも"Low Overhead"のためなのですよ。
この先キューが出てきたら「コマンド群の入れ物だ」と思ってください。
また、**KHRという名前の関数は「これはVulkanの拡張である」ことを示しています。

・インスタンスを作成
大体のVulkanアプリケーションが同じことを行っているでしょう。
ここでインスタンスに 検証レイヤ(Validation Layer)を任意に導入することが出来ます。検証レイヤとはVulkanのエラーチェックシステムです。
デバッグビルドであるときに検証レイヤを導入し、プログラムのミスに因るエラー検出を作ってバグ直しに役立てています。役立ちました。すごい分かりやすいエラーメッセージ。感謝してもしきれません。
また、使用できるVulkanの拡張も記述します。 自分は列挙した配列を予め用意しているだけです。

・サーフェスを作成 OSことにサーフェス作る関数が異なっています。こういう関数は拡張になってますね。
先に述べましたが、Vulkanではオフスクリーンレンダリングを行う上で サーフェスを作らないという選択が可能です。
OpenGLでは見えないウィンドウを作成する必要があったと思われます。

・使う物理デバイスを選ぶ インスタンスは使うかもしれない物理デバイスをすべて列挙することができます。そこから取捨選択が可能です。
ここで「キューファミリー」という語が登場します。
まず、GPUは幾つかのキューを持っています。これらのキューはできることが限られていることがあります。
参考元によれば、NVIDIA製はなんでも出来るキューを16個もっており、AMD製は{1つのグラフィックス/計算両用キュー,1~4個の計算用キュー,2つの転送用キュー}をもっているらしいです。
このキューのうち、同じ機能を持つキューの集合が一つのキューファミリーです。「なんでも出来るキュー16個」のNVIDIAはキューファミリー1つですね。
で、このキューファミリーの番号を保持しておきます。あとで使うためです。

・論理デバイスを作成 物理デバイスが論理デバイスを作成する役割を持っています。論理デバイスにも検証レイヤを導入することができます。
物理デバイスを選ぶ時に、キューファミリーの番号を保持していましたが、これは実際に使用するキューをvk::Queueとして取得するのに必要だったからです。
キューは描画用のキューと、ウィンドウに表示させる用のキューです。

・コマンドプールを作成
コマンドプールはメモリプールであり、そのバッファの中から適当なコマンドバッファを割り当てます。
作成でやることはこれだけです。
以上の{インスタンス作成,サーフェス作成(任意),物理デバイス選択,論理デバイス作成,コマンドプール作成(これも?)}が初期化の流れです。

4つの頂点とUV座標をアトリビュートとして描画できるようにする

・スワップチェーンを作る
スワップチェーンを作る部分をあえて初期化の段階に入れなかったのは、スワップチェーン内のレンダーターゲットの大きさをスワップチェーンを作り直さないと変えられないので、 例えばウィンドウの大きさが変わった時等はスワップチェーンを作り直さなければならないからです。 (Vulkanは冗長なAPIと良く言われておりまして、、、、)
これは、スワップチェーンのプロパティを唯一に選んで、キューファミリーによる設定の違いに考慮しながらcreateInfoを設計しています。
スワップチェーンを作り直す際も同じ関数を読んでいるのですが、「スワップチェーンを初めて作成するか、スワップチェーンを置き換えることをするか」というのはinfo.oldSwapchainの有無によって判別されるようです。

・レンダーパスを作る
上の語句解説で大分端折っていたレンダーパスについて分かる範囲で説明します。 あるアタッチメントがどのようなものであるかを書き(AttachmentDescription)、処理の記述(SubpassDescription)にそのアタッチメントの参照(AttachmentReference)を渡しています。
サブパスはカラーアタッチメントの参照を持つもの単一であるため依存関係は設ける必要がありません。以上まとめてRenderPassCreateInfoにしてレンダーパスを作成。後にレンダーパスのアタッチメント記述とスワップチェーンのイメージのビューを紐付けるフレームバッファを作ればおk!
wakaranaikao(依存関係って何....)
はい。
レンダーパスにサブパスを複数設置して、たとえばこれらをサブパスA、サブパスBとした場合、「サブパスBはサブパスAの入力が必要なので、サブパスBはサブパスAの後に照会されなければならない」ということがあります。
これを記述できるのが「サブパス依存関係(SubpasDependency)」です。Vulkanがコマンドを並列処理するために、複数のパスの照会順番が分からないことから生まれた模様です。
(サブパスを複数設置するもので、自分がぱっと思いつくのはポストプロセスですね。)

・グラフィックスパイプライン作成 めちゃくちゃ端折ってしまったのですが、グラフィックスパイプラインの各段階に情報を挿入するvk::Pipeline[StageName]CreateInfoを渡しています。

・頂点バッファとインデックスバッファ作成 バッファを2つ作って、StagingBufferの方にデータをマッピングしてから、バッファをVertexBufferに移しています。
これはBuffer作成の際使用されるvk::MemoryPropertyFlagBitsによるBufferの特性に関係しています。
グラフィックスカードがデータを読み出すのに最適なバッファのプロパティはvk::MemoryPropertyFlagBits::eDeviceLocalです。
しかしながら、このプロパティのバッファのメモリはCPUからアクセスできません
そこで、CPUからアクセス出来るプロパティでバッファを作ってから、一時的に取得したコマンドバッファを介してバッファをコピーする方策を撮っています。
これはバッファのステージングと呼ばれていて、DirectXではお馴染みの様ですが、自分は今まで知りませんでした。
インデックスバッファも同様の方法で作っています。

・コマンドバッファに描画命令を記録する コマンドプールから割り当てられたコマンドバッファ全てに対して、レンダーパスを開始してから描画命令を記録しています。コマンドバッファは、スワップチェーン内のフレームバッファそれぞれに割り当てられるので、コマンドバッファの数はスワップチェーン内のフレームバッファの数と同じです。

描画と表示

ここから毎フレームごとの話になります。
今、ここに描画命令の記録されたコマンドバッファがあります。 スワップチェーンからレンダーターゲットを(1つ)取得します。この時、「レンダリングする準備が整った」という信号を出すオブジェクトであるセマフォを登録してます。
レンダーターゲットに対応したコマンドバッファをグラフィックスキューに入れてからコマンドバッファに記録されたものが先のセマフォの信号を検知したらsubmitから実行されています。
「ウィンドウに表示する準備が整った」という信号を出すセマフォを使い、そのタイミングでプレゼントキューからウィンドウにレンダリング結果を表示しています。

レンダリングまでの作業が終わりました。びっくりするほど長かったですね。
しかしながら、まだUniform Buffer Objectという仕事が残っております・・・

・Uniform Buffer Objectを扱う
バッファを作る部分は頂点バッファやインデックスバッファと全く同じです。
Uniform Bufferを渡す場合は、そのほかにディスクリプタセットを作る必要があります。 コマンドバッファに記録している(上の"コマンドバッファ作成"の部分)コードの22行目にbindDescriptorSetsという関数がありますね。 そこにUBOの情報の入ったディスクリプタセットを渡しています。 そうすることで、後はUBOのデータを任意のタイミングで更新して、バッファのメモリを書き換えることでシェーダでのUBOの値も更新されます。
最終的なレンダリングが、上のパストレーシングの結果となります。

フレームワークにしてみる


普通にこの膨大な作業をするのは辛いので、その作業量を減らすためにフレームワークの実装に着手しました。
やっていることはvulkan.hppをさらにラッピングする、というスタンスです。
既に作っていた(OpenGL向けの)ライブラリの一部を転用しています。(数学関連、ウィンドウ等)

Ore::Instance

物理デバイスを作成するメソッドを持っています。 また、サーフェスも持っており、作成しています。

Ore::PhysicalDevice

論理デバイスを作成するメソッドを持っています。 インスタンスのshared_ptrを持っています。

Ore::Device

物理デバイスのshared_ptrを持っています。 色々なVulkanのオブジェクトを作成する関数を実装している 最中です。

その他

共通することは論理デバイスのshared_ptrを持っていて、デストラクタで自身のvk::**を削除していることなのですが、それ以外は今のところvulkan.hppのクラスをラッピングしているだけです!

まとめ

Vulkanで簡単な図形を描画することと、Uniform Buffer Objectを扱うことを紹介しました。この記事の情報で一応3Dモデルをテクスチャ無しで描画できるはずですよ。

感想

これ人間の書くAPIじゃなくないですか....これのレンダラを採用しているDOOMとかの技術者を改めて「めっちゃ凄いな」と思う冬の空の下です

Q&A

Q-"Low Overhead API"というのは、CPUのオーバーヘッドを減らす目的で策定されたのであって、GPUの計算が大半を占める上のパストレーシングでこれを使うのはあまり意味が無いんj
A-そこに(火)山があるから──。

以上です。ここまでたどり着いた方は、
takeshi
「この記事は全く意味のないものかもしれない」の意


次回のadvent calendarの更新は
rej55さんの「MATLABで3Dモデルを更新しよう!」です。 

ありがとうございました! 

MATLABゲーム制作の基礎の基礎

みなさんアローラ!13のマハトです.
これは rogy Advent Calendar 2016 の7日目の記事です.

普段はMATLABで作った変なものを紹介する記事を書いている僕ですが,今回はせっかくのAdvent Calendar企画だし少し技術的な記事にしようということで,「どうやったらゲームっぽい動きをMATLABで作れるか」について軽く書きたいと思います.

※この記事はわかりやすさを重視するために厳密には正確でない表記を用いている部分があります.またどうプログラムするかは個人の好みの部分ですので,プログラム例はあくまで参考としてお使いください.

・グラフィックスオブジェクト

 誰もが使ったことのある「plot」をはじめとした,figure上に描画できる図形のことをグラフィックスオブジェクトと言います.グラフィックスオブジェクトを作成する関数は色々ありますが,その全てが返り値としてそのオブジェクト自身を示す変数(グラフィックオブジェクト変数)を返します.
p = plot(t,x);		%pはplot(t,x)自体を指す.
よく使うグラフィックスオブジェクトを下に挙げておきます.それぞれの関数の使い方は各自調べてみてください.
f = figure(); %figureウィンドウ
ax = axes(); %座標軸
pat = patch(x,y,color); %多角形
rec = rectangle('Pos',pos); %曲率を設定可能な長方形
text = text(x,y,text); %文字
im = image(x,y,C); %画像
(ちなみにパズドラのゲーム画面はここに挙げたオブジェクトのみで作ってます)


・プロパティ
 MATLABのグラフィックスオブジェクトには「プロパティ」という設定値がありまして,このプロパティを設定することで図形を編集できます.例えばグラフの線の太さや色はそれぞれ「LineWidth」,「Color」プロパティに対応します.プロパティの値を設定するにはグラフィックスオブジェクト変数を使って
set(オブジェクト変数,'プロパティ名','プロパティ値');
または
オブジェクト変数.プロパティ名 = プロパティ値;
とします.逆に,プロパティの値を知りたいときには
value = get(オブジェクト変数,'プロパティ名');
または
value = オブジェクト変数.プロパティ名;
とします.例えば上のpの色を赤に変えたければ,
set(p,'Color','r');    or    p.Color = 'r';
です.逆にpの色を知りたければ,
p_color = get(p,'Color');    or    p_color = p.Color
です.各オブジェクトが持つプロパティは全てmathworks社のページに載っています.「(オブジェクト名) プロパティ」で調べるとすぐに出てきますので,気になるオブジェクトについていくつか調べてみるといいかもしれません.


・ゲームっぽいプログラムの例
 ここからゲーム制作に話が移ります(いきなり難易度が上がります).ここではこれまでの知識を使って「ドラッグすると図形が動く」というパズドラの一番基礎となるプログラムを例として,それについて解説を入れていきます.
patchとfigureとaxesのプロパティのページを見ながら読み進めるとわかりやすいかと思います.

function test
f = figure();
ax = axes();
axis([0 5 0 5]); %座標の範囲をx:0~5,y:0~5に
pat = patch([0,1,1,0],[0,0,1,1],'r'); %(0,0),(1,0),(1,1),(0,1)を頂点とする正方形
pat.ButtonDownFcn = @bdf; %①

    function bdf(object,eventdata) %patがクリックされた時に実行される関数,objectにはpatが入る
        cp = ax.CurrentPoint %②
        x = cp(1,1);
        y = cp(1,2);
        pat.XData = [x-0.5 x+0.5 x+0.5 x-0.5]; %patの中心のx座標をマウス位置に変更
        pat.YData = [y-0.5 y-0.5 y+0.5 y+0.5]; %patの中心のy座標をマウス位置に変更
        f.WindowButtonMotionFcn = @wbmf; %③
        f.WindowButtonUpFcn = @wbuf; %③
    end

    function wbmf(fig,eventdata) %figure上でマウスが動いた時に実行される関数,figにはfが入る
        cp = ax.CurrentPoint; %④
        x = cp(1,1);
        y = cp(1,2);
        pat.XData = [x-0.5 x+0.5 x+0.5 x-0.5];
        pat.YData = [y-0.5 y-0.5 y+0.5 y+0.5];
    end

    function wbuf(fig,eventdata) %マウスが離れたときに実行される関数,figにはfが入る
        fig.WindowButtonMotionFcn = ''; %⑤
        fig.WindowButtonUpFcn = ''; %⑤
    end
end
―メイン(test)関数―
①patch:ButtonDownFcn
 patchには「ButtonDownFcn」というプロパティがあり,このプロパティの値を「@関数名」と設定しておくことで,patchがマウスでクリックされたときに設定した関数が実行されます.このプログラムでは「bdf」という関数を設定しています. 

―bdf関数―
②axes:CurrentPoint
 axesには「CurrentPoint」というプロパティがあり,マウスが最後にクリックされた座標が行列形式で保存されており,(1,1)にはx座標,(1,2)にはy座標が保存されています.これを使うことでpatをクリックされた位置に移動させることができます.

③figure:WindowButtonMotionFcn,WindowButtonUpFcn
 figureには「WindowButtonMotion(Up)Fcn」というプロパティがあり,①同様関数名を設定しておくことで,figure上でマウスが動いた(離れた)ときに設定した関数が実行されます.このプログラムでは「wbmf(wbuf)」という関数を設定しています. ちなみにメイン関数ではなくbdf関数内で設定している理由は,「ドラッグ」を検知したいからです.マウスが押されてから離れるまでの間にのみ処理が実行されて欲しいわけです.

―wbmf関数― 
④axes:CurrentPoint
 先ほど解説した「CurrentPoint」ですが,figureの「WindowButtonMotionFcn」に何か関数が設定されている場合は「最後にクリックされた場所」ではなく「直前のマウスの座標」が保存されています.そのため,マウスが少しでも動けば「CurrentPoint」の値が更新されていきます.

―wbuf関数― 
⑤ figure:WindowButtonMotionFcn,WindowButtonUpFcn
 先ほど設定した2つの関数の設定をリセットします.これでマウスが離れた後はマウスが動いても何も処理が実行されません.


こんなかんじで,意外と簡単に実装できてしまいます.ちなみにこのpatchを複数のrectangleにして動きに応じて位置が入れ替わるようにしたのがMAT&ドLABンズです.


・まとめ
今回読んでもらった知識があれば結構いろんなものが作れます.色々わかりにくいところもあったと思いますが,講義や課題でシミュレーションしてグラフ作ることにしかMATLABを使っていない人もこれを機に遊んでみてもらえたらな,と思います. 

・今後作りたいもの
 
 修士1年になって忙しくなってきたのでこれまでみたいに色々作れるわけではないですが,とりあえず2つほど作ってみたいなというものがあります.
1.パズドラメーカー
 今のパズドラのコードを整理するとともに,チームやステージなどを自作できるツールを開発できたら面白そうだと思っています. 
2.ポ〇モン
  1つの時間軸があってそれに伴ってNPC達がランダムに色々動く,という処理を前から作ってみたかったので,どうせならポ〇モンでも作ろうかな,というようなかんじです.当初simulinkで作りたかったんですが,自分のPCだとsimulinkが重すぎてとても開発できないので,やるならfigureベースになるかなといった感じ.


以上です. 最後まで読んでいただきありがとうございました!
ギャラリー
  • ABUロボコン結果報告
  • スマホから部屋の電気をつけてみた
  • MakerFaireTokyo2017に出展します
  • MakerFaireTokyo2017に出展します
  • MakerFaireTokyo2017に出展します
  • MakerFaireTokyo2017に出展します
  • MakerFaireTokyo2017に出展します
  • MakerFaireTokyo2017に出展します
  • たのしいロボット帝国 製作物紹介
記事検索
最新コメント