Python3でRPGを作る時に使えそうな技術の断片 -ワールドマップ上に村を生成しよう-

f:id:sakage24:20170725232826j:plain

今回はワールドマップ上に村を自動で生成するやり方を記事にします!この手法はランダムなダンジョン生成など、様々な分野で応用が効くので覚えておくと良いですよ。

マップを手動で生成するのは結構しんどいです。10 × 10のマップとかならまだ良いのですが、100 × 100とかになってくると、訳が分からなくなるし、何より管理が面倒です。

どうせなら自動化したいですが、完全なランダム生成だと色々と問題があります。最悪、村の隣に村があったり、進入不可な場所に村が生成される可能性があるからです。

今回やること

  • ワールドマップを矩形で4つに分割しよう
  • 分割したマップに村を生成しよう

ワールドマップを矩形で4つに分割しよう

f:id:sakage24:20170725232826j:plain

村と村とは、ある程度距離があるべきですよね。それと毎回同じ場所に村があるのも、飽きが来ますよね。ある程度のランダム性と、間隔を持たせるために、まずはワールドマップを4つのエリアに分割しましょう。


from pprint import PrettyPrinter


def get_rect(maps):
    height = len(maps)
    width = len(maps[0])
    half_height = height // 2
    half_width = width // 2
    # 左上
    area_a = [(i, j) for i in range(half_width) for j in range(half_height)]
    # 右上
    area_b = [(i, j) for i in range(half_width, width) for j in range(half_height)]
    # 左下
    area_c = [(i, j) for i in range(half_width) for j in range(half_height, height)]
    # 右下
    area_d = [(i, j) for i in range(half_width, width) for j in range(half_height, height)]
    return area_a, area_b, area_c, area_d

if __name__ == '__main__':
    maps = [[0 for x in range(10)] for y in range(10)]
    areas = get_rect(maps)
    pp = PrettyPrinter(indent=4)
    pp.pprint(areas)

こんな感じの関数を作成しました。get_rect()関数は2次元配列を渡すと、矩形で4つに分けて、それぞれの座標を全て纏めて返してくれます。

area変数の中には矩形で分けたエリアごとの座標が全て入っていますので、各々、適当に1つずつ拾えば、ある程度均等に村が生成出来ますね。

randomモジュールにある、リストから1要素をランダムで返すchoice関数を利用しましょう。

from random import choice as ch
.
.
.
.
.
village_grid = [ch(area) for area in areas]
>>> 実行結果
[(2, 4), (9, 3), (1, 9), (6, 5)]

適当な座標が拾えましたね。拾った座標を村にすることにします。


for i, j in village_grid:
    maps[i][j] = 1
pp.pprint(maps)

>>> 実行結果
[   [0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]

以上で村の生成が出来ました。説明を忘れていましたが、

  • 草原が0
  • 村が1
  • 壁が999

みたいな感じで、代数的記法にすると楽ですね。以下に全ソースコードを貼ります。


from pprint import PrettyPrinter
from random import choice as ch


def get_rect(maps):
    height = len(maps)
    width = len(maps[0])
    half_height = height // 2
    half_width = width // 2
    # 左上
    area_a = [(i, j) for i in range(half_width) for j in range(half_height)]
    # 右上
    area_b = [(i, j) for i in range(half_width, width) for j in range(half_height)]
    # 左下
    area_c = [(i, j) for i in range(half_width) for j in range(half_height, height)]
    # 右下
    area_d = [(i, j) for i in range(half_width, width) for j in range(half_height, height)]
    return area_a, area_b, area_c, area_d

if __name__ == '__main__':
    # 10 × 10のマップ生成
    pp = PrettyPrinter(indent=4)
    maps = [[0 for x in range(10)] for y in range(10)]
    areas = get_rect(maps)
    # 分割したエリアごとに1つずつ、村にする
    village_grid = [ch(area) for area in areas]
    
    # 登録処理
    for i, j in village_grid:
        maps[i][j] = 1
    pp.pprint(maps)

終わり

次回は、村と村とを道で繋ぐような関数を作っていきましょう。

私事ですが、最近アドセンスの単価がめちゃ低くて悲しんでいます…やる気に直結してくるので、どうにかしてほしいですね…何が駄目なんだろうか。

コメントを残す

メールアドレスが公開されることはありません。