こんにちは
konoです。今回2022年度の白鷺祭ではオセロAIのようなものを作ろうと頑張っていましたが、どうしてもアルゴリズムの実装がうまくいかなかったので急遽代わりのものを作ることになりました。うーんどうしようかなーーそうだ!
##### 迫りくるマッチョから逃げるゲームを作ろう! まずフィールド作りから始めることにしました。すぐ終わるやろと思っていたわけですがフィールドを作っていたら白鷺祭の日になっていましたね。ほにゅ...?
ゲームを完成させることはできませんでしたが、フィールドをつくっただけでもきっとえらいです。というわけでこの記事は、後々作る(かもしれない)ゲームのためのフィールド作成プログラムを書いたよ、というものです。
ポケダンを目標に
フィールドを作るにあたって参考にしたのがポケモン不思議のダンジョン、略してポケダンシリーズです。 ポケダンではダンジョンがランダムに生成されます。同じ名前のダンジョンを訪れても毎回フィールドの形が違うんですね。こんなランダムなダンジョン生成に憧れて、似たようなものを作ろうと頑張りました。
手順
ポケダンのようなプレイするたびにマップやダンジョンが新たに作られるゲームは「ローグライクゲーム」と言うらしいです。ローグライクゲームでは普通、
① 大きい長方形を複数の長方形に分ける
② 分けられた長方形の中に部屋を作る
③ 部屋をつなぐ道を作る
という手順で地形が作られるらしいです。むずそーー。
プログラムを書く
うおおおおおおおおおおおおおおおおおおおおおおおお
おおおおおおおおおおおおおおおおおおおおおおおおお
おおおおおおおおおおおおおおおおおおおおおおおおお
!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!
書いた
書きました。できたものがこちらになります。
ポケダンには及ばないですがかなり様になっているのではないでしょうか。
中身
プログラムはPython3.9で書きました。Unityとかなんにもわかりませんので。可読性が低くバグの潰し方が汚いソースコードですが、まあ素人の書くコードなんてこんなものでしょう。 ~~うごけばええねん。~~ 去年作ったブラックジャックのプログラムの汚さと比べれば100倍マシです。
がんばったところ
色々あるのですがなんと言っても部屋をつなぐ道づくりです。頑張ったのでちょっと長くなります。どうやって道をつなげようか調べていた時に見つけたサイトでは、
① 繋ぐ2つの部屋を決める
② 2部屋から道を伸ばし、
③ 部屋の属する領域同士の境界線上でつなぎ合わせる
という方法が書かれていました。文ではわかりづらいですが、上のイラストのような部屋のつなぎ方ですね。
確かにこの方法でできる道はとてもポケダンらしい見た目なのですが、自分はちょっとこの方法で上手くいく気がしなかったので、以下の方法で道を作りました。
① ひとつの部屋を選ぶ
② その部屋から上下左右4方向に道(仮)を伸ばす
③ 道(仮)が他の部屋に当たれば道(仮)をちゃんとした道にする、
他の部屋に当たらなかったらとりあえず道(仮)を放置
④ 他の部屋からも4方向に道(仮)を伸ばし、他の部屋や他の部屋からの道(仮)に当たれば
道(仮)をちゃんとした道にして部屋同士をつなげる
⑤ ④を残りのすべての部屋について施す
④以降で道をつなげる時、「深さ優先探索」といわれるアルゴリズムを用いたのですが、僕はこのアルゴリズムがとっても苦手です。記事の最初に「アルゴリズムの実装ができなくてオセロAI作りを断念した」と書きましたが、そのアルゴリズムであるアルファベータ法は再帰(関数の定義に同じ関数を利用すること)を利用して強い手を探すというものです。
僕は再帰がめちゃくちゃ嫌いでできるだけ避けて生きてきたのですが、今回必要になった深さ優先探索も不幸なことに再帰を利用するアルゴリズムです。再帰から逃げてきたのに再帰に回り込まれてしまいました。仕方ないので泣く泣く深さ優先探索と向き合います。
なんとか⑤まで終わらせると、今度は怒涛のバグ潰しが待っていました。
まずなんか道が太くなってました。なんやかんやで原因をつきとめ、どうにもならんので太い道を作らないことを諦め、太くなった道を細くする方向で頑張りました。
頑張った結果、なんか道がちぎれてました。どうしようか考えましたが
深さ優先探索で補正するのが一番よさそうでした。
このバグ、10回のマップ生成で1回起きるか起きないかくらいの頻度です。もう放置でよくないですか?とも考えましたが、泣きながらもう一度深さ優先探索を書きました。もう自分でも何を書いているのか分からなかったですが、なんか上手く動いたのでOKです。
改善したい点
ひとつとてもわかりやすいバグがあります。上の動画を注意深く見ていた人は気づいたかもしれません。
このマップ生成方法だと、なんと他のどの部屋にも道が繋がっていない部屋、つまり孤立した部屋がごく稀にできてしまうんですね。マッチョから逃げるゲームを作るときにこのバグが残ったままだと、主人公のスポーン地点がその孤立部屋だった場合、どれだけ放置してもそこにマッチョが来ることがないという状況になってしまいます。
解決策は浮かんでいますがこの記事を書いている現在11月5日(土)の朝5時半ということで、バグを直してまた実行の様子をキャプチャしてうんぬんかんぬんする元気は僕にはありません。ゆるして。
もうひとつ絶対にどうにかしたい部分が、最初に示した全体の手順
① 大きい長方形を複数の長方形に分ける
② 分けられた長方形の中に部屋を作る
③ 部屋をつなぐ道を作る
の①と②の部分、つまり部屋づくりですね。今回、部屋の個数と大きさをランダムで良い感じに決まるようにしたかったのですが、大きさを良い感じにすることができませんでした。(部屋の数を良い感じのランダムにすることはできましたが、上のコードでは見栄えが良くなるように一旦部屋の数を8個に固定しています。)
動画を見てもらえればわかる通り、部屋の大きさが全体的に小さいです。一応部屋の大きさをランダムにすることはできていますがその自由度が低いんですね。なんとかしたいです。
感想
バグを直したところからまた別のバグが発生するというあるある(?)を体験できたのが楽しかったですね。今回書いたプログラムは400行くらいで僕にとってはとても大変だったのですが、ガチでプログラミングをする人は何千行何万行、多い時は何十万行のプログラムを書くこともあるらしくて戦慄しています。化け物か?
あと、変数名や関数名が同じようなものばかりになってどの変数に何が格納されているのかわからないことが多々ありました。読みやすいプログラムを書くにはどうしないといけないのか勉強していかないといけませんね。
作るものが途中で変更になりましたが、なんとか展示時間までに形にできてよかったです。最後に、こんな長ったらしい記事を最後まで読んでくださり、ありがとうございました。