
マイケル
みなさんこんにちは!
マイケルです!
マイケルです!

エレキベア
クマ〜〜〜〜

マイケル
今日も引き続き、ゲームボーイソフト制作です!
今回は前回持ち越した、倉庫番マップの自動生成処理 について解説していこうと思います!
今回は前回持ち越した、倉庫番マップの自動生成処理 について解説していこうと思います!

【GBDK】第一回 自作ゲームボーイソフトを作ろう! 〜環境構築編〜
マイケルみなさんこんにちは!マイケルです!エレキベアクマ〜〜〜〜マイケル今日から新しいシリーズ、ゲームボーイソフトを作ろうシリーズ を始めるよ!エレキベアゲームボーイソフトなんてそんな...
localhost
2021.06.01
↑マップの自動生成処理

エレキベア
待ってましたクマ〜〜〜
でも自動生成なんて難しそうクマ〜〜
でも自動生成なんて難しそうクマ〜〜

マイケル
難しいことはせずに、なるべくシンプルな処理で作ってみたよ!
ソースはGitHubにアップしてあるのでご自由にお使いください!
ソースはGitHubにアップしてあるのでご自由にお使いください!

エレキベア
やったるクマ〜〜〜〜
自動生成処理を作った経緯

エレキベア
そもそも何で一から作ったクマ?

マイケル
調べてもみたんだけど、難しそうな情報や論文ばかり・・・。
もっと手軽に自動生成したい と考えたのがきっかけだよ!
もっと手軽に自動生成したい と考えたのがきっかけだよ!

マイケル
考え抜かれたマップまでは作れないけど、
パッとそれっぽいステージが作れて遊べる なんちゃって自動生成 だね!
パッとそれっぽいステージが作れて遊べる なんちゃって自動生成 だね!

エレキベア
パッと作るくらいならそれでも十分クマね
はやく作りたいクマ〜〜〜〜
はやく作りたいクマ〜〜〜〜

マイケル
それでは早速トライしてみよう!
マップ自動生成のアルゴリズム

マイケル
まずおさらいになりますが、マップの構成は下記のように
数字でオブジェクトを表して表示しています!
数字でオブジェクトを表して表示しています!
{1,1,1,1,1,1,1,1,1,1},
{1,0,1,1,0,1,1,1,0,1},
{1,1,1,0,0,0,0,1,1,1},
{1,1,0,0,0,3,0,0,1,1},
{1,1,1,0,4,4,0,1,0,1},
{1,1,0,0,2,0,0,0,0,1},
{1,0,0,1,0,0,0,3,0,1},
{1,0,0,0,0,0,0,0,1,1},
{1,1,1,1,1,1,1,1,1,1},

エレキベア
この配列の数字を自動で決めていくクマね

マイケル
その通り!
そして自動生成の方針としては、下記で進めていくよ!
そして自動生成の方針としては、下記で進めていくよ!
自動生成の方針
- ゴールから逆算してボックスとプレイヤーを配置し、歩行経路以外はランダムでブロックで埋める。

エレキベア
ゴールから逆算クマか

マイケル
まずゴールを置いて、そこから離していくやり方だね!
具体的には下記のような流れになるよ!
具体的には下記のような流れになるよ!

エレキベア
分かったような分からないようなクマ・・・

マイケル
このやり方を一つ一つ実装します!
各処理のソースをみていきましょう!
各処理のソースをみていきましょう!
自動生成処理の実装

マイケル
まず全体の処理としては以下になります!
#include <stdio.h>
#include <rand.h>
#include "tile.h"
/* マップ情報(10x9) */
const int MAP_WIDTH = 10;
const int MAP_HEIGHT = 9;
// マップ配列
unsigned int map[9][10];
// 初期化用デフォルトマップ
unsigned int default_map[9][10] =
{
{1,1,1,1,1,1,1,1,1,1},
{1,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1},
};
・・・略・・・
/* 移動方向 */
const int LEFT_MOVE = 0;
const int RIGHT_MOVE = 1;
const int UP_MOVE = 2;
const int DOWN_MOVE = 3;
/* ループ回数制限用 */
const int MAX_LOOP_COUNT = 50;
int loop_count = 0;
/* プロトタイプ宣言 */
void generate_map(); // マップ配列自動生成処理
void set_map_tiles(int[], int[], int); // マップのタイル設定処理
int randint(int, int); // 乱数生成処理
/* マップ配列生成処理 */
void generate_map()
{
// デフォルトのマップ情報を設定
for (int i = 0; i < MAP_HEIGHT; i++) { // Y方向の配列数
for (int j = 0; j < MAP_WIDTH; j++) { // X方向の配列数
map[i][j] = default_map[i][j];
}
}
// マップ生成のための変数定義
int point_pos[2] = {0, 0}; // ポイント位置
int last_walk_pos[2] = {0, 0}; // 最後の歩行ポイント位置
// 1つ目のポイントをランダムに決めてマップを生成
point_pos[0] = randint(1, 8);
point_pos[1] = randint(1, 7);
set_map_tiles(point_pos, last_walk_pos, 5);
// 2つ目のポイントを最後の歩行ポイント位置としてマップを生成
point_pos[0] = last_walk_pos[0];
point_pos[1] = last_walk_pos[1];
set_map_tiles(point_pos, last_walk_pos, 3);
// 最後の歩行ポイント位置をプレイヤー位置とする
map[last_walk_pos[1]][last_walk_pos[0]] = PLAYER_TILE_NO;
// 生成されたマップを整形
for(int j = 0; j < MAP_HEIGHT; j++) { // Y方向の配列数
for (int i = 0; i < MAP_WIDTH; i++) { // X方向の配列数
if (map[j][i] == BLANK_TILE_NO) {
// ブランクタイルの場合、2分の1の確率でブロックタイルに設定
if (randint(0, 1) == 0) {
map[j][i] = BLOCK_TILE_NO;
}
} else if (map[j][i] == WALK_TILE_NO) {
// 歩行経路のタイルの場合、ブランクタイルに設定
map[j][i] = BLANK_TILE_NO;
}
}
}
}
/* マップのタイル設定処理 */
void set_map_tiles(int point_pos[2], int last_walk_pos[2], int move_count)
{
// 移動方向格納用
int move_vec = -1; // 移動方向
int pre_move_vec = -1; // 一つ前の移動方向
// 位置格納用
int box_pos[] = {0, 0}; // ボックス位置
int walk_pos[] = {0, 0}; // 歩行ポイント位置
int pre_box_pos[] = {0, 0}; // 一つ前のボックス位置
int pre_walk_pos[] = {0, 0}; // 一つ前の歩行ポイント位置
int walk2_pos[] = {0, 0}; // 歩行ポイント位置2(経路用)
int walk3_pos[] = {0, 0}; // 歩行ポイント位置3(経路用)
/* ポイント位置を基準にマップの自動生成を行う*/
// ポイント位置を基準とする
pre_box_pos[0] = point_pos[0];
pre_box_pos[1] = point_pos[1];
// ループ回数初期化
loop_count = 0;
// ボックスと歩行ポイントをポイントの直線上に並べて置く
while (1) {
// 4方向をランダムで決める
move_vec = randint(0, 3);
if (move_vec == LEFT_MOVE) {
box_pos[0] = pre_box_pos[0] - 1;
box_pos[1] = pre_box_pos[1];
walk_pos[0] = pre_box_pos[0] - 2;
walk_pos[1] = pre_box_pos[1];
} else if (move_vec == RIGHT_MOVE) {
box_pos[0] = pre_box_pos[0] + 1;
box_pos[1] = pre_box_pos[1];
walk_pos[0] = pre_box_pos[0] + 2;
walk_pos[1] = pre_box_pos[1];
} else if (move_vec == UP_MOVE) {
box_pos[0] = pre_box_pos[0];
box_pos[1] = pre_box_pos[1] - 1;
walk_pos[0] = pre_box_pos[0];
walk_pos[1] = pre_box_pos[1] - 2;
} else if (move_vec == DOWN_MOVE) {
box_pos[0] = pre_box_pos[0];
box_pos[1] = pre_box_pos[1] + 1;
walk_pos[0] = pre_box_pos[0];
walk_pos[1] = pre_box_pos[1] + 2;
}
// 歩行ポイント(2マス先)が置ける範囲なら置く
// マップの範囲 かつ ブランクか歩行経路のタイル
if (0 < walk_pos[0] && walk_pos[0] < MAP_WIDTH-1 && 0 < walk_pos[1] && walk_pos[1] < MAP_HEIGHT-1
&& (map[walk_pos[1]][walk_pos[0]] == BLANK_TILE_NO || map[walk_pos[1]][walk_pos[0]] == WALK_TILE_NO)
&& (map[box_pos[1]][box_pos[0]] == BLANK_TILE_NO || map[box_pos[1]][box_pos[0]] == WALK_TILE_NO)) {
// 移動した方向を保持
pre_move_vec = move_vec;
break;
}
// ループ回数が最大数を超えたら処理終了
loop_count++;
if (loop_count >= MAX_LOOP_COUNT) {
// 最終的な位置はポイント位置として返却
last_walk_pos[0] = point_pos[0];
last_walk_pos[1] = point_pos[1];
return;
}
}
// ポイント、ボックス、歩行ポイントを設定
map[point_pos[1]][point_pos[0]] = POINT_TILE_NO;
map[box_pos[1]][box_pos[0]] = BOX_TILE_NO;
map[walk_pos[1]][walk_pos[0]] = WALK_TILE_NO;
// ボックスを指定回数動かしながら歩行経路を埋める
for (int i = 0; i < move_count; i++) {
// 最後のボックス位置を基準とする
pre_box_pos[0] = box_pos[0];
pre_box_pos[1] = box_pos[1];
pre_walk_pos[0] = walk_pos[0];
pre_walk_pos[1] = walk_pos[1];
// ループ回数初期化
loop_count = 0;
while (1) {
// 4方向をランダムで決める
move_vec = randint(0, 3);
if (move_vec == LEFT_MOVE) {
box_pos[0] = pre_box_pos[0] - 1;
box_pos[1] = pre_box_pos[1];
walk_pos[0] = pre_box_pos[0] - 2;
walk_pos[1] = pre_box_pos[1];
} else if (move_vec == RIGHT_MOVE) {
box_pos[0] = pre_box_pos[0] + 1;
box_pos[1] = pre_box_pos[1];
walk_pos[0] = pre_box_pos[0] + 2;
walk_pos[1] = pre_box_pos[1];
} else if (move_vec == UP_MOVE) {
box_pos[0] = pre_box_pos[0];
box_pos[1] = pre_box_pos[1] - 1;
walk_pos[0] = pre_box_pos[0];
walk_pos[1] = pre_box_pos[1] - 2;
} else if (move_vec == DOWN_MOVE) {
box_pos[0] = pre_box_pos[0];
box_pos[1] = pre_box_pos[1] + 1;
walk_pos[0] = pre_box_pos[0];
walk_pos[1] = pre_box_pos[1] + 2;
}
// 歩行ポイント(2マス先)が置ける範囲なら置く
// マップの範囲 かつ ブランクか歩行経路のタイル
if (0 < walk_pos[0] && walk_pos[0] < MAP_WIDTH-1 && 0 < walk_pos[1] && walk_pos[1] < MAP_HEIGHT-1
&& (map[walk_pos[1]][walk_pos[0]] == BLANK_TILE_NO || map[walk_pos[1]][walk_pos[0]] == WALK_TILE_NO)
&& (map[box_pos[1]][box_pos[0]] == BLANK_TILE_NO || map[box_pos[1]][box_pos[0]] == WALK_TILE_NO)) {
// ボックスの移動方向が変わった場合、周囲に歩行経路を設定
walk2_pos[0] = -1;
walk2_pos[1] = -1;
walk3_pos[0] = -1;
walk3_pos[1] = -1;
if (pre_move_vec == LEFT_MOVE && move_vec == UP_MOVE) {
walk2_pos[0] = walk_pos[0] - 1;
walk3_pos[0] = walk_pos[0] - 1;
walk2_pos[1] = walk_pos[1];
walk3_pos[1] = walk_pos[1] + 1;
} else if (pre_move_vec == LEFT_MOVE && move_vec == DOWN_MOVE) {
walk2_pos[0] = walk_pos[0] - 1;
walk3_pos[0] = walk_pos[0] - 1;
walk2_pos[1] = walk_pos[1];
walk3_pos[1] = walk_pos[1] - 1;
} else if (pre_move_vec == RIGHT_MOVE && move_vec == UP_MOVE) {
walk2_pos[0] = walk_pos[0] + 1;
walk3_pos[0] = walk_pos[0] + 1;
walk2_pos[1] = walk_pos[1];
walk3_pos[1] = walk_pos[1] + 1;
} else if (pre_move_vec == RIGHT_MOVE && move_vec == DOWN_MOVE) {
walk2_pos[0] = walk_pos[0] + 1;
walk3_pos[0] = walk_pos[0] + 1;
walk2_pos[1] = walk_pos[1];
walk3_pos[1] = walk_pos[1] - 1;
} else if (pre_move_vec == UP_MOVE && move_vec == LEFT_MOVE) {
walk2_pos[0] = walk_pos[0];
walk3_pos[0] = walk_pos[0] + 1;
walk2_pos[1] = walk_pos[1] - 1;
walk3_pos[1] = walk_pos[1] - 1;
} else if (pre_move_vec == UP_MOVE && move_vec == RIGHT_MOVE) {
walk2_pos[0] = walk_pos[0];
walk3_pos[0] = walk_pos[0] - 1;
walk2_pos[1] = walk_pos[1] - 1;
walk3_pos[1] = walk_pos[1] - 1;
} else if (pre_move_vec == DOWN_MOVE && move_vec == LEFT_MOVE) {
walk2_pos[0] = walk_pos[0];
walk3_pos[0] = walk_pos[0] + 1;
walk2_pos[1] = walk_pos[1] + 1;
walk3_pos[1] = walk_pos[1] + 1;
} else if (pre_move_vec == DOWN_MOVE && move_vec == RIGHT_MOVE) {
walk2_pos[0] = walk_pos[0];
walk3_pos[0] = walk_pos[0] - 1;
walk2_pos[1] = walk_pos[1] + 1;
walk3_pos[1] = walk_pos[1] + 1;
}
// 上記で設定された場合
if (walk2_pos[0] != -1 && walk3_pos[0] != -1) {
// ブランクタイル以外の場合、やり直し
if (map[walk2_pos[1]][walk2_pos[0]] != BLANK_TILE_NO
|| map[walk3_pos[1]][walk3_pos[0]] != BLANK_TILE_NO) {
loop_count++;
if (loop_count >= MAX_LOOP_COUNT) {
last_walk_pos[0] = pre_walk_pos[0];
last_walk_pos[1] = pre_walk_pos[1];
return;
}
continue;
}
// 歩行経路を設定
map[walk2_pos[1]][walk2_pos[0]] = WALK_TILE_NO;
map[walk3_pos[1]][walk3_pos[0]] = WALK_TILE_NO;
}
// 移動した方向を保持
pre_move_vec = move_vec;
break;
}
// ループ回数が最大数を超えたら処理終了
loop_count++;
if (loop_count >= MAX_LOOP_COUNT) {
// 最終的な位置は最後の歩行ポイントとして返却
last_walk_pos[0] = pre_walk_pos[0];
last_walk_pos[1] = pre_walk_pos[1];
return;
}
}
// ボックス、歩行ポイントを設定
map[box_pos[1]][box_pos[0]] = BOX_TILE_NO;
map[pre_box_pos[1]][pre_box_pos[0]] = WALK_TILE_NO;
map[walk_pos[1]][walk_pos[0]] = WALK_TILE_NO;
}
// 最後の歩行ポイントを設定
last_walk_pos[0] = walk_pos[0];
last_walk_pos[1] = walk_pos[1];
}
// 乱数を範囲指定で生成
int randint(int min,int max)
{
// 計算式;min + rand()%(max-min+1)
// ※シード値によりrand()が負の値になる場合があるため注意
int random = rand()%(max-min+1);
if (random < 0) {
random *= -1; // 符号を反転
}
random += min;
return random;
}

エレキベア
長すぎて頭痛いクマ・・・。

マイケル
一つ一つ見ていこう!
ポイントを任意の位置に置く

マイケル
まずはゴールのポイントを一つ決めておきます!
こちらはランダムで設定しています。
こちらはランダムで設定しています。
・・・略・・・
/* マップ配列生成処理 */
void generate_map()
{
// デフォルトのマップ情報を設定
for (int i = 0; i < MAP_HEIGHT; i++) { // Y方向の配列数
for (int j = 0; j < MAP_WIDTH; j++) { // X方向の配列数
map[i][j] = default_map[i][j];
}
}
// マップ生成のための変数定義
int point_pos[2] = {0, 0}; // ポイント位置
int last_walk_pos[2] = {0, 0}; // 最後の歩行ポイント位置
// 1つ目のポイントをランダムに決めてマップを生成
point_pos[0] = randint(1, 8);
point_pos[1] = randint(1, 7);
set_map_tiles(point_pos, last_walk_pos, 5);
・・・略・・・
}
・・・略・・・
// 乱数を範囲指定で生成
int randint(int min,int max)
{
// 計算式;min + rand()%(max-min+1)
// ※シード値によりrand()が負の値になる場合があるため注意
int random = rand()%(max-min+1);
if (random < 0) {
random *= -1; // 符号を反転
}
random += min;
return random;
}

マイケル
ランダム値は乱数を使っていますが、
初期化処理はメイン処理の方で行っています!
初期化処理はメイン処理の方で行っています!
// 乱数シードを保持
UWORD seed = DIV_REG;
// ボタンの入力検知
button = joypad();
pre_button = button;
while (1) {
button = joypad();
if (pre_button != button) {
// スタートボタン押下で次処理へ
if (button & J_START) {
break;
}
}
pre_button = button;
}
// 乱数初期化
// ※ボタン押下までの時間を使用してランダム調整
seed |= (UWORD)DIV_REG << 8;
initrand(seed);

マイケル
ボタン押下までの時間でランダム値を調整しているのがミソです!

エレキベア
ゲームボーイならではクマね
ポイントの直線上にボックスと歩行ポイントを置く

マイケル
そして次はボックスとプレイヤーの歩行ポイントを置きます!
プレイヤーはボックスを押せる位置にいる必要があるため、
必然的にポイントの2マス先の位置になります!
プレイヤーはボックスを押せる位置にいる必要があるため、
必然的にポイントの2マス先の位置になります!
・・・略・・・
/* 移動方向 */
const int LEFT_MOVE = 0;
const int RIGHT_MOVE = 1;
const int UP_MOVE = 2;
const int DOWN_MOVE = 3;
/* ループ回数制限用 */
const int MAX_LOOP_COUNT = 50;
int loop_count = 0;
・・・略・・・
/* マップのタイル設定処理 */
void set_map_tiles(int point_pos[2], int last_walk_pos[2], int move_count)
{
// 移動方向格納用
int move_vec = -1; // 移動方向
int pre_move_vec = -1; // 一つ前の移動方向
// 位置格納用
int box_pos[] = {0, 0}; // ボックス位置
int walk_pos[] = {0, 0}; // 歩行ポイント位置
int pre_box_pos[] = {0, 0}; // 一つ前のボックス位置
int pre_walk_pos[] = {0, 0}; // 一つ前の歩行ポイント位置
int walk2_pos[] = {0, 0}; // 歩行ポイント位置2(経路用)
int walk3_pos[] = {0, 0}; // 歩行ポイント位置3(経路用)
/* ポイント位置を基準にマップの自動生成を行う*/
// ポイント位置を基準とする
pre_box_pos[0] = point_pos[0];
pre_box_pos[1] = point_pos[1];
// ループ回数初期化
loop_count = 0;
// ボックスと歩行ポイントをポイントの直線上に並べて置く
while (1) {
// 4方向をランダムで決める
move_vec = randint(0, 3);
if (move_vec == LEFT_MOVE) {
box_pos[0] = pre_box_pos[0] - 1;
box_pos[1] = pre_box_pos[1];
walk_pos[0] = pre_box_pos[0] - 2;
walk_pos[1] = pre_box_pos[1];
} else if (move_vec == RIGHT_MOVE) {
box_pos[0] = pre_box_pos[0] + 1;
box_pos[1] = pre_box_pos[1];
walk_pos[0] = pre_box_pos[0] + 2;
walk_pos[1] = pre_box_pos[1];
} else if (move_vec == UP_MOVE) {
box_pos[0] = pre_box_pos[0];
box_pos[1] = pre_box_pos[1] - 1;
walk_pos[0] = pre_box_pos[0];
walk_pos[1] = pre_box_pos[1] - 2;
} else if (move_vec == DOWN_MOVE) {
box_pos[0] = pre_box_pos[0];
box_pos[1] = pre_box_pos[1] + 1;
walk_pos[0] = pre_box_pos[0];
walk_pos[1] = pre_box_pos[1] + 2;
}
// 歩行ポイント(2マス先)が置ける範囲なら置く
// マップの範囲 かつ ブランクか歩行経路のタイル
if (0 < walk_pos[0] && walk_pos[0] < MAP_WIDTH-1 && 0 < walk_pos[1] && walk_pos[1] < MAP_HEIGHT-1
&& (map[walk_pos[1]][walk_pos[0]] == BLANK_TILE_NO || map[walk_pos[1]][walk_pos[0]] == WALK_TILE_NO)
&& (map[box_pos[1]][box_pos[0]] == BLANK_TILE_NO || map[box_pos[1]][box_pos[0]] == WALK_TILE_NO)) {
// 移動した方向を保持
pre_move_vec = move_vec;
break;
}
// ループ回数が最大数を超えたら処理終了
loop_count++;
if (loop_count >= MAX_LOOP_COUNT) {
// 最終的な位置はポイント位置として返却
last_walk_pos[0] = point_pos[0];
last_walk_pos[1] = point_pos[1];
return;
}
}
・・・略・・・
}

エレキベア
ここまでは簡単クマね
ボックスを動かす

マイケル
ここから何回かボックスを動かしていきます!
下記は最初と同様、2マス先まで考慮した移動処理になります!
下記は最初と同様、2マス先まで考慮した移動処理になります!
・・・略・・・
/* マップのタイル設定処理 */
void set_map_tiles(int point_pos[2], int last_walk_pos[2], int move_count)
{
・・・略・・・
// ポイント、ボックス、歩行ポイントを設定
map[point_pos[1]][point_pos[0]] = POINT_TILE_NO;
map[box_pos[1]][box_pos[0]] = BOX_TILE_NO;
map[walk_pos[1]][walk_pos[0]] = WALK_TILE_NO;
// ボックスを指定回数動かしながら歩行経路を埋める
for (int i = 0; i < move_count; i++) {
// 最後のボックス位置を基準とする
pre_box_pos[0] = box_pos[0];
pre_box_pos[1] = box_pos[1];
pre_walk_pos[0] = walk_pos[0];
pre_walk_pos[1] = walk_pos[1];
// ループ回数初期化
loop_count = 0;
while (1) {
// 4方向をランダムで決める
move_vec = randint(0, 3);
if (move_vec == LEFT_MOVE) {
box_pos[0] = pre_box_pos[0] - 1;
box_pos[1] = pre_box_pos[1];
walk_pos[0] = pre_box_pos[0] - 2;
walk_pos[1] = pre_box_pos[1];
} else if (move_vec == RIGHT_MOVE) {
box_pos[0] = pre_box_pos[0] + 1;
box_pos[1] = pre_box_pos[1];
walk_pos[0] = pre_box_pos[0] + 2;
walk_pos[1] = pre_box_pos[1];
} else if (move_vec == UP_MOVE) {
box_pos[0] = pre_box_pos[0];
box_pos[1] = pre_box_pos[1] - 1;
walk_pos[0] = pre_box_pos[0];
walk_pos[1] = pre_box_pos[1] - 2;
} else if (move_vec == DOWN_MOVE) {
box_pos[0] = pre_box_pos[0];
box_pos[1] = pre_box_pos[1] + 1;
walk_pos[0] = pre_box_pos[0];
walk_pos[1] = pre_box_pos[1] + 2;
}
・・・略・・・
// ループ回数が最大数を超えたら処理終了
loop_count++;
if (loop_count >= MAX_LOOP_COUNT) {
// 最終的な位置は最後の歩行ポイントとして返却
last_walk_pos[0] = pre_walk_pos[0];
last_walk_pos[1] = pre_walk_pos[1];
return;
}
}
// ボックス、歩行ポイントを設定
map[box_pos[1]][box_pos[0]] = BOX_TILE_NO;
map[pre_box_pos[1]][pre_box_pos[0]] = WALK_TILE_NO;
map[walk_pos[1]][walk_pos[0]] = WALK_TILE_NO;
}
// 最後の歩行ポイントを設定
last_walk_pos[0] = walk_pos[0];
last_walk_pos[1] = walk_pos[1];
}
動かす方向が変わった場合

マイケル
ここで1点注意しないといけないのが、
移動方向が変わった場合にはプレイヤーの歩行経路を余分に設定してあげる必要がある ということです!
移動方向が変わった場合にはプレイヤーの歩行経路を余分に設定してあげる必要がある ということです!

マイケル
このように2マス先に置くだけではなく、
そこまでの歩行経路を設定してあげなければなりません・・・。
そこまでの歩行経路を設定してあげなければなりません・・・。

エレキベア
これはややこしいクマね・・・。

マイケル
(うまく書く方法もあるかもしれませんが)
ここはゴリ押しで書きます!下記のようにありうるパターンを全て書くといいでしょう!
ここはゴリ押しで書きます!下記のようにありうるパターンを全て書くといいでしょう!
・・・略・・・
// 歩行ポイント(2マス先)が置ける範囲なら置く
// マップの範囲 かつ ブランクか歩行経路のタイル
if (0 < walk_pos[0] && walk_pos[0] < MAP_WIDTH-1 && 0 < walk_pos[1] && walk_pos[1] < MAP_HEIGHT-1
&& (map[walk_pos[1]][walk_pos[0]] == BLANK_TILE_NO || map[walk_pos[1]][walk_pos[0]] == WALK_TILE_NO)
&& (map[box_pos[1]][box_pos[0]] == BLANK_TILE_NO || map[box_pos[1]][box_pos[0]] == WALK_TILE_NO)) {
// ボックスの移動方向が変わった場合、周囲に歩行経路を設定
walk2_pos[0] = -1;
walk2_pos[1] = -1;
walk3_pos[0] = -1;
walk3_pos[1] = -1;
if (pre_move_vec == LEFT_MOVE && move_vec == UP_MOVE) {
walk2_pos[0] = walk_pos[0] - 1;
walk3_pos[0] = walk_pos[0] - 1;
walk2_pos[1] = walk_pos[1];
walk3_pos[1] = walk_pos[1] + 1;
} else if (pre_move_vec == LEFT_MOVE && move_vec == DOWN_MOVE) {
walk2_pos[0] = walk_pos[0] - 1;
walk3_pos[0] = walk_pos[0] - 1;
walk2_pos[1] = walk_pos[1];
walk3_pos[1] = walk_pos[1] - 1;
} else if (pre_move_vec == RIGHT_MOVE && move_vec == UP_MOVE) {
walk2_pos[0] = walk_pos[0] + 1;
walk3_pos[0] = walk_pos[0] + 1;
walk2_pos[1] = walk_pos[1];
walk3_pos[1] = walk_pos[1] + 1;
} else if (pre_move_vec == RIGHT_MOVE && move_vec == DOWN_MOVE) {
walk2_pos[0] = walk_pos[0] + 1;
walk3_pos[0] = walk_pos[0] + 1;
walk2_pos[1] = walk_pos[1];
walk3_pos[1] = walk_pos[1] - 1;
} else if (pre_move_vec == UP_MOVE && move_vec == LEFT_MOVE) {
walk2_pos[0] = walk_pos[0];
walk3_pos[0] = walk_pos[0] + 1;
walk2_pos[1] = walk_pos[1] - 1;
walk3_pos[1] = walk_pos[1] - 1;
} else if (pre_move_vec == UP_MOVE && move_vec == RIGHT_MOVE) {
walk2_pos[0] = walk_pos[0];
walk3_pos[0] = walk_pos[0] - 1;
walk2_pos[1] = walk_pos[1] - 1;
walk3_pos[1] = walk_pos[1] - 1;
} else if (pre_move_vec == DOWN_MOVE && move_vec == LEFT_MOVE) {
walk2_pos[0] = walk_pos[0];
walk3_pos[0] = walk_pos[0] + 1;
walk2_pos[1] = walk_pos[1] + 1;
walk3_pos[1] = walk_pos[1] + 1;
} else if (pre_move_vec == DOWN_MOVE && move_vec == RIGHT_MOVE) {
walk2_pos[0] = walk_pos[0];
walk3_pos[0] = walk_pos[0] - 1;
walk2_pos[1] = walk_pos[1] + 1;
walk3_pos[1] = walk_pos[1] + 1;
}
// 上記で設定された場合
if (walk2_pos[0] != -1 && walk3_pos[0] != -1) {
// ブランクタイル以外の場合、やり直し
if (map[walk2_pos[1]][walk2_pos[0]] != BLANK_TILE_NO
|| map[walk3_pos[1]][walk3_pos[0]] != BLANK_TILE_NO) {
loop_count++;
if (loop_count >= MAX_LOOP_COUNT) {
last_walk_pos[0] = pre_walk_pos[0];
last_walk_pos[1] = pre_walk_pos[1];
return;
}
continue;
}
// 歩行経路を設定
map[walk2_pos[1]][walk2_pos[0]] = WALK_TILE_NO;
map[walk3_pos[1]][walk3_pos[0]] = WALK_TILE_NO;
}
// 移動した方向を保持
pre_move_vec = move_vec;
break;
}
・・・略・・・

エレキベア
これを繰り返せばマップができそうクマ〜〜
2つ目のポイント定義、マップ整形

マイケル
あとは2つ目のポイントを、最後の歩行ポイントの位置として同様に設定すればOK!
これで2つの箱とポイント、そして歩行経路を設定することができます!
これで2つの箱とポイント、そして歩行経路を設定することができます!
・・・略・・・
/* マップ配列生成処理 */
void generate_map()
{
・・・略・・・
// 2つ目のポイントを最後の歩行ポイント位置としてマップを生成
point_pos[0] = last_walk_pos[0];
point_pos[1] = last_walk_pos[1];
set_map_tiles(point_pos, last_walk_pos, 3);
// 最後の歩行ポイント位置をプレイヤー位置とする
map[last_walk_pos[1]][last_walk_pos[0]] = PLAYER_TILE_NO;
// 生成されたマップを整形
for(int j = 0; j < MAP_HEIGHT; j++) { // Y方向の配列数
for (int i = 0; i < MAP_WIDTH; i++) { // X方向の配列数
if (map[j][i] == BLANK_TILE_NO) {
// ブランクタイルの場合、2分の1の確率でブロックタイルに設定
if (randint(0, 1) == 0) {
map[j][i] = BLOCK_TILE_NO;
}
} else if (map[j][i] == WALK_TILE_NO) {
// 歩行経路のタイルの場合、ブランクタイルに設定
map[j][i] = BLANK_TILE_NO;
}
}
}
}
・・・略・・・

マイケル
最後に、歩行経路でない部分をランダムでブロックに変えてあげれば
それっぽいマップの完成です!
それっぽいマップの完成です!

エレキベア
やったクマ〜〜〜!!
おわりに

マイケル
というわけで今回は簡単なマップの自動生成でした!
どうだったかな?
どうだったかな?

エレキベア
一見難しそうだったクマが
一つ一つやると意外と簡単だったクマ〜〜
一つ一つやると意外と簡単だったクマ〜〜

マイケル
ゴールから考えると言うのは、
倉庫番だけでなくいろんなマップの生成にも役立ちそうだね・・・
倉庫番だけでなくいろんなマップの生成にも役立ちそうだね・・・

マイケル
それでは今日はこの辺で!
アデュー!!
アデュー!!

エレキベア
次回はついに実機実行クマ〜〜〜
【GBDK】第二.五回 倉庫番のマップ自動生成アルゴリズムを作る【補足】 〜完〜
※続きはこちら!