2016年9月11日日曜日

ポーションの使用

しばらくブログの更新を後回しにしてしましました。
スクリプトを中心に、かなり進んでいます。
今から連投しますが、まずはポーションの使用を。


ポーションは以前にウディタで作った時と同じ以下のような仕様です。

  • HPポーションとSPポーションがあり、どちらも利尿作用があるが前者のほうが強い
  • 特殊コマンドとして実装し、ショートカットキーで使用できる
  • 個数という概念はなく、無限に使用できる
  • 回復量は最大HP・SPの半分


使用方法は以下の3つあります

  1. マップからショートカットキーで使用
  2. 戦闘中に特技として使用
  3. メニュー画面から、特技として使用

実際のゲームプレイの中で主に使われることを想定しているのは、1と2ですが、特技として実装したものをメニューから使えなくするのも不自然ですし、キーボードショートカットに関する説明を書いておけばチュートリアルで見逃された場合の救済にもなるので、残しました。

ツクールでスクリプトをがっつり書いていると、コモンイベントやその他のツクールのGUI機能を使うか自分で書くか迷うものですが、特殊効果のあるアイテムや特技は、ちょうどこの境界上にある要求だと感じました。

結局のところ、以下のようになりました。

  • ショートカットキーからの使用は全てスクリプトで実装する
  • 戦闘中とメニューからの特技使用は、利尿度の更新以外はツクールの機能で実装する


コードはこんな感じになりました。酷いラビオリコードですね。
これがベストプラクティスだったかどうかは疑問ですが、それなりの理由があってこうなっています。

なお、check_inputが、毎フレーム呼び出される部分で、ここから他のメソッドが呼び出されます。


そして特技はこんな感じです。回復やダメージの計算式を書くことができるのは、確かウディタには無かった仕様です。便利ですね。

利尿度だけはスクリプト側でオブジェクトのフィールドとして管理している値なので、コードを介さなければアクセスできません。
しかし、チュートリアルに、アイテム使用時の処理を再定義・拡張してメモ欄を参照するようにする方法がありました。

これに倣って以下のようにしました。また、戦闘中のアイテム使用に関しても同様にしました。

これでアイテムのメモ欄に書いた任意のコードを使用時に実行できます。
これを利用して、別出しした尿意更新の部分の処理だけを読んでいます。コードが断片化してしまったのは、このようなアクセスを可能にするためです。


さて、これでポーションの使用が可能になるはずでしたが、ショートカットキーが反応しないことがあることがわかりました。
どうやらコモンイベントからの呼び出しが良くないようです。必ずしも毎フレーム呼ばれるわけでは無いのでしょうか。
そもそも常時並行実行を目的にコモンイベントを用いる方式は良くないと思っていたところなので、結局インターバル実行のコモンイベントは廃止し、Scene_Map.updateにまとめることにしました。


で、こんな感じになりました。上から順に、ショートカットキー、戦闘中、メニューから。





はい、こんな感じです。
次は何について書こうかな。

2016年9月7日水曜日

積み残し部分の実装と早速の方式見直し

前回の記事の最後に、以下の2件が積み残しであると書きました。

  • システム情報から、戦闘中フラグを参照し、戦闘中は表示しないようにする。
  • 同、メニュー表示中


上記の、実装方法に関する記述はウディタでの経験を元にした推測なのですが、実装しようとしたところ、「システム情報からフラグを参照」して表示を切り替えるという方法での実装は不可能であることが分かりました。

ツクールには戦闘中などのフラグは無いものの、「シーン」という概念で現在の状態が管理されており、現在の「シーン」が戦闘であるか、メニューであるかという確認方法を取れば可能だということが分かりましたが、それとは別の箇所で問題が発見されました。

それは以下のような方式で処理が呼びされてる仕組みになっていたことでした。

マップシーン
↓
コモンイベント
↓
スクリプト(自作)

つまり、従来のコモンイベントからスクリプトを起動する方式では、マップ表示中しか実行されません。
そして、スプライトは一度表示したら自動的に表示され続ける仕組みです。どこかで明示的に非表示にする必要がありますが、メニューが表示された場合や戦闘が始まった場合には、非表示にするタイミングすら無くスクリプトは呼ばれなくなります。


この問題を修正するにあたり、以下のような既存のスクリプトを参考にしました。

これは、コメントにある通り、アップデートにより修正された部分の記述です。
既存のクラス定義を書き換えることによって実装されています。
クラス名は既存のものであり、メソッド名はaliasを用いて既存のものを退避することで、新しいメソッドの中で旧来のメソッドを呼ぶことを可能にしています。

これに倣い、以下のように実装しました。

この部分では、マップシーンで定義されている、フレーム更新処理を上書きし、ステータス表示処理を追加しています。
シーンが遷移しつつある場合は非表示になるようにしているので、マップシーンから別のシーンに切り替わった場合は、非表示になった状態で当該箇所のスクリプトが呼ばれなくなります。
再びマップシーンに移行した場合、再び表示状態に戻ります。


そういうわけで、この通り上手く行きました。




次回は、尿関連ステータスの増え方を自然にする調整かなぁ。
また波とか作って無いし。

2016年9月6日火曜日

スクリプトの構造とステータス表示

以前にウディタで、画面左上にHP、SP、尿量、尿意を表示していましたが、あれと同じことをします。
ツクールのコードを弄る部分に関する記事は今回が初めてなので、全般的なことも交えて紹介していきます。


まずスクリプトですが、エディタ機能がついていて、ここにRubyで処理を記述できます。

基本的に自作コードは、この「素材」という部分に追加するのが望ましいようです。

このPee_Holderというクラスは、まだ実装中ですが、おしっこ関連のステータスと処理を司る部分です。
一番下の行でインスタンスを生成していますが、このようなクラスやメソッドの定義ではない部分は、ゲーム起動時に一回実行されることが、調査の結果分かっています。
game_systemというシステム全般に関するクラスのコメントに「このクラスのインスタンスは $game_system で参照されます。」という記述があってので、それに倣ってシングルトン的に扱うためにこのようにしています。


全体的な設計ですが、予定通り毎フレーム呼び出される処理は1つにまとめ、そこから各メソッドを呼び出すようにしています。

これが、そのメソッド

そしてこちらが呼んでいる部分のコモンイベント。フラグが立っている場合のみ並列実行されます。


なお、現状では呼び出される間隔はコード側では制御していません。ツクール自体の並列実行の実装に依存しています。その辺の仕様についての調査と、複数環境によるテストプレイが必要になりそうですが、ダメならダメで何か噛ませればいいので、今は気にしないで実装していきます。



さて、ステータス表示そのものの話に戻りましょう。
ちょっと調べたところ、ウディタのように文字列を簡単に画面上に自由に表示する機能は無さそうです。また、重要箇所くらいは見やすいレイアウトにこだわりたいので、使用する文字を画像として描きだして表示することにしました。

色々試した結果、良さそうだったフォントと色で、画像を作りました。

なお、数字個別の画像の他に、項目の画像も作りました。



こういう画像表示の機能があるので、コードでこれと同じことが出来ればと探しましたが…



自分の理解不足もあり、それらしいものが上手く動かず、試行錯誤の末にシンプルなSpriteクラスとBitmapクラスを使用する方法が良さそうであることが分かりました。


Spriteクラスのインスタンスは、今回使った限りで以下のような情報を持っています。
  • x : 表示位置のx座標
  • y : 表示位置のy座標
  • z : 重なり順
  • bitmap : Bitmapクラスのインスタンス。これで画像を指定する
  • visible : 表示状態であるかどうか
bitmapさえセットされていて、表示状態であれば、指定した位置にbitmapの画像が表示されます。

なおBitmapのクラスのインスタンスは、今回使った限りでは、画像ファイルのパスしか渡していないので、画像そのものに関する情報だけを管理しているものと考えて良いでしょう。


以上を踏まえて、以下のような実装にすることにしました。

  • 最初に、使用する画像1つにつき1つのBitmapのインスタンスを生成し、以降それを複数のSpriteインスタンスで使い回す
  • 最初に、文字表示位置1つにつき1つのSpriteのインスタンスを生成し、以降そのbitmapの指定を逐次変更する


最後に、コードを少し紹介します。

画像読み込み部分です。0から9までは配列のインデックスと画像の数字が一致するようにしています。


Spriteインスタンス生成部分です。直後に位置決めもしています。反吐が出るほどにクソベタ書きですね。


そしてこちらが、毎フレームの処理から呼び出されるBitmapインスタンスの付け替え部分です。


レイアウトに関する情報をハードコーディングしたり、似たような処理をコピペしたりと、きったねぇソースですね。いいんです。きったないものを封じ込めて外に臭いが漏れないようにするためのカプセル化です。


さて、前回と同じ画像ですが、こんな感じに表示されます。



以下、今思い付く限りの積み残し。
  • システム情報から、戦闘中フラグを参照し、戦闘中は表示しないようにする。
  • 同、メニュー表示中


2016年9月5日月曜日

オープニングイベント

さて、オープニングを作ったので紹介します。


オープニングはマップ上のイベントとして実装しています。
ゲーム開始地点のあるマップに、出現条件の無い自動起動イベントとして配置しているため、メニュー画面からマップに移行した直後に、このイベントが実行されます。



なお、オープニングが複数回実行されることを避けるため、イベントの最後に二つの操作をしています。
一つは、「イベントの一時消去」。もう一つは「セルフスイッチ」と呼ばれる同一イベント内だけから参照されるある種のフラグの操作です。


「イベントの一時消去」では、同じマップにいる間、イベントが一時的に消去されますが、他のマップに行って帰ってくると、またイベントは有効になります。

そこで、セルフスイッチを出現条件とする「イベントページ」を追加し、スイッチON以降はそちらが実行されるようにしています。




続きまして、実際のオープニングの画面を紹介します。

ニューゲームを選択すると、画面袖からキャラが歩いてきます。




 キャラが喋ります。今作で主人公を指導する役割になる教授さん。何の教授かは知らん。



主人公ぬいちゃん。


なお、キャラ画像は、こういう生成機能で作りました。


仮置きのつもりでしたが、まあまあ気に入ったので、もう自分で描かなくても良いかも知れません。


イベント内でのキャラの移動は、こういうコマンドを使っています。


その辺を歩きながら会話をします。


室内に移動し、なおイベントは続きます。

移動はこういうコマンドで実装してます。


なお、屋内にマップを移動しましたが、実行中だった屋外マップのイベントは終わりまで実行されます。


さあ、続いてモンスターが現れて、戦闘になります。



この戦闘は、ぬい一人で行うため、直前と直後でメンバーの入れ替えを行っています。
またマップ上のスライムはイベントとして実装しており、表示・非表示切り替えだけの機能を持ったものです。このイベントが参照しているスイッチを、戦闘後に切り替えています。


これにより、戦闘後スライムが消えます。


先程と同じように場所移動コマンドを使い、部屋内に移動します。

なお、この絵面を実現するために、移動直前に再びパーティ編成で教授を抜いています。
部屋内には、最初から教授をイベントとして配置しています。


さて、ぬいちゃんはおトイレに行きたいようです。


会話が終了し、操作ができるようになります。
先行公開になりますが、イベントの最後にフレームごとのステータス更新と、ステータスの画面表示のフラグを立てているので、画面上に、HP、SP、尿量、尿意が表示されるようになります。


以上、オープニングでした。
直後にほんの少しだけチュートリアル会話が入る予定ですが、それはまた別の機会に。
次回の更新では、このステータスの表示と更新について説明します。

マップ作成

さてさて、黙々と製作を進めてますが、最初のバージョンとして公開する体験版のマップが既にほぼできています。


こんな感じの建物です。

左のあたりが壊れていて、そこを迂回してトイレに行くところまでを初期公開体験版とする予定です。


中にはこんな感じの廊下があって


こういう似たような感じの部屋が複数あります。


各部の連結イベントも既にできています。
あとはアイテムやメッセージ表示イベントを配置すれば完成です。



それにしてもツクールのマップエディタは頭良すぎてキモいですね。良いか悪いかと言われたら良いです。ただ、背後の仕組みもちゃんと知っておきたいところですね。

・全部で3層に分かれていて、どの層に配置されるかはマップチップが持っている情報による。
・自動的に4スミなどが整形される性質のチップがある。
・Shiftを押しながらだと自動整形機能が無効になる。
・右クリックによるコピーは、見たまんま。つまり基本的に全層。

要点はこんなとこですかね。


それはそうと、実は既にオープニングイベントと、ステータス表示モジュールができています。
次回の記事では、その辺を紹介していきます。

2016年9月1日木曜日

おしっこ我慢シミュレーター設計


モジュール分割、こんな感じかなぁ。

悩ましいのは、おしっこ我慢関連のステータスをキャラクターに属するものにするどうか。
主人公は一人だから、そういう実装はもしかしたら不要な複雑性かも知れない。しかし、生理的なものである以上、キャラに属しているほうが自然だし拡張性はあるはず。

もう一つは、これをクラスにまとめると、結局ひとまとめになってしまい、そうなった場合にパラメータのアクセス制限が言語の機能レベルでやりにくいこと。
参照はpublicで更新はprivateとか出来たっけなぁ…


2016/09/03 追記
Rubyではattr_accessorの代わりにattr_readerを使えば、参照限定で外部からのアクセスを許可できるようです。

ビルド・アンド・スクラップ

久しぶりの更新です。
作っていたものは、そこそこ完成間近でなんか飽きて放置してます。
RPGをやっていてラスボス直前で飽きる現象に似ていますね。

今回の記事では、こうして前作がエターナった原因分析と対策として考えたことを垂れ流しつつ、今後の展望について語ります。
斃れた冒険者は、倒れた時のレベルのまま、半額のゴールドと、拾ったままのアイテムを持って再びダンジョンに向かうのです。


さて、分析と対策です。

仕様

仕様の絞り込みの不十分さが一番大きい原因だったと思います。
そもそも一作目なのでスモールスタートにしようという頭はあったのですが、"試しに作ってみた1分で遊べるゲーム"的なものを見かけて、まだまだ大きすぎたと思いました。

特にその点が顕著なのが、モンスターのバリエーションやアイテムの種類、またマップの広さでした。
戦闘や成長のバランスなど、触っているうちに色々試してみたくなって手を出したのですが。やりすぎでした。

もっとコア部分だけに絞って削るべきでしょう。
コアとは何か。私のゲームの場合、それは他ならぬおしっこ我慢シミュレーターでしょう。
自分の作ったゲーム音楽を使いたくて東方を作ったZUN氏や、自分の作った人工言語を使いたくて指輪物語を書いたトールキンのように、私もおしっこ我慢シミュレーターを使いたくてゲームを作るのが良いのでしょう。

そう考えると、マップの広さは切り捨てることが出来ません。トイレまで行くのに時間がかかる。トイレに戻りたいのに、帰り道が分からないといった焦燥がゲームの楽しみの一つのコアだからです。
しかし改善すべき点が無いわけではありません。前作では、サクサク探索する楽しみを実現しようと、歩行速度をかなり速めに設定していました。そのため、いわば"マップの消費が速い"状況が生まれてしまいました。マップ内のコンテンツ量が多い必要はありませんが、マップ自体の量は多くならざるを得ません。コンテンツは開発者が創意工夫をしてゲームが面白くなるように作る必要がありますが、マップ自体は道順およびアートワークとして破綻しない範囲で単純作業で作ることができます。私はマップを操作するためのツールを作りましたが、あのような非合理な作業を強いられたのは、今から思い返せば仕様の甘さからの予定調和でした。
また、更に言えばマップを細かく分けずに、広大な一枚マップにしてしまったのも、マップの再利用性を下げる選択でした。あれはあれで面白いと今でも思っていますが、やはり初回では切り捨てるべき要素でした。

次回作

そこで次の作品は、以下のようにします

  • アイテム : 初期装備の他に装備品を1、2程度
  • 敵 : ダンジョン一つ分程度のバリエーション
  • 歩行速度 : 通常程度
  • マップ : 比較的小さいマップを繋ぎ合わせる。適宜テンプレートとなるマップを作る

チュートリアルイベントのようなものだけが遊べるゲームとし、イベントによて最初からある程度おしっこが溜まっている状態から始めようと思います。


プラットフォーム

次に選んだツールの問題です。
ウディタはコードを描くことができず、基本的にプルダウンなどのGUIで処理を入力する必要があるため、ある程度複雑な処理を書こうとすると、生産性が良くありません。
RPG作りの概念を学ぶ入門としては良かったのですが、ゲームシステム面を重視する今作に適した選択ではありませんでした。
そういう意味では、スクラップ・アンド・ビルドはいずれ必要でした。

次回作

RPGツクールVX Aceを入れました。Rubyが書けます。個人的にはJavaの次に使い慣れていて、C#の次に好きな言語です。
とりあえずオブジェクト指向的に書きやすいので、大抵のことはどうにでもなるでしょう。


モジュール分割

これも慣れないプラットフォームで手探りで実装を進めたせいという点もあるのですが、それにしても今回の設計はあまりに場当たり的で酷いものでした。

次回作

おしっこ我慢シミュレーターは今後も何かにつけて書く可能性があるので、メモ書き程度の汎用的なドキュメントを作成すべきでしょう。後で上げます。



まあ、こんなとこです。
しばらく止まってたこのブログですが、また動きますよ。