0. 코드 정리

맵을 랜더링하는 코드를 maze가 아닌 board로 관리한다.

 

1. 맵 만들기

(1-1) Board.cpp - 벽 생성

Binary Tree Maze Algorithm을 따라 size에 해당하는 맵을 만든다.

// Binary Tree 미로 생성 알고리즘
// - Mazes For Programmers
void Board::GenerateMap()
{
	for (int32 y = 0; y < _size; y++)
	{
		for (int32 x = 0; x < _size; x++)
		{
			if (x % 2 == 0 || y % 2 == 0) // 첫번째 가로줄과 세로줄은 벽으로 설정
				_tile[y][x] = TileType::WALL;
			else
				_tile[y][x] = TileType::EMPTY;
		}
	}

- x의 위치가 0이거나 y의 위치가 0이면 벽으로 만든다.

- 그리고 x가 짝수이거나 y가 짝수인 곳을 벽으로 만든다.

 

(1-2) 길 뚫기

// 랜덤으로 우측 혹은 아래로 길을 뚫는 작업
	for (int32 y = 0; y < _size; y++)
	{
		for (int32 x = 0; x < _size; x++)
		{
			if (x % 2 == 0 || y % 2 == 0)
				continue;
			if (y == _size - 2 && x == _size - 2) // 오른쪽 아래 모서리(끝)에 도달
				continue;

			if (y == _size - 2) // 아래 한계
			{
				_tile[y][x + 1] = TileType::EMPTY; // 오른쪽으로 길 뚫기
				continue;
			}

			if (x == _size - 2) // 우측 한계
			{
				_tile[y + 1][x] = TileType::EMPTY; // 아래로 길 뚫기
				continue;
			}

x축 상에서 타일은 0 ~ 24의 좌표를 가지고, y축 또한 0 ~ 24의 좌표를 가진다.

즉 x축에서 24번째 타일은 벽이다. 따라서 x축에서 이동가능한 우측 한계는 _size = 25 에서 2를 뺀 값이다.

y축도 동일하다.

x축 또는 y축이 한계에 도달할 때마다 아래로 혹은 오른쪽으로 길을 뚫게 지정한다.

 

위의 조건인 벽에 해당하지 않으면 랜덤값을 사용하여 길을 생성한다.

			const int32 randValue = ::rand() % 2; // 0 or 1
			if (randValue == 0) // 랜덤으로 길 생성
			{
				_tile[y][x + 1] = TileType::EMPTY;
			}
			else
			{
				_tile[y + 1][x] = TileType::EMPTY;
			}
		}
	}
}

0 또는 1의 값을 받아서 기댓값이 1/2인 확률로 오른쪽 또는 아래로 길을 생성하게 해준다.

 

(1-3) 벽 외부, 플레이어 색, 출구 색 설정

TileType Board::GetTileType(Pos pos)
{
	if (pos.x < 0 || pos.x >= _size)
		return TileType::NONE;

	if (pos.y < 0 || pos.y >= _size)
		return TileType::NONE;

	return _tile[pos.y][pos.x];
}

ConsoleColor Board::GetTileColor(Pos pos)
{
	if (_player && _player->GetPos() == pos)
		return ConsoleColor::YELLOW;

	if (GetExitPos() == pos)
		return ConsoleColor::BLUE;

	TileType tileType = GetTileType(pos);

	switch (tileType)
	{
	case TileType::EMPTY:
		return ConsoleColor::GREEN;
	case TileType::WALL:
		return ConsoleColor::RED;
	}

	return ConsoleColor::WHITE;
}

 

(2) 플레이어 생성

(2-1) Player.h

#pragma once

class Board;

class Player
{
public:
	void		Init(Board* board);
	void		Update(uint64 deltaTick);

	void		SetPos(Pos pos) { _pos = pos; }
	Pos			GetPos() { return _pos; }


private:
	Pos			_pos = {};
	int32		_dir = DIR_UP;
	Board*		_board = nullptr;
};

보드의 포인터를 받는 Init 함수와 프레임 당 업데이트를 수행하는 Update 멤버 함수를 선언한다.

SetPos 함수는 플레이어의 위치를 설정한다.

_dir은 pch.h에 정의된 enum DIR의 값을 사용한다. 

 

(2-2) Player.cpp

#include "pch.h"
#include "Player.h"
#include "Board.h"

void Player::Init(Board* board)
{
	_pos = board->GetEnterPos();
	_board = board;
}

void Player::Update(uint64 deltaTick)
{

}

플레이어의 시작 위치는 보드의 GetEnterPos 함수를 사용하여 받는다.

그리고 nullptr로 초기화된 _board 멤버 변수의 값을 Board 클래스의 포인터로 설정한다. 

 

(3) Maze.cpp

#include "pch.h"
#include <iostream>
#include "ConsoleHelper.h"
#include "Board.h"
#include "Player.h"

Board board;
Player player;

int main()
{
	::srand(static_cast<unsigned>(time(nullptr)));

	board.Init(25, &player);
	player.Init(&board);

srand를 사용하여 시드를 시간에 따른 변수로 받는다.

Board.Init(int32 size, Player* player)

이 때 Board 클래스의 Init 함수를 사용하여 크기와 player의 포인터로 초기화한다.

player도 board의 포인터를 받도록 초기화한다.

 

이후는 무한 루프를 통해서 프레임마다 플레이어(의 위치)를 업데이트 해주고, 보드를 렌더링하는 코드를 작성한다.

	uint64 lastTick = 0;
	while (true)
	{
#pragma region 프레임 관리
		const uint64 currentTick = ::GetTickCount64();
		const uint64 deltaTick = currentTick - lastTick;
		lastTick = currentTick;
#pragma endregion

		// 입력

		// 로직
		player.Update(deltaTick);

		// 렌더링
		board.Render();
	}
}