これは、ゲーム作りはおろかC言語初心者である40過ぎのおっさんがウィザードリィーのような疑似3D迷路を作ってみたいと思い試行錯誤を繰り返し奮闘した記録である。
今回Qiitaで執筆に至った経緯はよく「プログラミングの上達はアウトプットが云々」とブログ等で見かけるのでそれが本当かどうか確かめる意味も込めてやってみようと思った次第である。
下の方にソースコードを掲載しているので、修正したほうが良い点や、アドバイスなど頂けるとおじさんはとても嬉しいです。
##私が使ったもの
- Windows 10のパソコン
- VisualStudio2019
- C言語
- Dxライブラリ
- 高機能ドット絵エディタ EDGE
##参考にしたもの
###14歳からはじめるC言語わくわくゲームプログラミング教室
この書物はゲーム作りはおろかC言語プログラミング素人の私にとって神のような書物である。
学習にはVisual Studio 2013を使っているのだけれども、同じVisual Studioだし、なんとかなるだろうと思った。そしてなんとかなった。
前半でC言語を教えてもらい、その後パズルゲーム、横スクロールゲーム、そして今回の目標である疑似3D迷路の作り方を教えてくれた。
この場をお借りして私をわくわくさせてくれた著者に感謝申し上げたい。
そしてこの書物をアマゾンで買ってくれたかーちゃんにも感謝申し上げたい。
###ゲヱム道館館長の動画
ウィザードリィを小一時間で作ってみた【プログラミング実況】Programming Wizardry
この動画と出会わなければゲームを自分で作ってみようと思うことはなかったであろう。私はすでにこの動画を数十回繰り返し再生している。
特に参考になった部分は先に述べた「14歳からはじめる…」ではふれられていなかった1マス毎の上下左右の壁の考え方やドアシステムの箇所である。
館長にも感謝申し上げたい。
2020年2月現在、館長はweb dbpressでJava scriptでのゲーム作りを執筆されるとのことなので、必読書となるのは間違い無いであろう。
ゲヱム道館のページ
##成果物
先に現在まででどのようなものが出来上がったのか紹介させていただく。
タイトル画面、メッセージ表示、イベント、ワープ、隠し扉、一応実装できたのだが、この方法がベストなのか疑問。ある程度きれいにしたらgithubとかにあげてみようかな。ちなみにメッセージ文章とかはウィザードリィの攻略サイトから拾ってきた。 pic.twitter.com/cTkhSK65ry
— タニス (@non_tanisu) February 4, 2020
##作っていった流れ
これからソースコードを掲載してみようと思うがなにせ当方素人なのでツッコミどころは満載だろうがご容赦頂きたい。
###必要であろう変数とか定数?、関数などを定義する
#define CHIPSIZE 20 //2Dの1マスの大きさ
#define MAP_WIDTH 100 //マップの横幅
#define MAP_HEIGHT 100 //マップの縦幅
#define WALL_THICKNESS 3 //2Dマップ用の壁の厚さ
#define MOVERATE 8 //移動の制限のための値
#define DEPTH 3 //3D用の見える奥行き
#define START_2DX 800 //2Dを実際描画するためのx座標
extern char g_mapdata[MAP_HEIGHT][MAP_WIDTH];
extern char g_doordata[MAP_HEIGHT][MAP_WIDTH];
extern char g_s_doordata[MAP_HEIGHT][MAP_WIDTH];
extern int g_margeMapData[MAP_HEIGHT][MAP_WIDTH][4];
extern int g_trapData[MAP_HEIGHT][MAP_WIDTH];
enum class PlayMode {
PGM_EVENT, PGM_EXPLORE
};
struct StageData {
int mapwidth, mapheight;
int stagenum;
PlayMode playmode;
};
extern StageData stageData;
struct PlayerData {
unsigned int x, y, movecounter, direction,max_hp,hp,deadtime;
BOOL deadflag;
};
extern PlayerData playerData;
void GameMain();
void InitMap();
void InitPlayer();
void Draw2DMap();
void Draw3DMap();
void DrawPlayer();
void CheckCurrent();
void ShowEvent();
void DeadEvent();
次にプレイヤーの座標等の初期化処理を行う
void InitPlayer() {
playerData.x = 0;//プレイヤーの初期位置X
playerData.y = 19;//プレイヤーの初期位置Y
playerData.direction = (int)Direction::NORTH;//プレイヤーの初期の向き
playerData.deadflag = FALSE;//プレイヤーの死んでるかどうかフラグ
playerData.max_hp = GetRand(20) + 9;//プレイヤーの最大HP(当分使わない)
playerData.hp = playerData.max_hp;//プレイヤーの現在のHP
playerData.deadtime = 0;//プレイヤーの死んだ回数
stageData.playmode = PlayMode::PGM_EXPLORE;//現在のゲームモード(冒険中)
}
###2Dで作ってみる
「14歳からはじめる…」でも最初は疑似3Dではなく2Dでマップ作成していたのでそれに合わせて2Dマップを描画させてみた。
書籍では壁のマス、通路のマスを0,1で表現していたのだが、ウィザードリィ風にする場合は1マス毎に上下左右の方向があるためゲヱム道館館長の動画を参考に少し工夫してみた。
####1マス毎の壁のパターン作成
extern int g_wall[][4];
int g_wall[][4] = {
//上、左、下、右
{0,0,0,0},//0 無、無、無、無
{1,1,1,1},//1 上、左、下、右
{1,0,0,0},//2 上、無、無、無
{1,1,0,0},//3 上、左、無、無
{1,0,1,0},//4 上、無、下、無
{1,0,0,1},//5 上、無、無、右
{1,1,1,0},//6 上、左、下、無
{1,0,1,1},//7 上、無、下、右
{1,1,0,1},//8 上、左、無、右
{0,1,0,0},//9 無、左、無、無
{0,1,1,0},//: 無、左、下、無
{0,1,0,1},//; 無、左、無、右
{0,1,1,1},//< 無、下、左、右
{0,0,1,0},//= 無、無、下、無
{0,0,1,1},//> 無、無、下、右
{0,0,0,1},//? 無、無、無、右
};
上記の様にマス目ごとに壁のパターンを定義し、マップデータの文字、0~?に対応する配列を作った
次に読み込み用のマップデータの作成を行った。
6711113251;867673511
67624790?3?;1167:>35
111;67:=>9?;886767:>
325<32588<;;;;376735
90?190?;;3?;;;;867:>
:=>8:=>;;9?;;;;;6711
164?186>;9>;;;;;6735
32595;35<95;;;;;88:>
:=>:><:>19?;;;;;<;11
6444444440?<<<<<1<67
6444444479=444444447
344244247<8344442447
;35;37<653?;1165;345
;9?;;325;9?;111<;;8;
;9?;;:=>;9?;1135;;;;
;9><:222?9?;119?;;;;
;;3259==>9?;11:><;;;
;;:=>;3259?;11116>;;
;:444>:=>9?:444444><
:44444447:?644444447
02=?=?000?9?:>9?90==
=?602?900000220=0022
29209000=0009?959?90
000=0009200000900=00
00?190000000000?95:=
00020000000000009022
?=00200000000000:>90
0200?;90=000000022==
00000=00200000000022
00000200000=0=0=?=02
0000000000020202020?
00000000?90000000000
000000=00000?=900000
0000002000002?==0000
0000000000002?320000
000=000000002?909000
0002000000002?:=0000
00000000000029989000
00000=0=00000000000=
00000002000900000000
000?0?000000==0000==
0?9000000000220=0022
09009000=00000020000
00000009000000000000
000000000000000000==
00000000000000009022
?0002000000000000000
000000000000000000==
00000000000000000022
00000000000000000000
0000000000000000000?
00000000000000000000
000000000000?=000000
0000000000002?000000
00000000000020000000
00000000000020009000
00000000000020000000
00000000000029990000
00000000000000000000
00000000000000000000
これらのデータを読み込みマップを作ってみた。
void InitMap() {
char buf[256];
//マップ壁データ
sprintf_s(buf, 256, "media\\stage%d.txt", stage_num);
int fh = FileRead_open(buf);
int map_y = 0;
while (FileRead_eof(fh) == 0) {
FileRead_gets(g_mapdata[map_y], MAP_WIDTH, fh);
map_y++;
}
FileRead_close(fh);
stageData.mapwidth = (int)strlen(g_mapdata[0]);
stageData.mapheight = map_y;
//マップドアデータ
sprintf_s(buf, 256, "media\\d_stage%d.txt", stage_num);
fh = FileRead_open(buf);
map_y = 0;
while (FileRead_eof(fh) == 0) {
FileRead_gets(g_doordata[map_y], MAP_WIDTH, fh);
map_y++;
}
FileRead_close(fh);
//マップ隠しドアデータ
sprintf_s(buf, 256, "media\\sd_stage%d.txt", stage_num);
fh = FileRead_open(buf);
map_y = 0;
while (FileRead_eof(fh) == 0) {
FileRead_gets(g_s_doordata[map_y], MAP_WIDTH, fh);
map_y++;
}
FileRead_close(fh);
//それぞれのデータを1つの変数 g_margeMapData にまとめる
for (int y = 0; y < stageData.mapheight; y++) {
for (int x = 0; x < stageData.mapwidth; x++) {
int wall = g_mapdata[y][x] - '0';
int door = g_doordata[y][x] - '0';
int s_door = g_s_doordata[y][x] - '0';
//X,Y座標に加え上下左右のデータも入れていく
for (int i = 0; i < 4; i++) {
int marge_wall = g_wall[wall][i] + g_door[door][i] + g_door[s_door][i];
g_margeMapData[y][x][i] = marge_wall;
}
}
}
}
似たような処理を3回も書いてるのでもっとキレイに出来そうな気もするのだが、今回始めてのC言語プログラミングだから気にしないこととする。
上記の処理でg_margeMapDataに座標と、その座標に対しての「無、壁、ドア、シークレットドア(0,1,2,3)」のデータを保持させた。
そのデータを用いて2Dマップを描画してみる
void Draw2DMap() {
for (int y = 0; y < stageData.mapheight; y++) {
for (int x = 0; x < stageData.mapwidth; x++) {
for (int i = 0; i < (int)Direction::DIRECTION_MAX; i++) {
if (g_margeMapData[y][x][i] >= 1) {
unsigned int Cr = 0;
switch (g_margeMapData[y][x][i]) {
case 1://壁のときは赤色
Cr = GetColor(255, 0, 0);
break;
case 2://ドアのときは緑色
Cr = GetColor(0, 200, 0);
break;
case 3://シークレットドアのときは黄色
Cr = GetColor(255, 255, 0);
break;
}
int wall_thickness;
switch (i) {
case 0:
case 1:
wall_thickness = WALL_THICKNESS;
break;
case 2:
case 3:
wall_thickness = WALL_THICKNESS * -1;
}
if (i % 2 == 0) {//横線
DrawBox(x * CHIPSIZE + START_2DX, y * CHIPSIZE + (CHIPSIZE * i / 2), x * CHIPSIZE + CHIPSIZE + START_2DX, y * CHIPSIZE + (CHIPSIZE * i / 2) + wall_thickness, Cr, TRUE);
}
else {//縦線
DrawBox(x * CHIPSIZE + (CHIPSIZE * (i - 1) / 2) + START_2DX , y * CHIPSIZE, x * CHIPSIZE + (CHIPSIZE * (i - 1) / 2) + wall_thickness + START_2DX, y * CHIPSIZE + CHIPSIZE, Cr, TRUE);
}
}
}
}
}
//player自身の描画
DrawBox(
playerData.x * CHIPSIZE + 3 + START_2DX,
playerData.y * CHIPSIZE + 3,
playerData.x * CHIPSIZE + CHIPSIZE -3 + START_2DX,
playerData.y * CHIPSIZE + CHIPSIZE - 3,
GetColor(255, 255, 255),
TRUE);
//player方向の描画
DrawBox(
(playerData.x + g_mv[playerData.direction][0]) * CHIPSIZE + 8 + START_2DX,
(playerData.y + g_mv[playerData.direction][1]) * CHIPSIZE + 8,
(playerData.x + g_mv[playerData.direction][0]) * CHIPSIZE + CHIPSIZE - 8 + START_2DX,
(playerData.y + g_mv[playerData.direction][1]) * CHIPSIZE + CHIPSIZE - 8,
GetColor(0, 210, 255),
TRUE);
}
これをメイン関数で呼び出すと下記のような2D画像ができる。
続いて移動処理を作った流れを書いていこうかと思ったのだが、疲れたので次の機会に書いてみるかもしれない。
##拙い私のソースコード
とりあえず3D描画含めすべてのソースは下記の通りである。
自信の無いソースコードを晒すという行為は不特定多数の方々に恥部を見られるような気持ちである。
###画像を読み込むヘッダー
#ifndef __LOADING_H__
#define __LOADING_H__
#include <DxLib.h>
#define LOCATION 12
enum class Direction {
NORTH,
WEST,
SOUTH,
EAST,
DIRECTION_MAX
};
struct ImageHandles {
int walls[LOCATION * 4];
int doors[LOCATION * 4];
int dark[4];
int chara;
int floor;
int title;
int window;
int window_l;
int parson;
};
extern ImageHandles g_imagehandles;
BOOL LoadGameImage();
#endif
###実際に画像を読み込む処理
#include "loading.h"
ImageHandles g_imagehandles;
BOOL LoadGameImage() {
for (int i = 0; i < LOCATION * 4; i++) {
g_imagehandles.walls[i] = -1;
g_imagehandles.doors[i] = -1;
}
for (int i = 0; i < 4; i++) {
g_imagehandles.dark[i] = -1;
}
//左手前 0
//0
if ((g_imagehandles.walls[0 * 4 + (int)Direction::NORTH] = LoadGraph("media\\wall\\0_n.png")) == -1)return FALSE;
//3
if ((g_imagehandles.walls[0 * 4 + (int)Direction::EAST] = LoadGraph("media\\wall\\0_e.png")) == -1) return FALSE;
if ((g_imagehandles.doors[0 * 4 + (int)Direction::EAST] = LoadGraph("media\\wall\\0_door_e.png")) == -1) return FALSE;
//中手前 1
//4
if ((g_imagehandles.walls[1 * 4 + (int)Direction::NORTH] = LoadGraph("media\\wall\\1_n.png")) == -1) return FALSE;
if ((g_imagehandles.doors[1 * 4 + (int)Direction::NORTH] = LoadGraph("media\\wall\\1_door_n.png")) == -1) return FALSE;
//5
if ((g_imagehandles.walls[1 * 4 + (int)Direction::WEST] = LoadGraph("media\\wall\\1_w.png")) == -1) return FALSE;
if ((g_imagehandles.doors[1 * 4 + (int)Direction::WEST] = LoadGraph("media\\wall\\1_door_w.png")) == -1) return FALSE;
//7
if ((g_imagehandles.walls[1 * 4 + (int)Direction::EAST] = LoadGraph("media\\wall\\1_e.png")) == -1) return FALSE;
if ((g_imagehandles.doors[1 * 4 + (int)Direction::EAST] = LoadGraph("media\\wall\\1_door_e.png")) == -1) return FALSE;
//右手前 2
//8
if ((g_imagehandles.walls[2 * 4 + (int)Direction::NORTH] = LoadGraph("media\\wall\\2_n.png")) == -1) return FALSE;
//9
if ((g_imagehandles.walls[2 * 4 + (int)Direction::WEST] = LoadGraph("media\\wall\\2_w.png")) == -1) return FALSE;
if ((g_imagehandles.doors[2 * 4 + (int)Direction::WEST] = LoadGraph("media\\wall\\2_door_w.png")) == -1) return FALSE;
//左中央 3
//12
if ((g_imagehandles.walls[3 * 4 + (int)Direction::NORTH] = LoadGraph("media\\wall\\3_n.png")) == -1) return FALSE;
if ((g_imagehandles.doors[3 * 4 + (int)Direction::NORTH] = LoadGraph("media\\wall\\3_door_n.png")) == -1) return FALSE;
//14
if ((g_imagehandles.walls[3 * 4 + (int)Direction::SOUTH] = LoadGraph("media\\wall\\3_s.png")) == -1) return FALSE;
//15
if ((g_imagehandles.walls[3 * 4 + (int)Direction::EAST] = LoadGraph("media\\wall\\3_e.png")) == -1) return FALSE;
if ((g_imagehandles.doors[3 * 4 + (int)Direction::EAST] = LoadGraph("media\\wall\\3_door_e.png")) == -1) return FALSE;
//中中央 4
//16
if ((g_imagehandles.walls[4 * 4 + (int)Direction::NORTH] = LoadGraph("media\\wall\\4_n.png")) == -1) return FALSE;
if ((g_imagehandles.doors[4 * 4 + (int)Direction::NORTH] = LoadGraph("media\\wall\\4_door_n.png")) == -1) return FALSE;
//17
if ((g_imagehandles.walls[4 * 4 + (int)Direction::WEST] = LoadGraph("media\\wall\\4_w.png")) == -1) return FALSE;
if ((g_imagehandles.doors[4 * 4 + (int)Direction::WEST] = LoadGraph("media\\wall\\4_door_w.png")) == -1) return FALSE;
//18
if ((g_imagehandles.walls[4 * 4 + (int)Direction::SOUTH] = LoadGraph("media\\wall\\4_s.png")) == -1) return FALSE;
if ((g_imagehandles.doors[4 * 4 + (int)Direction::SOUTH] = LoadGraph("media\\wall\\4_door_s.png")) == -1) return FALSE;
//19
if ((g_imagehandles.walls[4 * 4 + (int)Direction::EAST] = LoadGraph("media\\wall\\4_e.png")) == -1) return FALSE;
if ((g_imagehandles.doors[4 * 4 + (int)Direction::EAST] = LoadGraph("media\\wall\\4_door_e.png")) == -1) return FALSE;
//右中央 5
//20
if ((g_imagehandles.walls[5 * 4 + (int)Direction::NORTH] = LoadGraph("media\\wall\\5_n.png")) == -1) return FALSE;
if ((g_imagehandles.doors[5 * 4 + (int)Direction::NORTH] = LoadGraph("media\\wall\\5_door_n.png")) == -1) return FALSE;
//21
if ((g_imagehandles.walls[5 * 4 + (int)Direction::WEST] = LoadGraph("media\\wall\\5_w.png")) == -1) return FALSE;
if ((g_imagehandles.doors[5 * 4 + (int)Direction::WEST] = LoadGraph("media\\wall\\5_door_w.png")) == -1) return FALSE;
//22
if ((g_imagehandles.walls[5 * 4 + (int)Direction::SOUTH] = LoadGraph("media\\wall\\5_s.png")) == -1) return FALSE;
//左奥 6
//24
if ((g_imagehandles.walls[6 * 4 + (int)Direction::NORTH] = LoadGraph("media\\wall\\6_n.png")) == -1) return FALSE;
if ((g_imagehandles.doors[6 * 4 + (int)Direction::NORTH] = LoadGraph("media\\wall\\6_door_n.png")) == -1) return FALSE;
//26
if ((g_imagehandles.walls[6 * 4 + (int)Direction::SOUTH] = LoadGraph("media\\wall\\6_s.png")) == -1) return FALSE;
if ((g_imagehandles.doors[6 * 4 + (int)Direction::SOUTH] = LoadGraph("media\\wall\\6_door_s.png")) == -1) return FALSE;
//27
if ((g_imagehandles.walls[6 * 4 + (int)Direction::EAST] = LoadGraph("media\\wall\\6_e.png")) == -1) return FALSE;
if ((g_imagehandles.doors[6 * 4 + (int)Direction::EAST] = LoadGraph("media\\wall\\6_door_e.png")) == -1) return FALSE;
//中奥 7
//28
if ((g_imagehandles.walls[7 * 4 + (int)Direction::NORTH] = LoadGraph("media\\wall\\7_n.png")) == -1) return FALSE;
if ((g_imagehandles.doors[7 * 4 + (int)Direction::NORTH] = LoadGraph("media\\wall\\7_door_n.png")) == -1) return FALSE;
//29
if ((g_imagehandles.walls[7 * 4 + (int)Direction::WEST] = LoadGraph("media\\wall\\7_w.png")) == -1) return FALSE;
if ((g_imagehandles.doors[7 * 4 + (int)Direction::WEST] = LoadGraph("media\\wall\\7_door_w.png")) == -1) return FALSE;
//30
if ((g_imagehandles.walls[7 * 4 + (int)Direction::SOUTH] = LoadGraph("media\\wall\\7_s.png")) == -1) return FALSE;
if ((g_imagehandles.doors[7 * 4 + (int)Direction::SOUTH] = LoadGraph("media\\wall\\7_door_s.png")) == -1) return FALSE;
//31
if ((g_imagehandles.walls[7 * 4 + (int)Direction::EAST] = LoadGraph("media\\wall\\7_e.png")) == -1) return FALSE;
if ((g_imagehandles.doors[7 * 4 + (int)Direction::EAST] = LoadGraph("media\\wall\\7_door_e.png")) == -1) return FALSE;
//右奥 8
//32
if ((g_imagehandles.walls[8 * 4 + (int)Direction::NORTH] = LoadGraph("media\\wall\\8_n.png")) == -1) return FALSE;
if ((g_imagehandles.doors[8 * 4 + (int)Direction::NORTH] = LoadGraph("media\\wall\\8_door_n.png")) == -1) return FALSE;
//33
if ((g_imagehandles.walls[8 * 4 + (int)Direction::WEST] = LoadGraph("media\\wall\\8_w.png")) == -1) return FALSE;
if ((g_imagehandles.doors[8 * 4 + (int)Direction::WEST] = LoadGraph("media\\wall\\8_door_w.png")) == -1) return FALSE;
//34
if ((g_imagehandles.walls[8 * 4 + (int)Direction::SOUTH] = LoadGraph("media\\wall\\8_s.png")) == -1) return FALSE;
if ((g_imagehandles.doors[8 * 4 + (int)Direction::SOUTH] = LoadGraph("media\\wall\\8_door_s.png")) == -1) return FALSE;
//左最奥 9
//38
if ((g_imagehandles.walls[9 * 4 + (int)Direction::SOUTH] = LoadGraph("media\\wall\\9_s.png")) == -1) return FALSE;
if ((g_imagehandles.doors[9 * 4 + (int)Direction::SOUTH] = LoadGraph("media\\wall\\9_door_s.png")) == -1) return FALSE;
//39
if ((g_imagehandles.walls[9 * 4 + (int)Direction::EAST] = LoadGraph("media\\wall\\9_e.png")) == -1) return FALSE;
if ((g_imagehandles.doors[9 * 4 + (int)Direction::EAST] = LoadGraph("media\\wall\\9_door_e.png")) == -1) return FALSE;
//中最奥 10
//41
if ((g_imagehandles.walls[10 * 4 + (int)Direction::WEST] = LoadGraph("media\\wall\\10_w.png")) == -1) return FALSE;
if ((g_imagehandles.doors[10 * 4 + (int)Direction::WEST] = LoadGraph("media\\wall\\10_door_w.png")) == -1) return FALSE;
//42
if ((g_imagehandles.walls[10 * 4 + (int)Direction::SOUTH] = LoadGraph("media\\wall\\10_s.png")) == -1) return FALSE;
if ((g_imagehandles.doors[10 * 4 + (int)Direction::SOUTH] = LoadGraph("media\\wall\\10_door_s.png")) == -1) return FALSE;
//43
if ((g_imagehandles.walls[10 * 4 + (int)Direction::EAST] = LoadGraph("media\\wall\\10_e.png")) == -1) return FALSE;
if ((g_imagehandles.doors[10 * 4 + (int)Direction::EAST] = LoadGraph("media\\wall\\10_door_e.png")) == -1) return FALSE;
//右最奥 11
//45
if ((g_imagehandles.walls[11 * 4 + (int)Direction::WEST] = LoadGraph("media\\wall\\11_w.png")) == -1) return FALSE;
if ((g_imagehandles.doors[11 * 4 + (int)Direction::WEST] = LoadGraph("media\\wall\\11_door_w.png")) == -1) return FALSE;
//46
if ((g_imagehandles.walls[11 * 4 + (int)Direction::SOUTH] = LoadGraph("media\\wall\\11_s.png")) == -1) return FALSE;
if ((g_imagehandles.doors[11 * 4 + (int)Direction::SOUTH] = LoadGraph("media\\wall\\11_door_s.png")) == -1) return FALSE;
//床
if ((g_imagehandles.floor = LoadGraph("media\\wall\\floor_2.png")) == -1) return FALSE;
if ((g_imagehandles.dark[0] = LoadGraph("media\\wall\\0_d_n.png")) == -1) return FALSE;
if ((g_imagehandles.dark[1] = LoadGraph("media\\wall\\1_d_n.png")) == -1) return FALSE;
if ((g_imagehandles.dark[2] = LoadGraph("media\\wall\\4_d_n.png")) == -1) return FALSE;
if ((g_imagehandles.dark[3] = LoadGraph("media\\wall\\7_d_n.png")) == -1) return FALSE;
//ウィンドウ
if ((g_imagehandles.window = LoadGraph("media\\wall\\mess_m.png")) == -1) return FALSE;
if ((g_imagehandles.window_l = LoadGraph("media\\wall\\mess_l.png")) == -1) return FALSE;
if ((g_imagehandles.parson = LoadGraph("media\\parson\\parson.png")) == -1) return FALSE;
return TRUE;
}
###ゲーム全体の処理のヘッダー
#ifndef __MAIN_H__
#define __MAIN_H__
#include <DxLib.h>
#include "loading.h"
#include "gamemap.h"
#include "floor.h"
extern int g_lasttime;
extern float g_frametime;
extern int g_timerstart;
enum class GameState {
GAME_TITLE,
GAME_MAIN,
GAME_CLEAR,
GAME_OVER
};
extern GameState gamestate;
extern BOOL g_akey_prev;
extern int g_middlefont;
extern int g_smallfont;
extern int g_largefont;
void DrawGameTitle();
void DrawGameMain();
void DrawGameClear();
void DrawGameOver();
BOOL IsAkeyTrigger(int key);
#endif
###ゲーム全体の処理の中身
#include "main.h"
GameState gamestate = GameState::GAME_TITLE;
int g_lasttime = 0;
float g_frametime = 0;
int g_timerstart;
BOOL g_akey_prev;
int g_middlefont;
int g_largefont;
int g_smallfont;
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hp, _In_ LPSTR lpC, _In_ int nC) {
ChangeWindowMode(TRUE);
SetGraphMode(1400, 600, 32);
if (DxLib_Init() == -1) {
return -1;
}
if (LoadGameImage() == FALSE) return -1;
g_middlefont = CreateFontToHandle("メイリオ", 42, -1, DX_FONTTYPE_ANTIALIASING);
g_largefont = CreateFontToHandle("メイリオ", 90, -1, DX_FONTTYPE_ANTIALIASING);
g_smallfont = CreateFontToHandle("メイリオ", 17, -1, DX_FONTTYPE_ANTIALIASING);
SetDrawScreen(DX_SCREEN_BACK);
g_lasttime = GetNowCount() & INT_MAX;
while (ProcessMessage() == 0 && CheckHitKey(KEY_INPUT_ESCAPE) == 0) {
int currenttime = GetNowCount();
g_frametime = (float)(currenttime - g_lasttime) / 1000.f;
ClearDrawScreen();
switch (gamestate)
{
case GameState::GAME_TITLE:
DrawGameTitle();
break;
case GameState::GAME_MAIN:
DrawGameMain();
break;
case GameState::GAME_CLEAR:
DrawGameClear();
break;
case GameState::GAME_OVER:
DrawGameOver();
break;
default:
break;
}
ScreenFlip();
}
WaitKey();
DxLib_End();
return 0;
}
void DrawGameTitle() {
DrawGraph(0, 45, g_imagehandles.window, TRUE);
DrawStringToHandle(CHIPSIZE * 10, CHIPSIZE * 10, "スーパー3Dダンジョン:例の迷宮", GetColor(255, 255, 255), g_smallfont);
DrawStringToHandle(370, 480, "Zキーでスタート", GetColor(255, 255, 255), g_middlefont);
DrawStringToHandle(370, 528, "カーソルキーで左右に移動", GetColor(255, 255, 255), g_middlefont);
DrawStringToHandle(CHIPSIZE*4, CHIPSIZE * 25, "やったるでい!", GetColor(255, 255, 255), g_smallfont);
DrawGraph(CHIPSIZE, CHIPSIZE * 25, g_imagehandles.parson, TRUE);
int key = GetJoypadInputState(DX_INPUT_KEY_PAD1);
if (IsAkeyTrigger(key)) {
gamestate = GameState::GAME_MAIN;
InitMap();
InitPlayer();
}
}
void DrawGameMain() {
GameMain();
}
void DrawGameClear() {
//このゲームにクリアはない
}
void DrawGameOver() {
//このゲームにゲームオーバーはない
}
BOOL IsAkeyTrigger(int key) {
if (key & PAD_INPUT_A) {
if (g_akey_prev == FALSE) {
g_akey_prev = TRUE;
return TRUE;
}
}
else {
g_akey_prev = FALSE;
}
return FALSE;
}
###マップ用のデータのヘッダー
#ifndef __TRAP_H__
#define __TRAP_H__
typedef struct {
int floor, x, y;
char mess[256];
}Events;
extern int g_door[][4];
extern int g_wall[][4];
extern int stair[][4];
extern int events[][4];
extern int dark[][3];
extern int heal[][3];
extern int warp[][5];
extern int dark[][3];
extern Events new_event[];
extern int warp_len;
extern int dark_len;
extern int stair_len;
extern int event_len;
extern int heal_len;
#endif
###マップ用のデータを書くファイル
#include "floor.h"
//
//int stair[][4]{
// {1, 1, 0, 19},
// { 1,0,0,9 }
//};
int g_wall[][4] = {
//上、左、下、右
{0,0,0,0},//0 無、無、無、無
{1,1,1,1},//1 上、左、下、右
{1,0,0,0},//2 上、無、無、無
{1,1,0,0},//3 上、左、無、無
{1,0,1,0},//4 上、無、下、無
{1,0,0,1},//5 上、無、無、右
{1,1,1,0},//6 上、左、下、無
{1,0,1,1},//7 上、無、下、右
{1,1,0,1},//8 上、左、無、右
{0,1,0,0},//9 無、左、無、無
{0,1,1,0},//: 無、左、下、無
{0,1,0,1},//; 無、左、無、右
{0,1,1,1},//< 無、下、左、右
{0,0,1,0},//= 無、無、下、無
{0,0,1,1},//> 無、無、下、右
{0,0,0,1},//? 無、無、無、右
};
int g_door[][4] = {
{0,0,0,0},//0 無、無、無、無
{1,1,1,1},//1 上、左、下、右
{1,0,0,0},//2 上、無、無、無
{1,1,0,0},//3 上、左、無、無
{1,0,1,0},//4 上、無、下、無
{1,0,0,1},//5 上、無、無、右
{1,1,1,0},//6 上、左、下、無
{1,0,1,1},//7 上、無、下、右
{1,1,0,1},//8 上、左、無、右
{0,1,0,0},//9 無、左、無、無
{0,1,1,0},//: 無、左、下、無
{0,1,0,1},//; 無、左、無、右
{0,1,1,1},//< 無、左、下、右
{0,0,1,0},//= 無、無、下、無
{0,0,1,1},//> 無、無、下、右
{0,0,0,1},//? 無、無、無、右
};
Events new_event[5]{
{1,9,7,"壁に貼ってある大きな看板が次のように告げている。\n***回廊の終端を超えようとしています 引き返しなさい! ***"},
{1,13,14,"フードを被った人間型の大きな彫像が見える。\nフードの穴の中からはキン色の光が漏れていて、\n彫像にはさまざまなかたちの宝石が散りばめられている。\n彫像の前には祭壇があり、新しい香が焚かれている。"},
{1,13,16,"部屋の中には、頭がネコで、からだがニワトリの異様なけだものの彫像\nがある。彫像はブロンズで、台座はオニキスでできている。\n飾り台の上には不自然な傷痕がある。"},
{1,13,1,"部屋の中には、ツノと長いキバをもつ牡豚のギンの像がある。\n彫像の横の壁には、通りすがりのエルフが、書き残していったらしい、\n消えかけたイタズラ書きがある。ほとんど読めないが、どうやら\n死霊や悪魔がいるとの警告らしい。"},
{1,9,0,"この部屋からは、何か妖しげな明かりが発せられている。\n中央で長い法衣を着た小柄な男が冒険者たちをふりかえり叫んだ。\n“異邦人たちよ、去りたまえ!”彼は手を振りはじめ、そして、念じた。\n“マピロ マハマ ディロマト”"}
};
int stair[][4] = {
{1,1,0,19},
{1,0,0,9}
};
int events[][4] = {
{1,0,9,7},
};
int heal[][3] = {
{1,0,19}
};
int warp[][5] = {
//階数、スタートx,スタートy,エンドx,エンドy
{1,5,10,15,15},
{1,13,15,15,15},
{1,9,0,0,19},
};
int dark[][3] = {
//階数,x,y
{1,9,6},
{1,9,5},
{1,9,4},
{1,9,3},
{1,9,2},
{1,9,1},
{1,10,6},
{1,10,5},
{1,10,4},
{1,10,3},
{1,10,2},
{1,10,1},
{1,10,0},
{1,9,19},
{1,9,18},
{1,9,17},
{1,9,16},
{1,9,15},
{1,9,14},
{1,9,13},
{1,9,12},
{1,10,19},
{1,10,18},
{1,10,17},
{1,10,16},
{1,10,15},
{1,10,14},
{1,10,13},
{1,10,12},
{1,11,19},
{1,12,19},
{1,13,19},
{1,14,19},
{1,15,19},
{1,16,19},
{1,17,19},
{1,18,19},
{1,19,19},
};
int warp_len = sizeof(warp) / sizeof(*warp);
int dark_len = sizeof(dark) / sizeof(*dark);
int stair_len = sizeof(stair) / sizeof(*stair);
int event_len = sizeof(new_event) / sizeof(*new_event);
int heal_len = sizeof(heal) / sizeof(*heal);
###ゲーム中のヘッダー
# ifndef __GAME_MAP_H__
#define __GAME_MAP_H__
#include <DxLib.h>
#include "main.h"
#define CHIPSIZE 20
#define MAP_WIDTH 100
#define MAP_HEIGHT 100
#define WALL_THICKNESS 3
#define MOVERATE 8
#define DEPTH 3
#define START_2DX 800
extern char g_mapdata[MAP_HEIGHT][MAP_WIDTH];
extern char g_doordata[MAP_HEIGHT][MAP_WIDTH];
extern char g_s_doordata[MAP_HEIGHT][MAP_WIDTH];
extern int g_margeMapData[MAP_HEIGHT][MAP_WIDTH][4];
extern int g_trapData[MAP_HEIGHT][MAP_WIDTH];
enum class PlayMode {
PGM_EVENT, PGM_EXPLORE
};
struct StageData {
int mapwidth, mapheight;
int stagenum;
PlayMode playmode;
};
extern StageData stageData;
struct PlayerData {
unsigned int x, y, movecounter, direction,max_hp,hp,deadtime;
BOOL deadflag;
};
extern PlayerData playerData;
void GameMain();
void InitMap();
void InitPlayer();
void Draw2DMap();
void Draw3DMap();
void DrawPlayer();
void CheckCurrent();
void ShowEvent();
void DeadEvent();
#endif
###実際のゲーム中の処理
#include "gamemap.h"
int g_mv[4][2] = {
//x,y
{0,-1},//NORTH
{-1,-0},//WEST
{0,1},//SOUTH
{1,0},//EAST
};
StageData stageData;
PlayerData playerData;
char g_mapdata[MAP_HEIGHT][MAP_WIDTH];
char g_doordata[MAP_HEIGHT][MAP_WIDTH];
char g_s_doordata[MAP_HEIGHT][MAP_WIDTH];
int g_margeMapData[MAP_HEIGHT][MAP_WIDTH][4];
int g_trapData[MAP_HEIGHT][MAP_WIDTH];
int stage_num = 1;
void GameMain() {
playerData.movecounter++;
playerData.movecounter %= MOVERATE;
if (stageData.playmode == PlayMode::PGM_EXPLORE) {
unsigned int w = 0;
int key = GetJoypadInputState(DX_INPUT_KEY_PAD1);
if (playerData.movecounter == 0) {
int hx = playerData.x;
int hy = playerData.y;
if (key & PAD_INPUT_LEFT) {
playerData.direction++;
playerData.direction = playerData.direction % 4;
}
if (key & PAD_INPUT_RIGHT) {
playerData.direction--;
playerData.direction = (playerData.direction + 4) % 4;
}
if (key & PAD_INPUT_DOWN) {
playerData.direction -= 2;
playerData.direction = (playerData.direction + 4) % 4;
}
if (key & PAD_INPUT_UP || key & PAD_INPUT_10) {
hx += g_mv[playerData.direction][0];
hy += g_mv[playerData.direction][1];
w = g_margeMapData[playerData.y][playerData.x][playerData.direction];
if (w == 2 || w == 3) {
if (key & PAD_INPUT_10) {
w = 0;
}
}
if (hx >= 0 || hx < stageData.mapwidth || hy >= 0 || hy < stageData.mapheight) {
if (w == 0) {
for (int i = 0; i < event_len; i++) {
if (new_event[i].floor == stage_num && new_event[i].x == hx && new_event[i].y == hy) {
stageData.playmode = PlayMode::PGM_EVENT;
}
}
playerData.x = hx;
playerData.y = hy;
if (hx >= stageData.mapwidth) {
playerData.x = 0;
}
if (hx < 0) {
playerData.x = stageData.mapwidth - 1;
}
if (hy >= stageData.mapheight) {
playerData.y = 0;
}
if (hy < 0) {
playerData.y = stageData.mapheight - 1;
}
if (playerData.hp > 0) {
playerData.hp--;
}
}
}
}
}
}
Draw3DMap();
Draw2DMap();
CheckCurrent();
DrawPlayer();
if (playerData.hp == 0) {
stageData.playmode = PlayMode::PGM_EVENT;
playerData.deadflag = TRUE;
playerData.deadtime++;
}
if (stageData.playmode == PlayMode::PGM_EVENT) {
if (playerData.deadflag == FALSE) {
ShowEvent();
}
else {
DeadEvent();
}
}
}
void ShowEvent() {
DrawGraph(0, 0, g_imagehandles.window_l, TRUE);
for (int i = 0; i < event_len; i++) {
if (new_event[i].floor == stage_num && new_event[i].x == playerData.x && new_event[i].y == playerData.y) {
DrawStringToHandle(CHIPSIZE * 5, CHIPSIZE * 2 ,new_event[i].mess , GetColor(255, 255, 255), g_smallfont);
}
}
int key = GetJoypadInputState(DX_INPUT_KEY_PAD1);
if (IsAkeyTrigger(key) == TRUE) {
stageData.playmode = PlayMode::PGM_EXPLORE;
}
}
void DeadEvent() {
DrawGraph(0, 0, g_imagehandles.window_l, TRUE);
if (playerData.deadtime < 3) {
playerData.x = 0;
playerData.y = 19;
playerData.hp = playerData.max_hp;
DrawStringToHandle(CHIPSIZE * 5, CHIPSIZE * 2, "迷宮に充満した毒ガスで気を失ったあなたは気がついたら入り口の階段\nを枕に倒れ込んでいた。\n一体誰がどうやってあなたを運んだのだろうか。\n\nいずれにしてもこのような幸運が今後も起こるとは限らないだろう。", GetColor(255, 255, 255), g_smallfont);
int key = GetJoypadInputState(DX_INPUT_KEY_PAD1);
if (IsAkeyTrigger(key) == TRUE) {
stageData.playmode = PlayMode::PGM_EXPLORE;
playerData.deadflag = FALSE;
}
}
else {
DrawStringToHandle(CHIPSIZE * 5, CHIPSIZE * 2, "やはり何度も幸運は起きることはなかったようだ。\nゲームオーバー", GetColor(255, 255, 255), g_smallfont);
}
}
void DrawPlayer() {
DrawGraph(CHIPSIZE, CHIPSIZE * 25, g_imagehandles.parson, TRUE);
int start_hp_x = CHIPSIZE * 5;
int max_hp_x = CHIPSIZE * 5 + playerData.max_hp;
int current_hp_x;
if (playerData.hp > 0) {
current_hp_x = CHIPSIZE * 5 + playerData.hp;
}
else {
current_hp_x = start_hp_x;
}
DrawBox(start_hp_x, CHIPSIZE * 26, max_hp_x, CHIPSIZE * 26 + CHIPSIZE, GetColor(255, 0, 0), TRUE);
DrawBox(start_hp_x, CHIPSIZE * 26, current_hp_x, CHIPSIZE * 26 + CHIPSIZE, GetColor(0, 0, 255), TRUE);
}
int g_view[4][3][2] = {
//視点からのマップの横の座標
// 左[0],中[1],右[2]
{{-1,0},{0,0},{1,0}},//NORTH
{{0,1},{0,0},{0,-1}},//WEST
{ {1,0},{0,0},{-1,0}},//SOUTH
{{0,-1},{0,0},{0,1}},//EAST
};
void Draw3DMap() {
DrawGraph(0, 0, g_imagehandles.floor, FALSE);
int dir = playerData.direction;
for (int y = 3; y >= 0; y--) {
for (int x = 2; x >= 0; x--) {
int sortx = (x + 1) % 3;//0,2,1
int view_x = playerData.x + g_view[dir][sortx][0] + g_mv[dir][0] * y;
int view_y = playerData.y + g_view[dir][sortx][1] + g_mv[dir][1] * y;
if (view_x < 0){
view_x = stageData.mapwidth -view_x;
}
if (view_x >= stageData.mapwidth) {
view_x = 0 + view_x -stageData.mapwidth;
}
if (view_y < 0) {
view_y = stageData.mapheight - view_y;
}
if (view_y >= stageData.mapheight) {
view_y = 0 + view_y - stageData.mapheight;
}
int min_block_num = (y * 3 + sortx) * 4;
int max_block_num = min_block_num + 3;
for (int i = 0; i < (int)Direction::DIRECTION_MAX; i++) {
int w = g_margeMapData[view_y][view_x][i];
if (w > 0) {
int iw = min_block_num + i;
switch (dir) {
case 0:
break;
case 1:
iw--;
if (iw < min_block_num) {
iw = max_block_num;
}
break;
case 3:
iw++;
if (iw > max_block_num) {
iw = min_block_num;
}
break;
case 2:
iw += 2;
if (iw > max_block_num) {
iw = min_block_num + i - dir;
}
break;
}
if (w == 2 && g_imagehandles.doors[iw] > -1) {
DrawGraph(0, 0, g_imagehandles.doors[iw], TRUE);
}
else if (g_imagehandles.walls[iw] > -1 ) {
DrawGraph(0, 0, g_imagehandles.walls[iw], TRUE);
}
}
}
//暗闇
for (int i = 0; i < dark_len; i++) {
if (dark[i][0] == stage_num && dark[i][1] == view_x && dark[i][2] == view_y && sortx == 1) {
DrawGraph(0, 0, g_imagehandles.dark[y], TRUE);
if (dark[i][1] == playerData.x && dark[i][2] == playerData.y) {
DrawGraph(0, 45, g_imagehandles.window, TRUE);
DrawStringToHandle(CHIPSIZE * 16, CHIPSIZE * 10, "まっくらやみだ", GetColor(255, 255, 255), g_smallfont);
}
}
}
}
}
}
void CheckCurrent() {
////ワープ
if (stageData.playmode == PlayMode::PGM_EXPLORE) {
for (int i = 0; i < warp_len; i++) {
if (warp[i][0] == stage_num && warp[i][1] == playerData.x && warp[i][2] == playerData.y) {
playerData.x = warp[i][3];
playerData.y = warp[i][4];
}
}
}
//階段
for (int i = 0; i < stair_len; i++) {
if (stair[i][0] == stage_num && stair[i][2] == playerData.x && stair[i][3] == playerData.y) {
DrawGraph(0, 45, g_imagehandles.window, TRUE);
if (stair[i][1] == 0) {
DrawStringToHandle(CHIPSIZE * 12, CHIPSIZE * 10, "したにおりるかいだんがあります。", GetColor(255, 255, 255), g_smallfont);
}
else {
DrawStringToHandle(CHIPSIZE * 12, CHIPSIZE * 10, "うえにのぼるかいだんがあります。", GetColor(255, 255, 255), g_smallfont);
}
}
}
for (int i = 0; i < heal_len; i++) {
if (heal[i][0] == stage_num && heal[i][1] == playerData.x && heal[i][2] == playerData.y) {
playerData.hp = playerData.max_hp;
}
}
}
void Draw2DMap() {
for (int y = 0; y < stageData.mapheight; y++) {
for (int x = 0; x < stageData.mapwidth; x++) {
for (int i = 0; i < (int)Direction::DIRECTION_MAX; i++) {
if (g_margeMapData[y][x][i] >= 1) {
unsigned int Cr = 0;
switch (g_margeMapData[y][x][i]) {
case 1:
Cr = GetColor(255, 0, 0);
break;
case 2:
Cr = GetColor(0, 200, 0);
break;
case 3:
Cr = GetColor(255, 255, 0);
break;
}
int wall_thickness;
switch (i) {
case 0:
case 1:
wall_thickness = WALL_THICKNESS;
break;
case 2:
case 3:
wall_thickness = WALL_THICKNESS * -1;
}
if (i % 2 == 0) {//横線
DrawBox(x * CHIPSIZE + START_2DX, y * CHIPSIZE + (CHIPSIZE * i / 2), x * CHIPSIZE + CHIPSIZE + START_2DX, y * CHIPSIZE + (CHIPSIZE * i / 2) + wall_thickness, Cr, TRUE);
}
else {//縦線
DrawBox(x * CHIPSIZE + (CHIPSIZE * (i - 1) / 2) + START_2DX , y * CHIPSIZE, x * CHIPSIZE + (CHIPSIZE * (i - 1) / 2) + wall_thickness + START_2DX, y * CHIPSIZE + CHIPSIZE, Cr, TRUE);
}
}
}
}
}
//player自身の描画
DrawBox(
playerData.x * CHIPSIZE + 3 + START_2DX,
playerData.y * CHIPSIZE + 3,
playerData.x * CHIPSIZE + CHIPSIZE -3 + START_2DX,
playerData.y * CHIPSIZE + CHIPSIZE - 3,
GetColor(255, 255, 255),
TRUE);
//player方向の描画
DrawBox(
(playerData.x + g_mv[playerData.direction][0]) * CHIPSIZE + 8 + START_2DX,
(playerData.y + g_mv[playerData.direction][1]) * CHIPSIZE + 8,
(playerData.x + g_mv[playerData.direction][0]) * CHIPSIZE + CHIPSIZE - 8 + START_2DX,
(playerData.y + g_mv[playerData.direction][1]) * CHIPSIZE + CHIPSIZE - 8,
GetColor(0, 210, 255),
TRUE);
}
void InitPlayer() {
playerData.x = 0;
playerData.y = 19;
playerData.max_hp = GetRand(20) + 9;
playerData.hp = playerData.max_hp;
playerData.direction = (int)Direction::NORTH;
playerData.deadflag = FALSE;
playerData.deadtime = 0;
stageData.playmode = PlayMode::PGM_EXPLORE;
}
void InitMap() {
//マップ基本データ
char buf[256];
sprintf_s(buf, 256, "media\\stage%d.txt", stage_num);
int fh = FileRead_open(buf);
int map_y = 0;
while (FileRead_eof(fh) == 0) {
FileRead_gets(g_mapdata[map_y], MAP_WIDTH, fh);
map_y++;
}
FileRead_close(fh);
stageData.mapwidth = (int)strlen(g_mapdata[0]);
stageData.mapheight = map_y;
//マップドアデータ
sprintf_s(buf, 256, "media\\d_stage%d.txt", stage_num);
fh = FileRead_open(buf);
map_y = 0;
while (FileRead_eof(fh) == 0) {
FileRead_gets(g_doordata[map_y], MAP_WIDTH, fh);
map_y++;
}
FileRead_close(fh);
//マップ隠しドアデータ
sprintf_s(buf, 256, "media\\sd_stage%d.txt", stage_num);
fh = FileRead_open(buf);
map_y = 0;
while (FileRead_eof(fh) == 0) {
FileRead_gets(g_s_doordata[map_y], MAP_WIDTH, fh);
map_y++;
}
FileRead_close(fh);
//それぞれのデータを1つの変数 g_margeMapData にまとめる
for (int y = 0; y < stageData.mapheight; y++) {
for (int x = 0; x < stageData.mapwidth; x++) {
int wall = g_mapdata[y][x] - '0';
int door = g_doordata[y][x] - '0';
int s_door = g_s_doordata[y][x] - '0';
for (int i = 0; i < 4; i++) {
int marge_wall = g_wall[wall][i] + g_door[door][i] + g_door[s_door][i];
g_margeMapData[y][x][i] = marge_wall;
}
}
}
}
##感想
いやーゲーム作りって難しいですね!
でも実際に動くとすごい嬉しかったです!