この記事はGPT先生と相談しながら書いています。
この記事では主にTileMapについて書いてます。
1.ダンジョンの概要
最初のgifを見てもらえれば、ある程度は想像つきますかね
- 1階層のダンジョンを生成する
- ダンジョンはランダムに生成する
- 階層はマス目状になっていて、各1マスが部屋になる(部屋が無いマスもあります)
- このダンジョンには通路はありません。各部屋の出入口が隣の部屋につながっています。
シレン風を目指していますが、通路を作ると面倒になりそうな気がしたのでこの形になりました。
2.ファイル構成
Roguelike_Test_v0_1
+-- main.tscn/.gd:ゲームのメインシーンとそのスクリプト。
+-- assets:ゲームで使用される各種アセット。
| (地形チップ、マップ画像、プレイヤーチップなど)
+-- src
+-- common
| +-- gamesettings.gd:ゲーム全体の設定や定数を定義するスクリプト。
+-- dungeon
| +-- dungeon.tscn/.gd:ダンジョン生成のメインロジックとシーン。
| +-- dungeon_map.tscn/.gd:ダンジョンマップの表示を扱うシーンとスクリプト。
| +-- dungeon_rooms.tscn/.gd:ダンジョンの部屋配置を管理するシーンとスクリプト。
| +-- mappin.tscn/.gd:マップ上のピンやプレイヤー位置を示すシーンとスクリプト。
+-- player
+-- player.tscn/.gd:プレイヤーの表示と動作を定義するシーンとスクリプト。今回はdungeonフォルダ以下のシーンを作成していきます。
3.Dungeonシーン
dungeonシーンではダンジョンの管理を行います。

dungeonシーンが親で他3シーンが子になっています。
・dungeon_roomsシーンでは1階層のダンジョンを生成し、部屋情報を保持しています。
・dungeon_mapシーンは1階層のマップです。到達した部屋のみ表示しています。
・mappinシーンはただの赤点です。ダンジョンマップ内の現在地を示す用です。
extends Node2D
# ダンジョンの自動生成(1階層)と管理を行う
enum RoomType{ # 部屋の種類
Start, # スタート地点
Goal, # ゴール地点
Others # その他
}
@onready var dungeonmap = $Dungeon_map
@onready var dungeonrooms = $Dungeon_rooms
@onready var mappin = $Mappin
var _width = 0 # 階層のマスの数(横)
var _height = 0 # 階層のマスの数(縦)
var _roomnum = 0 # 階層に部屋を何個作るか
func _ready():
# 初期処理
_width = Gamesettings.DUNGEON_WIDTH
_height = Gamesettings.DUNGEON_HEIGHT
_roomnum = Gamesettings.NUMBER_OF_ROOMS
# 部屋の生成
dungeonrooms.create_rooms()
# 確認用
var temp = dungeonrooms.get_dungeon2d()
for i in range(temp.size()):
print(temp[i])
# 確認用
temp = dungeonrooms.get_rooms()
for i in range(temp[0].objects.size()):
print(temp[0].objects[i])
func proc() -> void:
# 現在の部屋を表示
dungeonrooms.proc()
# ダンジョンマップの表示
dungeonmap.proc(dungeonrooms.get_rooms())
# マップピンの表示
mappin.dsp_pin(dungeonrooms.get_current_room_pos())GDScript- func _ready():
- 初期処理です。階層の広さ(横マスx縦マス)、部屋をいくつ生成するかを設定し
dungeonroomsに部屋の生成をさせています。
- 初期処理です。階層の広さ(横マスx縦マス)、部屋をいくつ生成するかを設定し
- func proc() -> void:
- ループ処理です。同期とか面倒そうなので各シーンのfunc _process(delta):は使用せず、
基本はmainシーンのfunc _process(delta):から.proc()を呼んで処理を行います。 - 部屋とマップとピンの表示を行っているだけです。
- ループ処理です。同期とか面倒そうなので各シーンのfunc _process(delta):は使用せず、
今はスカスカですが、ダンジョン内の処理はここで管理しようかと考えています。
4.Dungeon_roomsシーン(TileMapの設定)
dungeon_roomsシーンではダンジョン(1階層)を生成し、生成した部屋情報を所持し、部屋の表示処理も行っています。現在1番ボリューミーなシーンです。

TileMapを使用しています。正直苦手です。操作するたびに悩みます。
表示用に使っているだけなので、さほど作業はしていませんが・・・
というか設定してから時間が空いているので、どうやったか忘れかけてます。
なのでちょっとやってみます。

- まずは新規のプロジェクトを作成し、2dノードを1つ作ります。
そして2dノードの下にTileMapノードを追加します。 - そしてTileMapで使用する画像も持ってきます。

- インスペクターのTileMapを確認します。
- Tile Set <空> となっている横の下矢印をクリックし、プルダウンの新規を選択します。

- 新規を選択すると<空>だったところがTileSetに変わるので、そこをクリックすると項目が出てきます。
- Tile Sizeのところが1マスのサイズになります。今回は16×16のマップチップを使用するのでそのままです。

- 画面下のTileSetタブを選択し、マップチップをドラッグアンドドロップで隣の四角に持っていきます。
- ファイルを持っていくと「アトラスにタイルを自動的に作成しますか?」というダイアログが出るので「はい」を選択する。
- 一応準備はこれで終わりです?

- では、実際にタイルを描き込んでいきます。
- 下のTileMapを選択します。
- あとはお絵描きソフトのように、タイルを選んでペン(四角)を選択し、描いています。
dungeon_roomsシーンのTileMapでは二つのレイヤーを使用しています。
ground(床)とobject(物)を作成します。

- インスペクターのTileMapのLayersの項目を選択します。
- Nameを変更すると名前を付けれます。(プログラムで操作するときはインデックスを使用するのであまり意味ない気もします。)
- 要素を追加のボタンを押すと下に新しいレイヤーの項目が出てくるので必要な分追加します。
- 左側のプルダウンでレイヤーを選択して、それぞれに描きこんでいきます。
タイルセットは保存しておくと流用できるようです。私は使ってませんが・・

- インスペクターのTileSetの項目の下矢印をクリックすると、プルダウンから保存することができます。
- ほかのTileMapノードで使用するときは、読み込みからいけるのかなと思います(試してません。)
シーンが出来たので、今度はプログラムからタイルの操作を行います。
const TILELIST = {
"NON" : Vector2(0, 0),
"WALL" : Vector2(1, 0),
"STAIRS" : Vector2(2, 0)
}
func _disp_current_room():
# 現在部屋を表示する
# オブジェクトもいろいろ表示する予定だが、現在は出口しか出すものが無い
# 現在の部屋情報を取得
var room = get_room_from_pos(_current_room_pos)
# objectレイヤーのオブジェクトを設置
for y in range(len(room.objects)):
for x in range(len(room.objects[y])):
# 何もない。タイルを消す
if room.objects[y][x] == Gamesettings.RoomObj.NON:
tilemap.erase_cell(1, Vector2(x, y))
# 壁
elif room.objects[y][x] == Gamesettings.RoomObj.WALL:
tilemap.set_cell(1, Vector2(x, y), 0, TILELIST.WALL)
# 出口。タイルを消す
elif room.objects[y][x] >= Gamesettings.RoomObj.EXITL\
and room.objects[y][x] <= Gamesettings.RoomObj.EXITD:
tilemap.erase_cell(1, Vector2(x, y))
GDScript- この関数は現在プレイヤーがいる部屋を画面に表示するものです。
- 部屋内もマス目状(2次元配列)で管理しています。
- マス目に物があったら、対応したタイルを表示する感じです。
- tilemap.erase_cell(1, Vector2(x, y))で何もないところのタイルを消してます。
第1引数はレイヤーのインデックス(1はobjectレイヤー)、第2引数は座標です。 - tilemap.set_cell(1, Vector2(x, y), 0, TILELIST.WALL)で壁を配置しています。
第1引数はレイヤーのインデックス(1はobjectレイヤー)、第2引数は座標です。
第3引数はソースID、第4引数はアトラス座標になります。
第5引数もありますが今回は使わないので指定していません。

- 表示したいタイルにカーソルを合わせると、ソースIDとアトラス座標を確認できます。
5.終わりに
長くなってしまったので今回はここまでにします。
ダンジョン生成と表示まで行こうと思ってましたが力尽きてしまい、TileMap回になってしまいました。
プロジェクトはGithubにアップロードしています。
- ライセンスについて
- 本プロジェクトは [MITライセンス](LICENSE) の下で公開されています。
- 使用しているフリー素材
- このプロジェクトにはKenney (https://kenney.nl/) から提供されたアセットが含まれています。これらのアセットはCreative Commons Zero (CC0) ライセンスの下で提供されています。
これらの素晴らしいリソースを提供してくださったクリエイターの皆様に感謝します。