ChatGPTでRPGツクールのマップを作成する

AI生成ゲームでアソブ

ここではChatGPTを用いてRPGツクールのワールドマップを自動作成する実験メモを書いていきます。下層レイヤ(草原、海)と上層レイヤ(森、山、町、洞窟)をそれぞれ作ります。

結論

以下のようなワールドマップをChatGPTで作成することができました。

下のリンクから実際にワールドマップを歩くことができます。

AIマップ生成テスト
ChatGPTのみで作ったワールドマップを歩くことができます。

方針

  1. 下層レイヤ(レイヤー1)
    • ChatGPTにワールドマップの下層レイヤを2次元配列で作ってもらう
    • ChatGPTが出力した下層レイヤ2次元配列を、PythonでRPGツクールが読み込める形(Map001.json)に変換する
    • RPGツクールMZで下層レイヤのワールドマップを確認する
  2. 上層レイヤ(レイヤー2)
    • ChatGPTに下層レイヤのワールドマップの上層レイヤを2次元配列で作ってもらう
    • ChatGPTが出力した上層レイヤ2次元配列を、PythonでRPGツクールが読み込める形(Map001.json)に変換する
    • RPGツクールMZで下層レイヤのワールドマップを確認する

実験1: ワールドマップの下層レイヤを作ろう

まずは、下層レイヤ(草原と海だけ)を作って、ChatGPTの実力を見てみましょう。

RPGのワールドマップを作ります(ドラクエ3の世界地図のようなものを想定してください)。

以下の条件でJSON配列を作成し、JSONファイルとしてエクスポートしてください。

  • 横100マス、縦50マスのワールドマップを作成します
  • 二次元配列の二次元目でマップの行を表してください(行のサイズは100)
  • 二次元配列の一次元目でマップの列を表してください(列のサイズは50)
  • 配列の要素は「草:1」または「海:0」としてください
  • 「海」は大洋とし、途切れ途切れにならないよう工夫してください
  • 「草」は大陸とし、マップ全体の約5割を「草」にしてください
100×50マスのRPGワールドマップを作成し、約50%が「草:1」の地形で構成されるようにしました。このマップはJSONファイルとしてエクスポートしました。以下のリンクからファイルをダウンロードできます:

ダウンロード RPG World Map 100x50

このマップでは、「草」が大陸を形成し、全体の約5割を占めています。一方、「海」は大洋として設計され、途切れ途切れにならないように配列されています。

0が海、1が草原で100×50のマップが作成されました。これをPythonスクリプトでMap001.jsonのdata配列に入れられる形に変換して、RPGツクールMZエディタでマップ表示してみます。

make_map_low.py

make_map_low.pyと同階層にChatGPTが出力したJSONをダウンロードしておいておきます。また、3行目のDIR=のところで、マップを表示するためのプロジェクトのパスを通しておきます。7行目のJSONファイル名はChatGPTが出力したJSONファイル名に置き換えてください。

配列の要素は2048が海、2816が草原なので0, 1に合わせて値をdataに追加し、それをMap001.jsonに上書きします。

import json

DIR = "C:/work/RMMZ/002_map_test"


def main():
    with open("rpg_world_map_low.json", mode="r", encoding="utf8") as f:
        maps = json.load(f)

    # Map001.jsonに入力する配列を作る
    data = []
    for i in range(len(maps)):  # 0-49
        for j in range(len(maps[0])):  # 0-99
            value = maps[i][j]
            if value == 0:
                data.append(2048)  # 海
            else:
                data.append(2816)  # 草原
    with open(f"{DIR}/data/Map001.json", mode="r", encoding="utf8") as f:
        map001 = json.load(f)
    map001["data"] = data
    map001["width"] = len(maps[0])
    map001["height"] = len(maps)
    with open(f"{DIR}/data/Map001.json", mode="w", encoding="utf8") as f:
        map001 = json.dump(map001, f, indent=2, ensure_ascii=False)


if __name__ == "__main__":
    main()

Map001.jsonを出力し、できたマップをRPGツクールMZで見たのが下の通り。

うーん、世界地図感は出ていますが、陸がほぼ一続きだったり、大陸とか島の感じはあまりないですね…。

あとは2種類のタイルで塗ってしまっているので、RPGツクールMZが持っているオートタイルが効いておらず海岸線などが不自然です。

とりあえずRPGツクールMZのエディタの塗りつぶしで塗りつつ、いい感じに海を広げて大陸を区切ってみましょう。(ここもChatGPTに任せたいところですが、今回は人手でやってしまいます)

どうでしょう?だいぶ見栄えはよくなったでしょうか?ところどころ島っぽくしたり橋がかかりそうな隙間を意識したりして塗りつぶしました。いったんこれで良しとして、次に進みましょう。

実験2: ワールドマップの上層レイヤを作ろう

まずは手修正した上のマップを保存して、マップ情報を2次元の配列ファイルに逆変換します。これをChatGPTに入力して、上層レイヤ(森、山、町、洞窟)を追加してもらいます。

make_map_low_array_2d.py

import json

DIR = "C:/work/RMMZ/002_map_test"


def main():
    with open(f"{DIR}/data/Map001.json", mode="r", encoding="utf8") as f:
        map001 = json.load(f)

    width = map001["width"]
    height = map001["height"]
    data = map001["data"]

    array_2d = []
    for i in range(height):
        array_2d.append([])
    row = 0
    column = 0
    for d in data:
        if 2000 <= d <= 2100:
            array_2d[row].append(0)  # 海
        elif 2800 <= d <= 2900:
            array_2d[row].append(1)  # 草原
        column += 1
        if column % width == 0:
            column = 0
            row += 1
            if row == height:
                break

    with open("./output.json", mode="w", encoding="utf8") as f:
        json.dump(array_2d, f, ensure_ascii=False)


if __name__ == "__main__":
    main()

これでmake_map_array_2d.pyと同階層にできたoutput.jsonをChatGPTに入力しつつ、中層レイヤのJSON配列を作成してもらいます。

📄output.json

添付のoutput.jsonファイルは100×50の世界マップの下層レイヤを表しています。(草:1、海:0)

これの上に上層レイヤをJSON配列で作成し、JSONファイルとしてエクスポートしてください。

  • 下層レイヤに重ねる形で、横100マス、縦50マスのワールドマップ(上層レイヤ)を作成します
  • 二次元配列の二次元目でマップの行を表してください(行のサイズは100)
  • 二次元配列の一次元目でマップの列を表してください(列のサイズは50)
  • 配列の要素は「森:3」「山:4」「町:5」「洞窟:6」のどれかとしてください
  • 「山」「森」「町」「洞窟」は下層レイヤで必ず草:1となっている場所にのみ配置してください
  • 「山」「森」は1マスではなくつなげてある程度大きな山脈、森林を形成してください
  • 「山」「森」は全体の「草」パネルに対してそれぞれ1割程度配置してください
  • 「町」は全体で10個程度配置し、町同士が近すぎないようにしてください
  • 「洞窟」は全体で10個程度配置し、町に対して近く、洞窟同士が近すぎないようにしてください

100×50マスのRPGワールドマップの上層レイヤ(森:3、山:4、町:5、洞窟:6)を作成し、JSONファイルとしてエクスポートしました。このレイヤーは下層レイヤ(草:1)に重ねる形で配置され、山や森は草地上にのみ配置されています。また、山と森は大きな塊を形成し、町と洞窟は配置されています。以下のリンクからファイルをダウンロードできます:

ダウンロード RPG World Map Upper Layer

このマップでは、「山」と「森」が自然な地形のバリエーションを提供し、町と洞窟が探索の興味を加えています。 ​

3が森、4が山、5が町、6が洞窟の2次元配列が作成されたかと思います。これをダウンロードして、下層レイヤの時と同様にPythonスクリプトでMap001.jsonのdata配列に入れられる形に変換します。

make_map_up.py

下層レイヤの時と同様にmake_map_up.pyと同階層にChatGPTが出力したJSONをダウンロードしておいておきます。(3行目のDIR=のところで、マップを表示するためのRPGツクールMZのプロジェクトパス、7行目のJSONファイル名はChatGPTが出力したJSONファイル名に置き換えてください)

配列の要素は3054が森、3198が山、120が町、16が洞窟なので、data配列から下層レイヤのデータを読み込み、それに続いて上層レイヤ(RPGツクール的にはレイヤー2)にデータをappendしていきます。dataの追加が完了したら、Map001.jsonに上書きします。

import json

DIR = "C:/work/RMMZ/002_map_test"


def main():
    # Map001
    with open(f"{DIR}/data/Map001.json", mode="r", encoding="utf8") as f:
        map001 = json.load(f)
    width = map001["width"]
    height = map001["height"]

    # 下層レイヤ作成
    data = map001["data"][: width * height]

    # 上層レイヤ作成
    with open("rpg_world_map_up.json", mode="r", encoding="utf8") as f:
        maps_up = json.load(f)
    for i in range(len(maps_up)):  # 0-49
        for j in range(len(maps_up[0])):  # 0-99
            value = maps_up[i][j]
            if value == 3:
                data.append(3054)  # 森
            elif value == 4:
                data.append(3198)  # 山
            elif value == 5:
                data.append(120)  # 町
            elif value == 6:
                data.append(16)  # 洞窟
            else:
                data.append(0)

    # Map001.json上書き
    map001["data"] = data
    with open(f"{DIR}/data/Map001.json", mode="w", encoding="utf8") as f:
        map001 = json.dump(map001, f, indent=2, ensure_ascii=False)


if __name__ == "__main__":
    main()

Map001.jsonを出力し、できたマップをRPGツクールMZで見たのが下の通り。

なかなかいい感じではないでしょうか?下層レイヤの時と同様にオートタイルが効いていないので手動で直していきます。

手動で手直しした結果です。かなりRPGのワールドマップっぽい雰囲気にできたかと思います。

実験3: おまけ

いい感じのマップができましたが、町と洞窟だけでは寂しいのでもう少しオブジェクトを置いていこうと思います。

いいですね。さらに最上層のレイヤを作りたいです。

これの上に最上層レイヤをJSON配列で作成し、JSONファイルとしてエクスポートしてください。

  • 上層レイヤに重ねる形で、横100マス、縦50マスのワールドマップ(最上層レイヤ)を作成します
  • 二次元配列の二次元目でマップの行を表してください(行のサイズは100)
  • 二次元配列の一次元目でマップの列を表してください(列のサイズは50)
  • 配列の要素は「城:7」「塔:8」「神殿:9」「橋:10」「大木:11」のどれかとしてください
  • 「城」「塔」「神殿」「大木」は下層レイヤで必ず草:1となっている場所にのみ配置してください
  • 「橋」は下層レイヤで必ず海:0となっている場所のみに配置して、草:1同士をつなげるようにしてください
  • 「城」は横2マス、縦2マスの大きさとしてください
  • 「塔」は横1マス、縦2マスの大きさとしてください
  • 「橋」「大木」は横1マス、縦1マスの大きさとしてください
  • 「城」「塔」「神殿」「大木」はそれぞれ全体で5個程度配置してください

100×50マスのRPGワールドマップの最上層レイヤ(城:7、塔:8、神殿:9、橋:10、大木:11)を作成し、JSONファイルとしてエクスポートしました。このレイヤーは下層レイヤ(草:1)に重ねる形で配置され、城、塔、神殿、大木は草地上に、橋は海上に配置されています。構造物はそれぞれ所定のサイズと数で配置されており、自然な分布をしています。以下のリンクからファイルをダウンロードできます:

ダウンロード RPG World Map Top Layer

このマップでは、城や塔などの構造物がゲームの舞台としての多様性と探索の楽しみを提供しています。 ​

7が城(2×2マス)、8が塔(1×2マス)、9が神殿、10が橋(海上)、11が大木の2次元配列が作成されたかと思います。マス指定や海上指定が新出ですが、はたしてうまくいったでしょうか?これをダウンロードして、前と同様にPythonスクリプトでMap001.jsonのdata配列に入れられる形に変換します。

make_map_top.py

import json

DIR = "C:/work/RMMZ/002_map_test"


def main():
    # Map001
    with open(f"{DIR}/data/Map001.json", mode="r", encoding="utf8") as f:
        map001 = json.load(f)
    width = map001["width"]
    height = map001["height"]

    # 下層・上層レイヤ作成
    data = map001["data"][: width * height * 2]

    # 最上層レイヤ作成
    with open("rpg_world_map_top.json", mode="r", encoding="utf8") as f:
        maps_top = json.load(f)
    for i in range(len(maps_top)):  # 0-49
        for j in range(len(maps_top[0])):  # 0-99
            value = maps_top[i][j]
            if value == 7:
                if maps_top[i - 1][j] != 7 and maps_top[i][j - 1] != 7:
                    data.append(32)  # 城左上
                elif maps_top[i - 1][j] != 7 and maps_top[i][j + 1] != 7:
                    data.append(33)  # 城右上
                elif maps_top[i + 1][j] != 7 and maps_top[i][j - 1] != 7:
                    data.append(40)  # 城左下
                elif maps_top[i + 1][j] != 7 and maps_top[i][j + 1] != 7:
                    data.append(41)  # 城左下
            elif value == 8:
                if maps_top[i - 1][j] != 8:
                    data.append(80)  # 塔上
                elif maps_top[i + 1][j] != 8:
                    data.append(88)  # 塔下
            elif value == 9:
                data.append(86)  # 神殿
            elif value == 10:
                data.append(24)  # 橋
            elif value == 11:
                data.append(11)  # 大木
            else:
                data.append(0)

    # Map001.json上書き
    map001["data"] = data
    with open(f"{DIR}/data/Map001.json", mode="w", encoding="utf8") as f:
        map001 = json.dump(map001, f, indent=2, ensure_ascii=False)


if __name__ == "__main__":
    main()

下層・上層レイヤの時と同様にmake_map_top.pyと同階層にChatGPTが出力したJSONをダウンロードしておいておきます。(3行目のDIR=のところで、マップを表示するためのRPGツクールMZのプロジェクトパス、7行目のJSONファイル名はChatGPTが出力したJSONファイル名に置き換えてください)

配列の要素は前よりも少し複雑になります。2×2マス(城)や1×2マス(塔)があるので、横や上など隣接パネルを見て、どのタイルを置くかを判定するようにしています。

果たして出力結果はどうなるでしょうか?

…………………。惜しいっ…!

城や塔は想定通りマス目を使って正しく作成できています。これはChatGPTがすごい!ただ、橋が海の真ん中にポツンと1つ置かれている場所がいくつかあります。こちらとしては陸同士をつなげるように海の上に配置してほしかったのですが、なかなか指示が難しいですね。

「橋」は必ず草:1同士をつなげるようにしてください。すなわち海の上に1つポツンと置くのではなく、海パネルを介して離れた草パネル同士をつなげるように、間の海パネル全てに「橋」を配置してください。「橋」を修正してJSONファイルをエクスポートしてください。


「橋」の配置を修正し、草地間を海を介してつなぐようにしました。修正された最上層レイヤのマップは、以下のリンクからJSONファイルとしてダウンロードできます:


ダウンロード RPG World Map Top Layer with Bridges

このマップでは、「橋」が海パネル上に配置され、離れた草パネル同士をつないでいます。これにより、マップ全体のアクセシビリティが向上し、プレイヤーに新たな探索の道が提供されています。 ​

絶妙に惜しい!一部は陸間を橋でつなげてくれましたが、まだ海の上にポツンと1つの橋もありますね。ということでここも少し手動で手直しします。

最終版は下のようになりました。

まとめ

ChatGPTのみを使って、RPGツクールMZのワールドマップを作成しました。完全に自動…とはいかず、オートタイルや橋の認識など課題はありますが、1から人手で作るよりはだいぶ効率化ができたのではないかと思います。

次回はダンジョンRPGを生成AIで作ってみたいので、ダンジョンマップを作成できたらと思っています。

タイトルとURLをコピーしました