2015年12月22日

DXRubyAdventCalender2015 22日目

DXRubyAdventCalender2015も22日目になりましたね!

今回はDXRubyWSのGUIとしての見た目のお話になります。

WSにはthemaフォルダというものがありまして、ここにGUIのデザインというか描画の仕方が定義されているRubyスクリプトがあります。
今はguibasic一個しかありませんが。

WS.set_theme('フォルダ名')でフォルダの中の定義ファイルをすべて読み込んでGUIの見た目を変えることができます。

guibasicを読み込むとこのような感じ。

WS1.jpg

何も読み込まないとこのような感じになります。

WS2.jpg

定義ファイルの中身は単純に各コントロールの描画メソッド部分をオーバーライドするというものなので、誰でも簡単に変更できます。

では、実際にthemeを作ってオリジナルのGUIを作ってみましょう。

簡単なのはボタンでしょうか?
最近よく使われているフラットデザイン的なのを作ってみましょう。

まずはthemeフォルダ内にguiflatというフォルダを作ります。

次に適当な名前のRubyスクリプトファイルを作ります。
これもguiflatとでもしておきましょう。

さてボタンの描画を変えたいのでstandardguiのbuttonのスクリプトを見て描画部分を見つけてきましょう。


module WS
class WSButtonBase
# set_imageで@image[true](押された絵)と@image[false](通常の絵)を設定する。
# オーバーライドしてこのメソッドを再定義することでボタンの絵を変更することができる。
def set_image
# 画像を再作成する前にdisposeする
@image.each_value{|image| image.dispose if image.disposed?}

# 通常時の画像を作成
@image[:usual] = Image.new(@width, @height, COLOR[:base]).draw_border(true)
# 押下時の画像を作成
@image[:pushed] = Image.new(@width, @height, COLOR[:base]).draw_border(false)
# キャプションの描画
if @caption.length > 0
width = @font.get_width(@caption)
@image[:usual].draw_font_ex(@width / 2 - width / 2 ,
@height / 2 - @font.size / 2 ,
@caption, @font, {:color => @fore_color, :aa => false})

@image[:pushed].draw_font_ex(@width / 2 - width / 2 + 1,
@height / 2 - @font.size / 2 + 1,
@caption, @font, {:color => @fore_color, :aa => false})
end
refreshed
end

def render
set_image if refresh?
change_image
end

def draw
super
if self.image && self.activated?
self.target.draw_line(self.x - 1, self.y - 1, self.x + @width, self.y - 1, C_BLACK)
self.target.draw_line(self.x - 1, self.y - 1, self.x - 1, self.y + @height, C_BLACK)
self.target.draw_line(self.x + @width, self.y - 1, self.x + @width, self.y + @height, C_BLACK)
self.target.draw_line(self.x - 1, self.y + @height, self.x + @width, self.y + @height, C_BLACK)
end
end
end


大体この辺りでしょうか?
あらかじめ画像を作ってキャッシュしておいて、ボタンの状態によってそれを切り替える方式のようなので、その画像を作るところを書き換えれば良さそうです。


module WS
class WSButtonBase
# set_imageで@image[true](押された絵)と@image[false](通常の絵)を設定する。
# オーバーライドしてこのメソッドを再定義することでボタンの絵を変更することができる。
def set_image
# 画像を再作成する前にdisposeする
@image.each_value{|image| image.dispose if image.disposed?}

# 通常時の画像を作成
@image[:usual] = Image.new(@width, @height, COLOR[0, 102, 153])
# 押下時の画像を作成
@image[:pushed] = Image.new(@width, @height, COLOR[0, 50, 76])
# キャプションの描画
if @caption.length > 0
width = @font.get_width(@caption)
@image[:usual].draw_font_ex(@width / 2 - width / 2 ,
@height / 2 - @font.size / 2 ,
@caption, @font, {:color => @fore_color, :aa => false})

@image[:pushed].draw_font_ex(@width / 2 - width / 2 + 1,
@height / 2 - @font.size / 2 + 1,
@caption, @font, {:color => @fore_color, :aa => false})
end
refreshed
end


さてこれでWS.set_theme("guiflat")で読み込むとこうなります。

WS3.jpg

(思ってたのとなんか違うというか。
ボタンのサイズを大きめにするとか他のコントロールも書き換えたらそれっぽくみえますかねえ?)

ちなみに余談ですが、テーマ読み込まない状態のGUIは全部DXRubyの図形描画メソッドで書かれてます。
draw_lineなどを駆使してドット絵を描くがごとく描かれてるわけですね。
まさにDXRuby純正といった感じです。

guibasicは逆にコントロールの画像ファイルが用意されててそれをつかって描画しています。
画像データはソースに貼り付けてあるのでスクリプトの他に画像をダウンロードする必要はありません。

両極端な感じですがサンプルとしてはおもしろいような・・・

こういった感じでコントロール毎に描画処理を変更していけばオリジナルのGUIの完成です。
何か作ったら是非mirichiさんのGitHubに是非プルリクしましょう!

次回はあおたくさん『野メイド拡張講座』です!
あおたくさん作DXRubyACゲーム野メイドを自分好みに作り替えてイチャラブできる…そんな夢が叶う…

おまけ

guibasicを作る時に色の参考にしたサイト


RGBだけじゃなくて色んな色表現がのっていて、明度や彩度が違うカラーの一覧も載っていてとても便利でした。

ソースに貼る画像を文字列にするツールを作った


いちいちコマンドラインでやるのが面倒だという方は使ってあげてください。
アイコンにドラッグ&ドロップすると画像と同名のテキストファイルを作ってくれます。
これをDXRuby上でload_from_file_in_memoryにかければで画像に戻ります。
(文字列はunpack('m')[0]で渡してください)
あとocraで固めたものなので環境によっては動かないかもしれません。
posted by 鳴海つかさ at 20:53| Comment(0) | TrackBack(0) | 日記

2015年12月14日

DXRubyAdventCalender2015 14日目

この記事はDXRuby Advent Calendar 2014の14日目です。

今年も昨年に引き続きDXRubyWSを使った作例の紹介になります。

■DXRubyWSを使ってゲームっぽいもの作ろう



2014年はDXRubyWSを使って作ったエフェクト作成ツールと、そのツールで作ったエフェクトをプログラム上で再生するデモを紹介しました。

今年はDXRubyの基本に立ち戻りDXRubyWSを使ったゲームを作ろうと思います。

DXRubyWSはWindowSystemの名の通りDXRuby上でWindowベースのGUIを作るのに適したライブラリです。
機能を見るとどっちかというとアプリ向けでありゲームに使うにはオーバースペックという意見が見られます。
ただ、スプライトを階層化して管理する機能を上手く使えばゲーム制作にも便利だと思います。

実用上はマウスで操作するゲームのメニュー部分なんかをWSで作って、ゲーム部分は自分で作るみたいな使い分けをするような形になるでしょうか。

どのような形が一番いいのかはわからないですが、今回はWSの機能を積極的に使う方向で考えてみます。

まずはどういうゲームがいいか考えましょう。

WSにはマウスイベントを受け取る機能があります。
WSのコントロールオブジェクトに対してクリックやドラッグなどを行うと、それに応じて処理を実行してくれるので、それを軸にしてみます。

マウスを使うゲームというとOSU!みたいなタイプの音ゲー、メイドインワリオのタッチペン使って遊ぶようなミニゲームなどが候補でしょうか。

ということで、今回はうちのマスコットエイプリルの頭をなでなでするゲームで行きます。

■エイプリルなでなでゲーム(仮)



まずは簡単に構造を考えましょう。

WSでアプリを作る時はウィンドウを作ってそこにボタンやらチェックボックスやらコントロールを配置します。
WSControlオブジェクトはWSContainerによって管理され、コンテナが配下のコントロールにイベントを流します。
ウィンドウがコンテナで、ボタンなどがコントロールですね。

標準コントロールのウィンドウをそのまま使ってもよいですが、標準のウィンドウはいろいろと機能がついていて、今回はそれらは使わないのでGameScreenというコンテナを作ってそれにコントロールを管理してもらうことにしましょう。

さて、他に必要なものは、なでなでされるキャラクターですね。
これは単純コントロールとして作ってもよいのですが、コンテナとして内部をパーツ分けすると別のものに使い回す時に便利だと思われるので、Charaというコンテナを作って中にパーツを配置していく形にします。

中身についてですが、せっかくなので表情の変化なども行えるようにキャラクターのベースと顔を分けておこうと思います。
また、マウスで左右にドラッグするとなでなでしていると判定される判定用のコントロールも作りましょう。

必要なものと構造をまとめるとこのような感じですね。

WS.desktop
聾SGameScreen
 聾SChara
  聾SFace
  聾SNadeNadeArea

全部説明すると長くなってしまうので特に重要な部分だけ簡単に解説します。


module WS
class WSChara
### なでなで判定エリア ###
class WSNadeAreaA < WSControl
include Draggable
def initialize(cx, cy, width, height)
super(cx, cy, width, height)
end
end
end
end


Draggableモジュールをインクルードするとドラッグ処理に関するイベントを受け取ることができるようになります。
コントロールをドラッグした際自動でon_drag_moveメソッドが実行されます。
また:drag_moveとして登録されているハンドラが実行されます。
WSNadeAreaA内にon_drag_moveメソッドを作ってその中で処理を行ってもよいですし、上の構造からハンドラ
を登録して処理をさせても構いません。
処理の流れや構造にあった方法を選択しましょう。
それらのメソッドやハンドラはドラッグ開始点からマウスのx,y座標がどれくらい移動したかを引数としてうけとります。
例えばウィンドウをドラッグした場合、受け取った移動量からウィンドウの座標を求めて再設定する処理を書くことでウィンドウの移動処理が作れます。

今回は移動量からなでる速度を求めて丁度いい速度の時だけスコアがあがる。
早すぎたり遅すぎたりしたらスコアが下がるようにします。


module WS
class WSChara < WSContainer
def initialize(cx, cy, width, height)
super(cx, cy, width, height)
@stroke = 0
@last_stroke = 0
@score = 0
create_control
end

def create_control
area = WSNadeAreaA.new(50, 20, 220, 100)
add_control(area_a, :c_area_a)
area_a.add_handler(:score_reset){ score_reset }
area_a.add_handler(:drag_move, method(:nadenade))
end

def nadenade(obj, dx, dy)
@stroke = (@last_stroke - dx).abs
@last_stroke = dx
case @stroke
when 1
@score += 2 if @score < 20
when 2...4
@score += 4
when 5...8
@score += 1
when 9...24
@score = -200 if @score > -200
when 25...640
@score = -500
end
end
end
end


ざっくりとこのような感じで。
ドラッグ時のイベントで渡されてくる移動量dxから1フレームでどれくらい動いているかを判定して丁度いい速さなら
@scoreに+4、ちょっと早いなら+2、遅いなら+1、とても早いなら大きくマイナス。
このようなメソッドをWSCharaに作り、配下のWSNadeAreaオブジェクトのarea_aの:drag_moveハンドラとして登録します。
area_aがドラッグされる度にハンドラとして登録したnadenadeが実行され、引数としてドラッグ量が渡されるのでそれを元にスコアが算出されます。

このように、WSではドラッグやクリックなどの判定や実行結果などを利用し処理を実行させるための基本的な処理が用意されていてお手軽に作ることができます。

■まとめ



ということで、今回突貫で作ったゲームがこちら。

【エイプリルなでなでゲーム】
http://www.abish.sakura.ne.jp/el/NTAC2015.zip


adc15.jpg

遊び方
・エイプリルの頭のあたりを丁度いい速度で左右にドラッグしてなでなでしてください。
・エイプリルが喜ぶと表情が変わります。
・最高にハイになるとなんかキラキラします。
・エイプリルが嫌がるとしょんぼりします。  

雑なゲームですが是非さわってみてください。
これ判定範囲や表情パターンなどを変えたらドキドキ魔女裁判みたいないかがわしいゲームが作れそうですよね。
また今回作ったWSCharaから判定を取っ払ってWSFaceに目パチや口パク処理をつければ立ち絵のひな形として使えそうですね。
最小構成にするという意味ではどうしても自分で一から作るがよいことになりますが、アイデア次第ではメニューなどのインターフェース以外にもWSを使っていろいろなゲームパーツがつくれそうです。

明日は土屋つかささんの『ショートショートなノベルゲームを作ってみた』です。
ノベルエンジンの制作者である土屋さんのノベルゲーム、とても楽しみですね!
posted by 鳴海つかさ at 22:15| Comment(0) | TrackBack(0) | 日記