【疑似3DダンジョンRPG開発日誌 07】 マップ管理システムをリファクタリング
やったこと
疑似3DダンジョンRPG「グラヴェンの傷跡」の開発中、マップデータの管理方法を見直して整理した。
きっかけ:階段のグラフィックをどう描くか考えていた
階段をどこに描画するか調べていて、「床に描くか、壁に描くか」という問題にぶつかった。
オリジナルのWizardryは床面に台形の穴として描いていたが、今の自分のコードだと床は全体を一色で塗りつぶしているだけなので、特定のマスだけ穴を描くには台形の計算を新たに書く必要がある。
一方、壁面なら今ある扉の仕組みがそのまま使えるので、そっちのほうが無難という結論に。
本題:扉と階段の管理方法を見直した
階段の話をしていて、今の管理方法に問題があることに気づいた。
変更前の状態
扉・階段・固定エンカウントがそれぞれ別の辞書で管理されていた。
python
DOORS = {
0: {(2, 1): False, (4, 7): False}, # False=閉, True=開
...
}
STAIRS = {
0: {(1, 8): "up", (8, 8): "down"},
...
}
FIXED_ENCOUNTERS = {
(3, 6): "グラキエス = ドラゴス",
}将来、罠や他の要素を追加するたびに辞書が増えていく構造で、管理がどんどん大変になっていた。
ウィザードリィライクの定番設計をClaudeに聞いてみた
一般的なWizardryライクのゲームでは壁属性方式が主流だとわかった。
各マスが4面の壁を持ち、それぞれの壁面に属性(壁/扉/階段/隠し扉…)を持たせる設計。
北の壁
↑
西の壁 ←[マス]→ 東の壁
↓
南の壁ただしフルに実装すると100マス×4方向=400面分のデータが必要になる。実際にはほとんどの壁面は「壁」か「通路」しかないので、特殊な属性だけ辞書で持つ「差分方式」が現実的。
実際にやった変更
ベースマップ(0/1の2次元リスト)はそのまま活かして、特殊属性だけを1つの辞書に統合した。
python
# 変更後:CELL_ATTRSに一本化
INITIAL_CELL_ATTRS = {
0: { # 1階
(2, 1): {"type": "door", "open": False},
(4, 7): {"type": "door", "open": False},
(1, 8): {"type": "stairs_up"},
(8, 8): {"type": "stairs_down"},
},
1: { # 2階
...
},
}
CELL_ATTRS = copy.deepcopy(INITIAL_CELL_ATTRS)変更したファイルは3つ。
data.py … DOORS / STAIRS を削除して CELL_ATTRS に統合
utils.py … is_wall() と get_view_data() を CELL_ATTRS 参照に更新
dungeon_rpg.py … DOORS / STAIRS を参照していた箇所を修正
変更後の感想
データ量はほとんど増えず、むしろ辞書の数が減ってスッキリした。
今後の拡張もこの形で追加するだけでよくなった。
python
# 将来の追加例
(4, 2): {"type": "door", "open": False, "locked": True}, # 鍵付き扉
(6, 3): {"type": "relief"}, # レリーフ(壁の飾り)
(7, 4): {"type": "trap"}, # 罠小さなリファクタリングだけど、設計の考え方をいろいろ考えたので多少は身になったかも…
次にやること
階段のグラフィック素材を作る(壁面に重ねる方式で)
_draw_3d() に stairs_down / stairs_up の描画分岐を追加する
いいなと思ったら応援しよう!
もし記事が参考になったり、開発を応援してもいいと思ってくれた方がいたら、noteのサポートをいただけると次の記事を書く原動力になります。もちろん読んでくれるだけで十分嬉しいです。