Smalruby (スモウルビー) を Ubuntu 15.04 にインストールする
今日の午後はプログラミング道場:ProgShouDojoのお手伝いに行ったのだけれども、講師+アシスタントの数>参加者数という状態だった。(午前中の方が参加者が多く盛況だったとのことなので手伝いに行くタイミングが良くなかった。)
手持ち無沙汰気味だったので、自分のマシン (Ubuntu 15.04) に Smalruby の開発環境を用意するという作業をしてみたメモ。
Smalruby は web 版があるのでお試しで触ることはできるのだけど、作ったプログラムの実行までは web 版だけではできないので、手元にインストールするときっといいことあるよ。
続きを読むProcessing AndroidモードでGoogle Play Game Servicesを利用する
某フォーラムにトピックが上がってたのと、(今のところ使う予定は特に無いが)個人的に興味があったのでいろいろ調べてみたまとめ。
長いので結論から先に書くと以下のようにProcessing Androidモードを利用しつつGoogle Play Game Servicesを利用することが出来た。
続きを読む
役に立ちそうで役に立たない少し役に立つかも知れないDXRubyローグライクのノウハウ
とうに旬が過ぎたネタ改変をタイトルに掲げた本投稿はDXRuby Advent Calendar 2014 22日目の記事です。
21日目はあおいたくさんの「DXRubyユーザーのための標準添付ライブラリの紹介(前編)」でした。
「こういうのあると便利なんだけどなー」なんて思ったクラスやメソッドなんかは大体用意されてたり、あるいは誰かが作って公開してくれてたりするんですが、その存在自体を知らないと利用しようがないです。今回のAdvent Calendarであおたくさんは8日目の記事も合わせて便利ライブラリを横断的に紹介してくれていてとても助かりますね!
さてそれでは僕の記事の内容ですが、これまでの記事達ですっかりハードルが上がってる上に、内容が被るのも避けたいトコロ。(次の機会があったら早めの日程にしよう...。)うんうん頭を捻っていたわけですが、そもそも参加のキッカケが、DXRubyでローグライク(不思議のダンジョンライク)を作っていたところAdvent Calendarにお誘いをいただいたというもので。
DXRuby/ruby-processingでローグライクゲーム その1 - 雑念日記
ならばこの不思議のダンジョンライクゲームに関する記事にしようではないかと。完成していない・誰も触れないものについての記事を描くのは若干気が引けますが、タイトルの通り、役に立つかどうかわからない自前不思議のダンジョンライク実装のTipsです。Tipsというか「ここをこうしてみたら捗ったぞよ」的な。
まだゲーム自体のコードを公開できていないので断片的な情報となってしまい(そのため実際のコードと細部が異なる点も多々あり)ますが、もし参考にしていただけたり、あるいは「もっといい方法があるよ!」と指摘をいただけたりすると嬉しいです。
ダンジョン生成
ダンジョン生成のアルゴリズムにはよくあるこういうやつを使っています。
ここでは特に詳細について書きませんが、「ローグライク 生成」とかでググるとたくさん参考情報が出てくるので気になる方は調べてみてください。
前述のようにゲームのソースコードは公開できていませんが、ダンジョン生成部分だけはGithubに置いてあります。
DXRubyに依存していないためどの環境でもお試しいただけるかと思いますが、ドキュメント等々を全然整備していないので誰も使い方がわからないと思います。よろしくないですね。
生成されたダンジョンには敵キャラやアイテムをこれまたランダムで設置します。マップが自動生成されると、自分で定義することがグッと減るから楽でいいですよね。ただし何か問題があった場合に再現できないと困るので、ゲーム全体で単一共通の乱数生成器を使う点に気をつけています。そのあたりは昨年のAdvent Calendarでmieki256さんが詳しく書いてくださっています。
テキストの辞書化
ゲームを作っているとアイテム名やメッセージなどを表示する機会が頻繁にあるため、文字列をよく使います。
例えば単純に作るとこんな感じになるかと思います。
# 薬草 class Yakusou < Item NAME = "薬草" NOTE = "使うとHPが少し回復するよ" end # いやし草 class Iyashisou < Item NAME = "いやし草" NOTE = "使うとMPが少し回復するよ" end # おっさん class Ossan < Character def talk show_message("武器や防具は装備しないと意味がないぞ!") end end # お姉さん class Onesan < Character def talk show_message("あらあなた素敵ね。ぱふぱふしない?") end end
でもこれだとあとから「アイテム説明文が馴れ馴れしいよ!丁寧語にしろ丁寧語に!」みたいな横断的な変更があった場合に、複数のクラス(≒ファイル)にまたがって変更しなければならなかったり、デバッグ中に誤字を見つけた時に修正対象のメッセージがどこにあるかわかりづらくなったりしちゃいます。そもそもの話、ロジック(コード)とデータは分離してまとめておきたいですよね。
ので、yamlにまとめました。
:items: :yakusou: :name: "薬草" :note: |- 薬草【やくそう】 使うとHPが少し回復するよ :iyashisou: :name: "いやし草" :note: |- いやし草【いやしそう】 使うとMPが少し回復するよ :messages: :pafu_pafu: "あらあなた素敵ね。ぱふぱふしない?" :recommend_equipping: "武器や防具は装備しないと意味がないぞ!" :level_up: "playerはレベルlevelに上がった。" :damage: "pointポイントのダメージを受けた。" :kill: "targetをやっつけた。"
yamlについては@vivit_jcさんが13日目の記事で書いてくださったので、そちらを読んでいただければこのセクションはだいたいOKです。
もう少し僕のゲーム内での使われ方について記述すると、以下のような感じで上記のyamlをロードして利用する辞書クラス(またはモジュール)を作ってしまえば、
require 'yaml' require 'active_support/core_ext/hash/keys' # for stringify_keys # 辞書用のモジュール module Dictionary LIST = YAML.load_file('/path/to/dictionary.yml') module ModuleMethods # アイテム情報を返す def get_item_info(key) LIST[:items][key] end # メッセージを返す # optionsを指定した場合、キーに指定した文字列を値に指定した文字列で # 置換する def get_message(key, options = {}) options.stringify_keys! # HashのキーをSymbolからStringに変換 regexp = Regexp.union(*options.keys) # キーから正規表現を生成 LIST[:messages][key].gsub(regexp, options) # 置換して返す end end extend ModuleMethods end
↓こんな感じでテキストを取得できるので、
Dictionary.get_item_info(:yakusou) #=> {:name=>"薬草", :note=>"薬草【やくそう】\n使うとHPが少し回復するよ"} Dictionary.get_message(:pafu_pafu) #=> "あらあなた素敵ね。ぱふぱふしない?"
個々のクラスにテキストをベタ書きする必要がなくなりますし、集約されているためラベルさえ変わらなければ文言の変更があっても辞書ファイルの中身を修正するだけで済みます。
あとこのあたり
:level_up: "playerはレベルlevelに上がった。"
がミソで、「誰々が何々にほげほげのダメージを与えた」みたいな一部を変更して繰り返し使い回すメッセージは、正規表現などを使って置換できるようにしておけば、
Dictionary.get_message(:level_up, player: "勇者", level: 5) #=> "勇者はレベル5に上がった。" Dictionary.get_message(:level_up, player: "魔法使い", level: 3) #=> "魔法使いはレベル3に上がった。" Dictionary.get_message(:kill, target: "スライム") #=> "スライムをやっつけた。"
↑こんな感じでオプションに渡す値を変えるだけで目的のテキストがスッキリ取得できますし、辞書ファイルを見に行かなくてもコードを見ただけで大体どんなメッセージが利用されているかが想像しやすいです。
パラメータのDSL化
ゲームを作っているとキャラクターやアイテムにいろんなパラメータを設定しなければならないですよね。
STGだったら、スピードとか耐久力とか弾の種類とか。RPGならHPとかMPとか攻撃力とか防御力とか。
RPGやローグライクのように同じ種類の敵キャラやアイテムを同じパラメータで使いまわす場合は、種別ごとにクラス化する感じの実装になるかと思います。
# スライム class Slyme < Enemy LEVEL = 1 MAX_HP = 10 MAX_MP = 0 def initialize @level = LEVEL @hp = MAX_HP @mp = MAX_MP @max_hp = MAX_HP @max_mp = MAX_MP end end
↑の例はちょっと極端ですが、ごちゃごちゃしちゃっていて、同じ感じでたくさん敵キャラクターを定義するのはしんどいです。もうちょっと楽ができつつ見通しが良いようにしました。
まずは上位のクラスでパラメータを簡単に記述できるような仕組みを用意します。例えばこんな感じ。
# 敵キャラクターのベースとなるクラス class Enemy class << self # HPの設定 def hp(value) @default_hp = value end # HPの取得 def default_hp @default_hp || 1 end end def initialize @hp = self.class.default_hp @max_hp = self.class.default_hp end end
パラメータはHPだけじゃないので黒魔術使ってもうちょっと増やします。やってることは↑と同じです。
# 敵キャラクターのベースとなるクラス class Enemy class << self [ :hp, # HP :mp, # MP :level, # レベル :power, # 攻撃力 :defence, # 防御力 :exp, # 経験値 ].each do |param_name| define_method(param_name) do |value| self.instance_variable_set("@default_#{param_name}", value) end define_method("default_#{param_name}") do self.instance_variable_get("@default_#{param_name}") || 1 end end end def initialize @hp = self.class.default_hp @max_hp = self.class.default_hp @mp = self.class.default_mp @max_mp = self.class.default_mp @level = self.class.default_level ...(省略)... end end
上のEnemyクラスを継承すると、さっきのスライムの定義は以下のようになります。
# スライム class Slyme < Enemy level 1 hp 10 mp 0 power 3 defence 2 exp 5 end Slyme.new #=> #<Slyme:0x00000001591e78 @hp=10, @max_hp=10, @mp=0, @max_mp=0, @level=1, @power=3, @defence=2, @exp=5>
ドキュメントっぽいコードになって、敵キャラクターの能力が一目で見渡せます。これなら敵を増やすのも楽チン。
ここからは妄想の話で、ゆくゆくは攻撃パターンとかもタイプ化してラベルで指定できるようにすれば、個々の敵キャラクタークラスにコードを書く必要が無くなってこれ↓で完結しちゃったりして、
# スライム class Slyme < Enemy level 1 hp 10 mp 0 ... attack :normal # 通常攻撃 weak :fire # 火が弱点 end
であれば最早クラス定義そのものもコードを書く必要はなくて、以下みたいなyamlから動的に敵キャラのクラスを定義できるようになるかもしれません(それが良いか悪いかは別として)。
:enemies: :slyme: :name: "スライム" :image_file: "images/enemies/slyme.png" :attack: :normal # 通常攻撃 :weak: :fire # 火が弱点 :status: :hp: 10 :mp: 0 :cerberus: :name: "ケルベロス" :image_file: "images/enemies/cerberus.png" :attack: :three_way # 3方向攻撃 :status: :hp: 250 :mp: 120
そうなるとだんだんフレームワークっぽい感じになってきて、最終的に「誰でもyamlを書くだけでオリジナルのローグライクゲームができちゃう!」みたいな夢がひろがりんぐ。
妄想終わり。
キーコンフィグ
操作性の悪いボタン割り当ては大変ストレスフルです。可能であれば自らが一番気持ちよく操作できるボタン配置でプレイしたいものです。
それなら、通常こんな感じで書くところを、
# 攻撃ボタンが押されたら if Input.key_push?(K_Z) player.attack end
こんな感じで書いてあげるのはどうでしょうか。
# デフォルトのキー割り当て DEFAULT_KEY_CONFIG = { attack: K_Z, # 攻撃 menu: K_X, # メニュー sort: K_C, # アイテムの整理 }.freeze # 既存のキー設定の読み込み # 無ければデフォルトの設定 @key_config = load_key_config || DEFAULT_KEY_CONFIG.dup if Input.key_push?(@key_config[:attack]) player.attack end
あとはキー割り当て用の画面を用意してあげて、@key_configの中身を変更可能にしてあげればキー設定が可能になります。上の例ではキー設定はただのHashなので、これもやっぱりyamlに吐いておいて、次の起動時にそのyamlを読み込むようにしておけば、変更したキー設定の永続化も可能です。
ただし、ゲームプレイ続行不可能になるようなキー設定にしたり、一つのボタンに複数の役割を割り当てたりができないように、設定内容を検証する処理を行わなければならない点に注意です。
おわりに
以上です。
あまりローグライクやDXRubyに特化した内容が書けなかったのが心残りですが、これら細々としたホニャララを積み重ねてDXRuby不思議のダンジョンライクは鋭意製作中です。
...嘘です。プライベートが何かと忙しかったりCrypt of the NecroDancerとかにハマったりしてしまって現在進捗はズタズタです。
いずれにせよ、何かひとつでも興味を持っていただけるTOPICがあったのであれば幸いです。
明日23日目はみれいゆーさんの「DXRubyでノベルADVエンジンを作った(仮)」です。
ノベルゲーム制作というのもかなり人気のあるジャンルですよね。僕なんかはゲーム作ろう!ってなると、剣と盾持ったキャラクターをぐりぐり動かして...という方向に進みがちなのですが。明日の記事に関心が高い人も多いのではないかと思います。乞うご期待!