ゲームの自動生成を目指すならばレベル、つまり面のパターンの自動生成もやっておきたい。ゲーム&ウォッチ的な単一の画面で遊ぶゲームはともかく、大抵のゲームは複数の面があったりスクロールしたりするのでレベル自動生成が必須である。
どれだけ質の良いレベルを作れるかがゲームの出来にダイレクトに効くのは何と言ってもパズルゲームだ。特に倉庫番のようなアクション性が無いパズルゲームにおいては、いかに解くのが楽しいレベルが作れるかがとても重要。
倉庫番のレベルジェネレータはすでにいろいろある。作り方としては、
- 箱が配置されていない空の部屋を作る
- 箱を正解位置に置く
- ゲームを逆にシミュレートして箱を適当な位置に散らす
というのが一般的のようだ。3.は要するにプレイヤーで箱を「引いて」ずらしていけば、その結果移動される箱はちゃんと正解位置へ移動可能であるということを保証できるという意味だ。
上の作り方に沿ったジェネレータ実装の一つ。プレイヤーが動けなくなったらバックトラックして他の置き方を探す。
こちらは論文。プレイヤーが箱を直線方向に押す回数や、別の箱を押しに行く回数などのメトリクスを導入して正解位置からなるべく離れた、つまりプレイヤーがやることが多くなるレベルを作っている。
この論文はさらにチャレンジングで、PuzzleScriptで作られる任意のパズルのレベルを生成することを目標としている。PuzzleScriptルールのアナライザからゲーム内オブジェクトの特性をレベルのジェネレータとレベルのエバリュエータに渡してレベルを生成させる。これは後段のエバリュエータが生成されたレベルが解けるかどうか、解けたとして楽しいものか、などを判定するアプローチなので、上記2つのような逆方向のシミュレーションをするものとはちょっと違う。
とまあ既存アプローチはいろいろあるが、せっかくだから何か簡単なパズルゲームを作って、自分でもレベル生成を試してみようと思った。
それがこれ。倉庫番とペンゴをつまみ食いしたようなパズルゲーム。青い四角で表示される箱をフリックでスリップさせて黄色の正解位置へ置いてください。
レベル自動生成方法は他の倉庫番ジェネレータと同様の正解から逆方向にゲームをシミュレートする方式。ただ空の部屋を最初に作るのではなくて、全て壁の部屋を作ってから箱を逆方向にスリップさせつつ壁を掘る方式にした。
- 適当な大きさの全て壁(正確には壁か空白どちらでもいい状態)からなる部屋を作る
- 正解位置に箱を置く
- 箱を適当な方向に適当な距離スリップさせる
- スリップ開始時にスリップ方向と逆方向に壁を置いてそこで止まるようにする
- スリップした場所は空白で確定する(壁を掘る)
上記スリップ動作を適当な回数行ってレベルを作る。ただこれで闇雲に作ってもあまり楽しいレベルが出来るとは限らないのでちょっとだけ細工をしている。
このパズルはプレイヤーキャラがいないからかなり自由に箱をスリップさせることが出来てしまう。なのでパズルっぽさを出すには箱をスリップさせる順番や、別の箱にぶつけて箱を適切な場所で止めるにはどうするかを考えるような、箱と箱の間のインタラクションが多いものにしないといけないはず。あと壁が多すぎるとより制約が減ってしまってパズルっぽさが減りそう。それらを踏まえて
- スリップさせる時はなるべく壁を作らないで、すでにある壁や箱で止められる方向を優先する
- スリップさせた後になるべく他の箱に隣接させるようにする
という小細工を加えた。これら小細工がうまく効いているかはよく分からん。でも一応それなりに遊べるレベルは出来ているし、
ここまでやってクリアできない組み合わせは出なかったので、自動生成のアルゴリズム的には問題ないかと思われます(`・ω・´)ゞ pic.twitter.com/CXlm1j9mnL
— タマネギ炒め (@tamanegi_itame) 2017年9月30日
クリアできないレベルができるということもとりあえず無さそうだ。
あとはPuzzleScript論文にあるようなエバリュエータを加えてレベルの楽しさを判定して生成したレベルを選別できると本当は良いのだろう。この論文では正解位置と箱の距離や繰り返しでないプレイヤーの操作数、適用されたルール数などを使って評価を行っている。これらのメトリクスが楽しさをちゃんと反映しているかどうかはともかく、明らかに楽しくないレベルを弾くには使えそうである。似たような仕組みをちょっとずつ試していきたいところ。