この記事はゲームエンジン・ライブラリ・ツールの開発 Advent Calendar 2016 の 6 日目の記事です。
quincite
クインサイトと読みます。ゲームで手軽に GUI を記述することを目的にしています。
quincite 自体は GUI コンポーネントのツリー構造を構築するだけで、描画とかはしません。実際に GUI のコンポーネントを画面に表示するためには、たとえば DXRuby や Gosu を使って、それぞれ描画処理を実装する必要があります。
DXRuby 用の実装には dxruby_sprite_ui があって、Gosu 用の実装には gestalt_ui というものが予定されています。Gosu 版のほうはまだ試作段階で gem 化できていませんが、ゆくゆくは gem にして bundler でインストールできるようになる予定です。
どうして Ruby ?
ぼくにとって書いてて楽しいのが Ruby だったというのが一番の理由です。
それはそれとして、ちょっと思いついたアイデアをすぐに動かして試せる環境がほしいと思ったことはありませんか。ぼくはあります。
紙の上にゲーム画面を書いて、この画面でこうやったら次はこうなって、みたいなの考えるのはとても楽しいです。
さて、このアイデアをもとにさくっとプロトタイプを作りたいと考えます。
Ruby だと思いついたことをすぐにコードに起こせます。思いつきドリブンでゲーム作るのにもってこいの言語だと思っています。
ゲームの GUI を作るのはめちゃ大変
ゲームの GUI というのは、思ってるよりかなり労力を要求されます。タイトル画面ひとつ作るだけでもけっこうな手間です。
そのへんよろしくやってくれるライブラリが、Ruby にはありません。そもそも Ruby でゲームを作るということがニッチなのでそれはしょうがない。
でも、さくっとゲーム書きたいのに、GUI ベースのゲーム作るのが大変なのでは困ります。
そういう自分の中での要請が背景にあって、quincite が作られました。
quincite の使い方
quincite の wiki から抜粋すると、
require 'quincite'
include Quincite
class Node
include UI::Component
def initialize(id)
init_component
self.id = id
end
end
class Nodeset < Node
include UI::Container
def initialize
super
init_container
end
end
ui = UI.build(UI::Nodeset) do
Node {
x 0
y 0
}
end
ui.layout
こういうコードで、シンプルなコンポーネントツリーを構築できます。
Quincite::UI::Component
モジュールをインクルードしたらそれが quincite のコンポーネントになって、さらに Quincite::UI::Container
モジュールをインクルードしたら子コンポーネントを持つことができるようになります(コンテナとコンポーネントを同時にインクルードすることを想定しています)。
UI.build
メソッドに渡したブロックをビルダー DSL と呼んでいて、このブロックの中でコンポーネントツリーを宣言的に記述できるようになっています。例のコードでは Node
を (0, 0)
に配置するだけになっています。
これはコンポーネントツリーを構築するだけしかしないので、実際にゲーム画面に出すには任意のライブラリを使った描画メソッドを実装する必要があります。
DXRuby で quincite を使う
Ruby でゲームを作るためのライブラリに DXRuby というものがあるのでこれを使って quincite の GUI を実際に画面に出すのをやってみます。
quincite 上に DXRuby 用のコンポーネント定義を書くサンプルでもよいのですが、DXRuby にはすでに dxruby_sprite_ui というライブラリがあるので、これを使います。
dxruby_sprite_ui
描画やクリック判定に DXRuby::Sprite
を使っているので dxruby_sprite_uiという名前になっています。DXRuby
モジュールの下に追加されるので DXRuby から使うときはそのまま SpriteUI
と書けば使えます。
クリックすると内容が変わるボタンのデモです。
require 'dxruby'
require 'dxruby_sprite_ui'
SpriteUI.equip :MouseEventHandler
ui = SpriteUI::build {
TextButton {
text 'Hello, world!'
hello = true
onclick -> target {
target.text = hello ? 'Goodbye, world...' : 'Hello again, world!'
hello = !hello
ui.layout
}
}
}
ui.layout
mouse_event_dispatcher = SpriteUI::MouseEventDispatcher.new(ui)
Window.loop do
mouse_event_dispatcher.update
mouse_event_dispatcher.dispatch
ui.draw
end
こんな感じで、DSL でコンポーネントツリーを作って、イベントハンドラを書くだけで、クリックで何かする GUI が作れます。簡単。
quincite がやっていること
描画やクリック判定以外のことはだいたい quincite がやっています。
たとえばクリックイベントの発火は dxruby_sprite_ui で実装していますが、イベントの伝達とイベントハンドラの呼び出しは quincite 側で実装されています。
上の例にない機能だと自動レイアウトがあって、コンポーネントを絶対座標で指定しなくても他のコンポーネントとの位置関係からよろしく自動配置できるようになっています。
ちょっとサンプルのコードが大きいのでリンクだけ貼っておきます。
課題
- リンク先のコードを見てもらえればわかりますが
margin
やpadding
を都度指定しないといけなくてぜんぜん DRY ではないので同じようなスタイルを使いまわせるようにしたいということを考えています。 - リファレンスがないので使い方がぜんぜんわからないことに定評があります。
- ビルダー DSL はでかくなりがちなので外部ファイルに吐き出せるようにしたい気持ちが大きい。
- ビルダー DSL のエラーがわかりにくいのでなんとかしたい気持ちが大きい。
- 動的にコンポーネントを生やす仕組みが弱いので同じような要素の繰り返しなどは力技でコードを書く必要があります。
- キーイベント処理はいまのところ対応できていません。
まだまだ若いライブラリなのでこれからだと思っています。
dxruby_sprite_ui を使ったゲームのサンプル
「野メイド」という育成 SLG っぽいデモゲームを作ったのでそちらをご覧ください。
おわりに
Ruby でゲームを作るための GUI ライブラリを紹介させていただきました。いまのところ使っているのが自分だけしかいないという状況なんですが、今後リファレンスとサンプルコードを充実させていこうと思っているのでどんどん使ってみてほしいと思う次第です。