#世界を創造しませんか?
今回は初代ドラクエやFFなどに出てきそうなワールドマップを簡単に作っていきます。
#条件
マップの大きさは縦横ともに16の倍数 (例: 256 * 128)
マップの縦横はループする。(上下、左右に接続する)
今回は標高ごとに**"海(青色)"、"浅瀬(水色)"、"陸(緑色)"、"高山(灰色)"**に分けて出力します。
#ソースコード
WorldMap.hpp
#pragma once
#include <vector>
#include <iostream>
#include <random>
namespace world {
using vsize_t = std::vector<size_t>;
using vvsize_t = std::vector<vsize_t>;
//乱数
class Rand32 {
private:
std::mt19937 mt;
public:
void srand(const unsigned int seed_) { mt.seed(seed_); }
void init() {
std::random_device rd;
mt.seed(rd());
}
unsigned int operator()(const int max_) {
std::uniform_int_distribution<> uid(0, max_ - 1);
return uid(mt);
}
};
static thread_local Rand32 rand32;
//チャンク生成
void worldMapMake(const size_t x_, const size_t y_, const size_t size_, const size_t t1_, const size_t t2_, const size_t t3_, const size_t t4_, size_t map_[17][17])
{
//再起の終了処理
if (size_ == 0) return;
//頂点の高さを決める
const size_t mapPlus = ((t1_ + t2_ + t3_ + t4_) >> 2) + (const size_t)rand32((const int)size_);
map_[x_][y_] = (mapPlus >= 255) ? 255 : mapPlus;
//四角形の2点同士の中点の高さを決定
const size_t s1 = ((t1_ + t2_) >> 1);
const size_t s2 = ((t1_ + t3_) >> 1);
const size_t s3 = ((t2_ + t4_) >> 1);
const size_t s4 = ((t3_ + t4_) >> 1);
//4つの地点の座標を決める
map_[x_ + size_][y_] = s3;
map_[x_ - size_][y_] = s2;
map_[x_][y_ + size_] = s4;
map_[x_][y_ - size_] = s1;
//分割サイズを半分にする
const size_t size = size_ >> 1;
//4つに分割
worldMapMake(x_ - size, y_ - size, size, t1_, s1, s2, map_[x_][y_], map_);
worldMapMake(x_ + size, y_ - size, size, s1, t2_, map_[x_][y_], s3, map_);
worldMapMake(x_ - size, y_ + size, size, s2, map_[x_][y_], t3_, s4, map_);
worldMapMake(x_ + size, y_ + size, size, map_[x_][y_], s3, s4, t4_, map_);
}
//チャンク生成の呼び出し・実行
void worldMapSimple(size_t map_[17][17])
{
worldMapMake(8, 8, 8, map_[0][0], map_[16][0], map_[0][16], map_[16][16], map_);
}
constexpr size_t aslib_world_make_seed1 = 0x3220;
constexpr size_t aslib_world_make_seed2 = 0x292;
//ワールドマップ生成
void worldMake(vvsize_t& world_map, size_t seed_ = 0)
{
if (seed_ == 0) seed_ = size_t(rand32(65535));
size_t map_[17][17]{};
//横画面サイズ
const size_t map_x = world_map.size();
//縦画面サイズ
const size_t map_y = world_map.front().size();
const size_t chunk_x = (map_x >> 4);
const size_t chunk_y = (map_y >> 4);
for (size_t i = 0; i < chunk_x; ++i) {
for (size_t j = 0; j < chunk_y; ++j) {
//四角形の4点の高さを決定
rand32.srand((unsigned int)(seed_ + i + (j * aslib_world_make_seed1) + ((i^j) * aslib_world_make_seed2)));
map_[0][0] = size_t(rand32(255));
rand32.srand((unsigned int)(seed_ + ((i + 1) % chunk_x) + (j * aslib_world_make_seed1) + ((((i + 1) % chunk_x) ^ j) * aslib_world_make_seed2)));
map_[16][0] = size_t(rand32(255));
rand32.srand((unsigned int)(seed_ + i + (((j + 1) % chunk_y) * aslib_world_make_seed1) + ((i ^ ((j + 1) % chunk_y)) * aslib_world_make_seed2)));
map_[0][16] = size_t(rand32(255));
rand32.srand((unsigned int)(seed_ + ((i + 1) % chunk_x) + (((j + 1) % chunk_y) * aslib_world_make_seed1) + ((((i + 1) % chunk_x) ^ ((j + 1) % chunk_y)) * aslib_world_make_seed2)));
map_[16][16] = size_t(rand32(255));
//チャンク生成
worldMapSimple(map_);
//生成したチャンクをワールドマップにコピペ
for (size_t i2 = 0; i2 < 16; ++i2)
for (size_t j2 = 0; j2 < 16; ++j2)
world_map[(i << 4) + i2][(j << 4) + j2] = map_[i2][j2];
}
}
}
}
#サンプルコード(文字で表示)
Source.cpp
#include "WorldMap.hpp"
constexpr size_t size_x = 304;
constexpr size_t size_y = 160;
int main() {
//乱数初期化
world::rand32.init();
//世界生成
world::vvsize_t wmap(size_y, world::vsize_t(size_x, 255));
world::worldMake(wmap);
//表示
for (size_t i = 0; i < size_y; ++i) {
for (size_t j = 0; j < size_x; ++j) {
if (wmap[i][j] <= 30) std::cout << "▲";
else if (wmap[i][j] <= 110) std::cout << "■";
else if (wmap[i][j] <= 120) std::cout << "□";
else std::cout << "△";
}
std::cout << std::endl;
}
return 0;
}
#出力結果
しっかりと生成できていますね!
#サンプルコード(色付きで表示)
Source.cpp
#include "WorldMap.hpp"
constexpr size_t size_x = 304;
constexpr size_t size_y = 160;
int main() {
//乱数初期化
world::rand32.init();
//世界生成
world::vvsize_t wmap(size_y, world::vsize_t(size_x, 255));
world::worldMake(wmap);
//表示
for (size_t i = 0; i < size_y; ++i) {
for (size_t j = 0; j < size_x; ++j) {
if (wmap[i][j] <= 30) std::cout << "\x1b[47m" << " ";
else if (wmap[i][j] <= 110) std::cout << "\x1b[42m" << " ";
else if (wmap[i][j] <= 120) std::cout << "\x1b[46m" << " ";
else std::cout << "\x1b[44m" << " ";
}
std::cout << "\x1b[0m" << " " << std::endl;
}
return 0;
}
#ゲーム制作に使ってみる
##ソースコードのライセンス