3Dダンジョンを表示するクラスのコードを置いておく場所

  • 20091223:更新があったらこのエントリに追加・修正していきたいと思います。
  • d3dung.h
#ifndef _D3DUNG_H_INCLUDED_
#define _D3DUNG_H_INCLUDED_

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "DxLib.h"
/*#include "SFMT.h"*/

#define D3DMAPH 64
#define D3DMAPW	64

#define N_TILETYPES	4

enum eD3DIRs {
	D3DIR_L,
	D3DIR_U,
	D3DIR_R,
	D3DIR_D,
};

enum eDUNGWALLS {
	DWA_NONE,
	DWA_STD,//1
	DWA_STONE,
	DWA_DOOR_O,
	DWA_DOOR_C,//4
};

typedef struct tag_masu_type {
	int is_wall;
} masu_t;

class D3DungDrawer
{
private:
	masu_t map[D3DMAPH][D3DMAPW];
	int dir, posx, posy;
	int maph, mapw;
	int loop_flag;
	int hpictbase;
	int hpict[N_TILETYPES][15];
	int hpictdungbase;
	int iswtmp[3][3];
	int drawing_size;
	int drawingx, drawingy;
	int bmp_wid, bmp_hgt;
	int colorkey[3];
	int loadposx[N_TILETYPES], loadposy[N_TILETYPES];
	int loadposbx, loadposby;
	void D3DungDrawer::DrawWallPict(int i, int j);
public:
	char pictfilename[256];
	D3DungDrawer()
	{
		loop_flag = 1;
		maph = 20; mapw = 20;
		hpictbase = -1;
		for(int i = 0;i<N_TILETYPES;i++)
		{
			for(int j = 0;j<15;j++)
			{
				hpict[i][j] = -1;
			}
		}
		strcpy(pictfilename, "3DDung4832.bmp");
		hpictdungbase = -1;
		posx = 0;
		posy = 0;
		dir = D3DIR_D;
		drawing_size = 3;
		bmp_wid = 48;
		bmp_hgt = 32;
		loadposx[0] = 144;
		loadposy[0] = 16;
		loadposbx = 16;
		loadposby = 112;
		colorkey[0] = 71;
		colorkey[1] = 108;
		colorkey[2] = 108;
		memset(map,0,sizeof(map));
	}
	void SetD3DMapSize(int h, int w)
	{
		maph = h; mapw = w;
	}
	void LoadSettingFIle(char *fname);
	void DrawDungeon(int dx, int dy);
	void D3DungDrawer::DrawDungeon3D(int dx, int dy);
	
	//成功時 1 ,失敗時 0
	int D3DungDrawer::LoadBitmap(char *filename);
	void D3DungDrawer::MakeMazeByTextFile(char *fname);
	void D3DungDrawer::PosTurning(int xxx);
	void D3DungDrawer::PosSusumu(int flag);
};

#endif /*__D3DUNG_H_INCLUDED__*/
  • d3dung.cpp
#include "d3dung.h"

/*                     L, U, R, D*/
static int adxx[4] = {-1, 0, 1, 0};
static int adyy[4] = { 0,-1, 0, 1};

int D3DungDrawer::LoadBitmap(char *filename)
{
	const int bmpw = bmp_wid, bmph = bmp_hgt, bmppad = bmp_wid;
	SetTransColor(colorkey[0], colorkey[1], colorkey[2]);
	hpictbase = LoadGraph(filename);
	if (hpictbase == -1)
	{
		return 0;
	}

	hpictdungbase = DerivationGraph(loadposbx, loadposby, bmpw, bmph, hpictbase);
	for(int i=0; i<4; i++)
	{
		for(int j=0; j<9; j++)
		{
			hpict[i][j] = DerivationGraph(
				loadposx[i] + (j%3)*bmppad,
				loadposy[i] + (j/3)*bmph, bmpw, bmph, hpictbase);
		}
	}
	return 1;
}

void D3DungDrawer::DrawWallPict(int i, int j)
{
	int wtp[3][3] = {
		{6,7,8},
		{3,4,5},
		{0,1,2},
	};
	if (iswtmp[i][j])
	{
		int pidx, picthandle;
		pidx = wtp[i][j];
		switch(iswtmp[i][j])
		{
		case DWA_STONE:
			picthandle = hpict[1][pidx];
			break;
		default:
			picthandle = hpict[0][pidx];
			break;
		}
		if (picthandle == -1) return;
		if (pidx == 7) //hack -- do not draw youare in wall
		{
			;
		}
		else
		{
			DrawExtendGraph(drawingx, drawingy, 
				drawingx + bmp_wid*drawing_size, drawingy + bmp_hgt*drawing_size,
				picthandle, TRUE);
		}
		// uwagaki hack
		switch(iswtmp[i][j])
		{
		case DWA_DOOR_O:
			picthandle = hpict[2][pidx];
			break;
		case DWA_DOOR_C:
			picthandle = hpict[3][pidx];
			break;
		default:
			picthandle = -1;
			break;
		}
		if (picthandle == -1) return;
		DrawExtendGraph(drawingx, drawingy, 
			drawingx + bmp_wid*drawing_size, drawingy + bmp_hgt*drawing_size,
			picthandle, TRUE);
	}
}

void D3DungDrawer::DrawDungeon3D(int dx, int dy)
{
	int ddir;
	
	this->drawingx = dx;
	this->drawingy = dy;

	ddir = this->dir;
	memset(iswtmp,0,sizeof(iswtmp));
	DrawBox(dx-1,dy-1,
		dx+bmp_wid*drawing_size+1,dy+bmp_hgt*drawing_size+1,
		GetColor(40,40,255), FALSE);
	for(int i=0;i<3;i++)
	{
		for(int j=-1; j<2;j++)
		{
			int wset = 0;
			int dx = posx, dy = posy;
			switch(ddir)
			{
			case D3DIR_L:
				dx += i*-1;
				dy += j*-1;
				break;
			case D3DIR_R:
				dx += i*1;
				dy += j*1;
				break;
			case D3DIR_U:
				dx += j*1;
				dy += i*-1;
				break;
			case D3DIR_D:
				dx += j*-1;
				dy += i*1;
				break;
			}
			if (dy < 0 || dy >= maph ||
				dx < 0 || dx >= mapw)
			{
				if (!loop_flag)
				{
					wset = 1;
				}
				else
				{
					if (dx >= mapw)
					{
						dx = dx % mapw;
					}
					if (dy >= maph)
					{
						dy = dy % maph;
					}
					while (dx < 0) {dx += mapw;}
					while (dy < 0) {dy += maph;}
					
					if (map[dy][dx].is_wall)
					{
						wset = map[dy][dx].is_wall;
					}
				}
			}
			else if (map[dy][dx].is_wall)
			{
				wset = map[dy][dx].is_wall;
			}
			iswtmp[i][j+1] = wset;
		}
	}

	DrawExtendGraph(dx, dy,
			drawingx + bmp_wid*drawing_size, drawingy + bmp_hgt*drawing_size,
			hpictdungbase, TRUE);
	DrawWallPict(2, 0);
	DrawWallPict(2, 2);
	DrawWallPict(2, 1);
	DrawWallPict(1, 0);
	DrawWallPict(1, 2);
	DrawWallPict(1, 1);
	DrawWallPict(0, 0);
	DrawWallPict(0, 2);
	DrawWallPict(0, 1);
}

void D3DungDrawer::DrawDungeon(int dx, int dy)
{
	int i, j;

	DrawDungeon3D(dx, dy);

	SetDrawBlendMode(DX_BLENDMODE_ALPHA, 128);
	for(i=0;i<maph;i++)
	{
		for(j=0;j<mapw;j++)
		{
			char buf[2];
			char ch = '.';
			switch(map[i][j].is_wall)
			{
			case DWA_STD:
			case DWA_STONE:
				ch = '#';
				break;
			case DWA_DOOR_C:
				ch = '+';
				break;
			case DWA_DOOR_O:
				ch = '`';
				break;
			}
			if (posx == j && posy == i) ch = '@';
			buf[0] = ch;
			buf[1] = 0;
			DrawString(dx + 320 + j*9, dy + i*16, buf, GetColor(255,255,255));
		}
	}
	SetDrawBlendMode(DX_BLENDMODE_NOBLEND, 0);

	{
		char buf[100];
		char *dirch[4] = {"←","↑","→","↓"};
		// char *dirch_e[4] = {"<","^",">","v"};
		sprintf(buf, "dir:%s x:%d y:%d", dirch[dir], posx, posy);
		DrawString(dx, dy + 200, buf, GetColor(255,255,255));
		// debug info 
		sprintf(buf, "Loop:%d mapw:%d maph:%d", loop_flag, mapw, maph);
		DrawString(dx, dy + 200 + 16, buf, GetColor(255,255,255));
		sprintf(buf, "pic:%s loadposbx:%d loadposby:%d", pictfilename, loadposbx, loadposby);
		DrawString(dx, dy + 200 + 16*2, buf, GetColor(255,255,255));
	}
	DrawGraph(200,200,hpictbase, TRUE);
}

void D3DungDrawer::LoadSettingFIle(char *fname)
{
	char buf[1024];
	FILE *f;
	f = fopen(fname, "rt");
	if (f == 0) return;

	while(fgets(buf, 1024, f))
	{
		if (buf[0] == '#') continue;
		if (buf[0] == '\n' || buf[0] == '\r' || buf[0] == 0)  continue;
		
		if (buf[1] == ':')
		{
			switch(buf[0])
			{
			case 'F':
				{
					char *str = &buf[2], *tmp;
					tmp = str;
					while(1)
					{
						if (*tmp == 0) break;
						if (*tmp == '\n' || *tmp == '\r')
						{
							*tmp = 0;
							break;
						}
						tmp++;
					}
					strcpy(pictfilename, str);
				}
				break;
			case 'S':
				{
					char *str = &buf[2], *tok;
					tok = strtok(str, ":");
					bmp_wid = atoi(tok);
					tok = strtok(0, ":");
					bmp_hgt = atoi(tok);
				}
				break;
			case 'C':
				{
					char *str = &buf[2], *tok;
					tok = strtok(str, ":");
					colorkey[0] = atoi(tok);
					tok = strtok(0, ":");
					colorkey[1] = atoi(tok);
					tok = strtok(0, ":");
					colorkey[2] = atoi(tok);
				}
				break;
			case 'T':
				{
					char *str = &buf[2], *tok;
					tok = strtok(str, ":");
					if (*tok == 'B')
					{
						tok = strtok(0, ":");
						loadposbx = atoi(tok);
						tok = strtok(0, ":");
						loadposby = atoi(tok);
					}
					else
					{
						int tidx = atoi(tok);
						tok = strtok(0, ":");
						loadposx[tidx] = atoi(tok);
						tok = strtok(0, ":");
						loadposy[tidx] = atoi(tok);
					}
				}
				break;
			}
		}
	}
	fclose(f);
}

void D3DungDrawer::MakeMazeByTextFile(char *fname)
{
	FILE *f;
	int yy = 0;
	char buf[1024];
	f = fopen(fname, "rt");
	if (f == 0) return;

	//test
	maph = 10;
	mapw = 10;

	while(fgets(buf, 1024, f))
	{
		if (buf[0] == 'R') continue;
		
		if (buf[0] == 'L' && buf[1] == ':')
		{
			loop_flag = atoi(&buf[2]);
			continue;
		}
		else if (buf[0] == 'S' && buf[1] == ':')
		{
			char *str = &buf[2];
			char *tok;
			tok = strtok(str, ":");
			mapw = atoi(tok);
    		tok = strtok(0, ":");
			maph = atoi(tok);
			continue;
		}

		for(int j=0;j<mapw;j++)
		{
			if (!buf[j] || buf[j] == '\n' || buf[j] == '\r')
			{
				break;
			}
			switch(buf[j])
			{
			case '#':
				map[yy][j].is_wall = DWA_STD;
				break;
			case '%':
				// stone wall
				map[yy][j].is_wall = DWA_STONE;
				break;
			case '+':
				// closed door
				map[yy][j].is_wall = DWA_DOOR_C;
				break;
			case 'o':
				// opened door
				map[yy][j].is_wall = DWA_DOOR_O;
				break;
			case '@':
				map[yy][j].is_wall = 0;
				posx = j;
				posy = yy;
				break;
			case '.':
			default:
				map[yy][j].is_wall = 0;
				break;
			}
		}
		yy++;
		if (yy >= maph) break;
	}
	fclose(f);
}

void D3DungDrawer::PosTurning(int xxx)
{
	// if opened door
	if (map[posy][posx].is_wall == DWA_DOOR_O)
	{
		if (xxx != 2) return;
	}
	dir += xxx;
	if (dir < 0)  dir = dir + 4;
	if (dir >= 4) dir = dir % 4;
}

/*"int flag" is to pass walls*/
void D3DungDrawer::PosSusumu(int flag)
{
	int nposx, nposy;
	nposx = posx + adxx[dir];
	nposy = posy + adyy[dir];
	if (!loop_flag)
	{
		if (nposx < 0) nposx = 0;
		if (nposy < 0) nposy = 0;
		if (nposx > mapw-1) nposx = mapw-1;
		if (nposy > maph-1) nposy = maph-1;
	}
	else
	{
		if (nposx < 0) nposx += mapw;
		if (nposy < 0) nposy += maph;
		if (nposx > mapw-1) nposx = nposx % mapw;
		if (nposy > maph-1) nposy = nposy % maph;
	}
	if (!flag && map[nposy][nposx].is_wall)
	{
		if (map[nposy][nposx].is_wall == DWA_DOOR_C)
		{
			map[nposy][nposx].is_wall = DWA_DOOR_O;
			return;
		}
		else if (map[nposy][nposx].is_wall == DWA_DOOR_O)
		{
			;// advance
		}
		else
		{
			return;
		}
	}

	posx = nposx;
	posy = nposy;
}

  • Game.cpp
    実際に動かすコードの一部です。
// getcommand()  : DXライブラリでgetch()のようなことをします。
keykode_type keykode[7] = {
	{CTRL_CODE_LEFT, 'h'},
	{CTRL_CODE_RIGHT, 'l'},
	{CTRL_CODE_DOWN, 'j'},
	{CTRL_CODE_UP, 'k'},
	{CTRL_CODE_ESC, ESCAPE},
	{CTRL_CODE_CR, '\n'},
	{0, 0},
};

char getcommand()
{
	char rval;

	ClearInputCharBuf();

	while(1)
	{
		int i;
		char ch = GetInputCharWait(TRUE);
		for(i=0;i<6;i++)
		{
			if (keykode[i].kode == 0) break;
			if (ch == keykode[i].kode)
			{
				ch = keykode[i].changeto;
				break;
			}
		}
		rval = ch;
		break;
	}
	return rval;
}


// インスタンス
D3DungDrawer dung;

...

	dung.LoadSettingFIle("0tiledef.txt");
	if (!dung.LoadBitmap(dung.pictfilename))
	{
		return;
	}
	dung.MakeMazeByTextFile("Maze.txt");
	
	while(1)
	{
		int cmd;
		
		clear_s();
		
		dung.DrawDungeon(10, 10);
		
		refresh_s();
		
		cmd = getcommand();
		switch(cmd)
		{
		case 'h':
			dung.PosTurning(-1);
			break;
		case 'l':
			dung.PosTurning( 1);
			break;
		case 'j':
			dung.PosTurning( 2);
			break;
		case 'k':
			dung.PosSusumu(0);
			break;
		case ESCAPE:
			return;
		}
	}