RPGツクールVXAceの網

RPGツクールVXAceの製作過程で気が付いたことをメモする。

RPGでギャンブル

RPGでゲームの中にカジノを作ろう。

と思って作ったこともある人もいるだろう。カジノに限らず、運や確率に左右されるものは失敗と成功が絡まり人間の興味をそそる。しかし、RPGならではの問題がつきまとう。

 

単純な例を挙げる。サイコロを2個振って両方とも1(ピンゾロ)が出たら掛け金20倍、それ以外だと掛け金没収という少しえげつないギャンブルを、RPGのゲーム内通貨を利用して行うシステムを作るとする。賭ける金額は自由で、一攫千金のチャンス。

このゲームを作ってしばらく遊んだとき、多くの人がこう考えるだろう。

「セーブしたあとに所持金全部賭けよう。掛け金が没収されたらリセットしよう。」

ほとんどのRPGは、セーブというシステムが存在し、ゲームの状態を保存できる。いわばこれは、あらゆる失敗をなかった事にするシステムと言える(プレイヤーがプレイにかけた手間と時間はなかったことにならないが)。

これにより、特にハイリスクハイリターンな要素がある場合、セーブとリセットを繰り返して最高の報酬を偶然に(必然に)出すことが出来、下手をするとその結果がゲームバランスに大きく影響してしまうこともある。

しかし、トライアンドリセットの繰り返しはもはや作業であって、ドキドキ感はなく、ギャンブルがギャンブルでなくなってしまっている。ここでRPG内でのギャンブルのドキドキ感を維持する方法をいくつか考える。

 

(1)オートセーブ

 まず思いつくのがリセットをできなくしてしまう方法。賭けの結果がわかった瞬間に強制的にセーブしてしまえば、後戻りできなくなる。ギャンブル自体の緊張感は成立するが、いくつか問題点がある。まず、セーブは結果がわかった本当に直後でなければ意味がない。結果が表示されたあと、プレイヤーにセーブファイルを選択させているようでは、その間に安易にリセットされてセーブの強制力は無い。また、プレイヤーのためを考えるなら、ゲーム前に「強制セーブされるが良いか?」というアピールが必要。興味本位で所持金を全部かけて、結果表示後に後戻りできなくなると、どうしようもなくなる。ギャンブル性は維持されるが、少し使い勝手が難しい。

(2)リセットすると何かペナルティがある

 カジノとセーブポイントとの間には険しい山脈があり、行くのに苦労する。この場合、カジノで負けてリセットすると失うものも大きいため、リセットは躊躇される。また、ポーカーのダブルアップのように引き際を選択できるのも手だ。次にはずすと今までの分はなくなるが、より利益も高く、この勝負の間はセーブができなければ良い。ただ、やはり元の掛け金が大きいと時間がかかってでもリセットしよう、という考えになることもある。

(3)常にオートセーブ

 オンラインゲームやソーシャルゲームで主流の、とにかく何かあるごとにセーブされるシステム。後戻りが効かないため、逆にギャンブル性の高い仕掛けを多く出来る。ただ、RPGでは過去のあるシーンに立ち戻ってリプレイしたり、セーブポイントまで後どれくらいか見積もってアイテムの消費を抑えたりとセーブポイントありきの楽しみ方もある。また、宝箱を取り損ねて2度と取れなくなったりという心配もある。

(4)ローリスクローリターン

 ドラゴンクエストVのカジノには1回ロールするのに1,10,100コインが必要な3種類のスロットマシンがあるが、10000コインのスロットマシンはない。10000コインで数倍のアタリが出るなら、10000コインたまった時点でトライアンドロードで何回も試せば一攫千金だから、それを設定していないのだろう。少ない掛け金で数多く試す方式の場合、時間もかかるためリセットの意味は薄れてくる。これも一つの手法だが、おそらく一般にハイリスクハイリターンのほうがギャンブル性が高く、多くの人を虜にする。

(5)乱数テーブル固定

 この記事は、この乱数テーブル固定について書き留めておくために書いている。RGSSのランダムさを生み出す乱数生成:rand関数は、特に何も指示しなければおそらくPCが起動してからのミリ秒などからシードを作り、新たな乱数を返す。この乱数は、メルセンヌツイスタという方式で作られ、ほぼ現実でサイコロを振ったときと同じように振舞う。

 上述のシードを指定することもできる。srand(シード値)。シード値(仮にXとする)が同じであれば、srand(X)命令後にrandで生成した値が、順にA,B,C・・・であったとするならば、また別のシーンで同じXを指定したあとのrandで生成する値は常にA,B,C・・・になる。これを使うと、ゲーム中の好きなタイミングで乱数テーブルを固定することができる。ただし、上述のA,B,Cはrand関数を呼ぶたびに常に次の値に更新されるため、たとえばスロットマシン開始時にsrand(X)したとしても、スロットマシン中にカジノ内でキャラがランダム方向に1歩動くと乱数生成値AやBは消費されてしまう。

 しかしここで、ゲームの乱数とはまったく無関係に別の乱数テーブルを作り出す方法がある。

 乱数テーブル = Random.new(seed値)

ここで返された乱数テーブルは、乱数テーブル.rand するたびに新たな乱数を返し、この組み合わせはゲーム内の他の乱数生成にまったく影響されない。しかも、セーブファイルに乱数テーブルを保存することができる。上の例で使ったシード値Xを使ってテーブルを生成し、乱数テーブル.randを呼ぶとAが返される。このあと、セーブファイルに乱数テーブルを保存し、コンティニューしてまた乱数テーブル.randを呼ぶとBが返される。その後、C、D、E・・・と決まったテーブルを呼び続ける。セーブできるのにいつか終端が繰るのではないか?と思うが、この乱数テーブルが返す乱数列はおそらく無限の長さがある。かといって無限のメモリを使っているわけではなくて、1からNまでの和がN(N+1)/2で求められるように、あるシード値から生まれた乱数列も呼ぶたびに一意の乱数を返すようになっているイメージになる。

 これで何が出来るかというと、たとえばNEWGAME時にNEWGAMEをはじめたミリ秒数などから乱数テーブルを一つ作っておく。この乱数テーブルはセーブ時に保存されるようにする。そしてゲーム中盤で、とあるギャンブルがはじまったとき、確率計算にはこの乱数テーブルを使う。1回目ははずれ、2回目もはずれ、3回目に大当たりが出たとすると、何度リセットしても、やはり1回目ははずれ、2回目もはずれ、3回目に大当たりが出る。このゲームでは、NEWGAMEの瞬間にプレイヤーのくじ運を確定している。このギャンブルに対してもはやトライアンドロードは無意味で、大当たりを出すためには必ず3回やらなければならない。(新しくNEWGAMEを始めれば、そのゲームではもしかすると2回目で大当たりかもしれない)。これでロードすることなく、現実に近い形でギャンブルが楽しめる。

 ただ、注意しなければならないのはサイコロで掛け金を自由に決められるようなもの。このとき掛け金全てに対して同じシードを使ってしまうと、1回目1円かけてはずれ、2回目1円かけてはずれ、3回目1円かけて大当たり10倍の10円。ときたときにリセットして、1回目と2回目に1円、3回目に所持金全額50万円かけて一躍大金持ちになれば良い。これはギャンブルでもなんでもなくただの予知で、乱数テーブルが固定されていれどもそのテーブルを予想できてはゲームにならない。

 こういうときは、たとえば掛け金を10円、1000円、10万円の3種類に絞らせ、3つの乱数テーブルを用意しておのおのを割り当てると良いだろう。10円の賭けは10万円の賭けとテーブルが無関係なので、10万円であたりを出すにはセーブロードに関わらずNEWGAMEで定まった運による、ある一定の回数をトライしなければならない。

 

 これらの方式は、ギャンブルだけではなく、ある一定の確率でランダムに効果が付与される何らかのイベントなどにも使える。製作者サイドから見れば、ファミコンなどではおそらく取りづらい手法で、PCならではだろうと思う。