멘탈을 다시 고쳐서
꼭 구현해내기로 다짐했다!
Snake
클래스를 만듭니다. 이 클래스는 뱀의 상태와 이동, 음식 먹기, 자신의 몸에 부딪혔는지 확인 등의 기능을 담당합니다.FoodCreator
클래스를 만듭니다. 이 클래스는 맵의 크기 내에서 무작위 위치에 음식을 생성하는 역할을 합니다.Main
함수에서 게임을 제어하는 코드를 작성합니다. 이 코드는 뱀의 이동, 음식 먹기, 게임 오버 조건 확인 등을 주기적으로 수행합니다.Get Set을 이용했다.
처음에는 맵의 테두리를 제외한 부분은 빈칸(' ', 공백 두개)으로 설정하려 했는데
char 데이터 형식을 이용하니, 문자는 하나 밖에 등록하지 못했다.
최대한 맵의 가시성을 주고 싶었다.
IsBorderCell 메서드를 통해 맵을 그리는 메서드의 코드를 줄이고 가독성을 높였다.
애먹었던 점은 행렬의 개념이 2차원 공간의 x,y와 계속 대입해서 생각하다보니
순간 까먹었던게 제일 문제였다.
아쉬운점은 처음 Get Set을 써버리니 프로퍼티로 수정하려는데
작동이 안할까 두려워 시도하지 못했다.
꼭 추후에 프로퍼티로 수정해봐야겠다!
List와 Queue 중 어떤 컬렉션을 쓸지 고민했다.
Queue가 List보다 유동적으로 관리할 수 있고,
선입선출 구조니 머리가 움직일 때 마다 꼬리가 자동으로 따라온다 생각했다. (사실 맞음)
그러나 내 실력 부족인지 뱀의 위치가
초기
○●○○○
○●○○○
○●○○○
○○○○○
오른쪽 방향키 입력
○●○○○
○●○○○
○○●○○
○○○○○
그 후
○●○○○
○○●○○
○○●○○
○○○○○
이런 식으로 따라오고 방향의 회전이 불가능 했다.
결국 List 컬렉션을 이용해 구현을 했다.
또한
switch (_direction)
{
case Direction.UP:
_newSnakeHeadX--;
break;
case Direction.DOWN:
_newSnakeHeadX++;
break;
case Direction.LEFT:
_newSnakeHeadY--;
break;
case Direction.RIGHT:
_newSnakeHeadY++;
break;
}
이 부분에서 보통은 UP을 누르면, Y--가 맞는데,, 내 코드에서는 어찌된 영문인지 X-- 가 작동한다.
이게 정상인데 아직도 의문이다.
음식은 랜덤으로 생성하게 했다.
처음에는 정해진 맵의 배열로 범위를 지정했는데,
양이 너무 많아져서 확률로 구성했다.
if (_random.Next(0,100) < 5) // 5% < n, n으로 확률 조절 가능
Thread.Sleep(1000)
1000ms 즉 1s 동안 실행을 중지 시킨다.
속도 조절에서 사용하기도 한다!
namespace SnakeGame
{
class Program
{
static void Main(string[] args)
{
Map _m = new Map();
Food _f = new Food(_m);
Snake _s = new Snake(_m);
while (true)
{
if (Console.KeyAvailable)
{
ConsoleKeyInfo _keyInfo = Console.ReadKey();
_s.SnakeKey(_keyInfo.Key);
}
Console.Clear();
_s.Move();
_m.DrawMap();
_f.CreateFood();
//
Thread.Sleep(1000 / 144);
}
}
}
// 음식 클래스
public class Food
{
private Map _map;
private Random _random;
public Food(Map map)
{
_map = map;
_random = new Random();
}
public void CreateFood()
{
int _x, _y;
if (_random.Next(0,100) < 5) // 5% < n, n으로 확률 조절 가능
{
do
{
_x = _random.Next(1, _map._maxMapCol - 1);
_y = _random.Next(1, _map._maxMapRow - 1);
} while (_map.GetCell(_x, _y) != '○');
_map.SetCell(_x, _y, '★'); // 음식 생성
}
}
}
public class Snake
{
private Map _map;
private List<(int _col, int _row)> _snakeBody; // 뱀 바디 저장
private Direction _direction;
public Snake(Map map)
{
_map = map;
_snakeBody = new List<(int _col, int _row)>();
_direction = Direction.RIGHT;
int _snakeInitX = _map._maxMapCol / 2;
int _snakeInitY = _map._maxMapRow / 2;
// 초기 길이를 설정
for (int i = 0; i < 4; i++)
{
_snakeBody.Add((_snakeInitX - i, _snakeInitY));
_map.SetCell(_snakeInitX - i, _snakeInitY, '●');
}
}
public void SnakeKey(ConsoleKey _key)
{
switch (_key)
{
case ConsoleKey.UpArrow:
_direction = Direction.UP;
break;
case ConsoleKey.DownArrow:
_direction = Direction.DOWN;
break;
case ConsoleKey.LeftArrow:
_direction = Direction.LEFT;
break;
case ConsoleKey.RightArrow:
_direction = Direction.RIGHT;
break;
}
}
public void Move()
{
int _currentHeadX = _snakeBody[0]._col;
int _currentHeadY = _snakeBody[0]._row;
int _newSnakeHeadX = _currentHeadX;
int _newSnakeHeadY = _currentHeadY;
switch (_direction)
{
case Direction.UP:
_newSnakeHeadX--;
break;
case Direction.DOWN:
_newSnakeHeadX++;
break;
case Direction.LEFT:
_newSnakeHeadY--;
break;
case Direction.RIGHT:
_newSnakeHeadY++;
break;
}
// 머리 위치 리스트에 추가
if (_map.GetCell(_newSnakeHeadX, _newSnakeHeadY) == '★')
{
_snakeBody.Insert(0, (_newSnakeHeadX, _newSnakeHeadY)); // 뱀 머리 추가
_map.SetCell(_newSnakeHeadX, _newSnakeHeadY, '●'); // 새로운 머리 위치 설정
}
// 자신의 몸에 부딪힐 경우
else if (_map.GetCell(_newSnakeHeadX, _newSnakeHeadY) == '●')
{
Console.WriteLine("뱀이 벽에 닿았습니다! 게임 오버!");
Environment.Exit(0);
}
else
{
// 기존 꼬리 위치에 '○' 설정
int tailX = _snakeBody[_snakeBody.Count - 1]._col;
int tailY = _snakeBody[_snakeBody.Count - 1]._row;
_map.SetCell(tailX, tailY, '○'); // 꼬리 위치 비우기
_snakeBody.RemoveAt(_snakeBody.Count - 1); // 꼬리 제거
_snakeBody.Insert(0, (_newSnakeHeadX, _newSnakeHeadY)); // 뱀 머리 추가
_map.SetCell(_newSnakeHeadX, _newSnakeHeadY, '●'); // 새로운 머리 위치 설정
}
}
}
// 게임 맵
public class Map
{
public int _maxMapCol = 15;
public int _maxMapRow = 15;
private char[,] _map;
public Map()
{
// 행열 순서
_map = new char[_maxMapCol, _maxMapRow];
InitMap();
}
// 프로퍼티를 사용하면 더 좋을듯!
// 셀에 값 설정
public void SetCell(int x, int y, char value)
{
_map[x, y] = value;
}
// 셀의 값 반환
public char GetCell(int x, int y)
{
if (x >= 0 && x < _maxMapCol && y >= 0 && y < _maxMapRow)
{
return _map[x, y];
}
else
{
Console.WriteLine("뱀이 자신의 몸에 부딪혔습니다! 게임 오버!");
Environment.Exit(0);
// 화면 바깥으로 나갔을 때 예외 처리
throw new IndexOutOfRangeException();
}
}
// 맵 초기화 메서드
public void InitMap()
{
for (int x = 0; x < _maxMapCol; x++)
{
for (int y = 0; y < _maxMapRow; y++)
{
_map[x, y] = '○';
}
}
}
// 맵 그리는 메서드
public void DrawMap()
{
for (int x = 0; x < _maxMapCol; x++)
{
for (int y = 0; y < _maxMapRow; y++)
{
if (IsBorderCell(x, y))
{
Console.Write("■");
}
else
{
Console.Write(_map[x, y]);
}
}
Console.WriteLine();
}
}
// 경계 판별
private bool IsBorderCell(int x, int y)
{
return x == 0 || x == _maxMapCol - 1 || y == 0 || y == _maxMapRow - 1;
}
}
public enum Direction
{
UP,
DOWN,
RIGHT,
LEFT
}
}
결국 구현은 완성했다.
어제의 우울함은 조금 해소가 됐다.
하지만 코드가 더럽고, 중간에 생각과 다르게 구현된 부분도 있지만
모로 가도 서울만 가면 된다니..