Всем Хай! Продолжаем делать нашу игру «Pacman». В прошлой статье мы нарисовали и вывели на экран карту и нашего героя. А сегодня мы должны заставить его двигаться.
Сразу предупрежу, что сегодня мы будем разбирать только новые куски кода. Кода описанного в прошлой статье здесь не будет. То есть если вы скопируете и вставите этот код в «Visual Studio», то он без кода из прошлой статьи работать не будет. Я не вижу смысла дублировать старый код в новую статью, тем самым засоряя её. Весь код в собранном виде будет в третьей статье. Но давайте уже смотреть, как изменился код и что туда добавлено нового.
internal class Program
{
static void Main(string[] args)
{
Console.CursorVisible = false;
bool playing = true;
int pacmanX, pacmanY;
int pacmanDX = 0, pacmanDY = 0;
char[,] map = ReadMap("map", out pacmanX, out pacmanY);
DrawMap(map);
while (playing)
{
if (Console.KeyAvailable)
{
ConsoleKeyInfo key = Console.ReadKey(true);
ChangeDirection(key, ref pacmanDX, ref pacmanDY);
}
if (map[pacmanX + pacmanDX, pacmanY + pacmanDY] != '#')
{
Move(ref pacmanX, ref pacmanY, pacmanDX, pacmanDY);
}
System.Threading.Thread.Sleep(150);
}
}
static void Move(ref int x, ref int y, int dX, int dY)
{
Console.SetCursorPosition(y, x);
Console.Write(" ");
x += dX;
y += dY;
Console.SetCursorPosition(y, x);
Console.Write('@');
}
static void ChangeDirection(ConsoleKeyInfo key, ref int dX, ref int dY)
{
switch (key.Key)
{
case ConsoleKey.UpArrow:
dX = -1; dY = 0;
break;
case ConsoleKey.DownArrow:
dX = 1; dY = 0;
break;
case ConsoleKey.LeftArrow:
dX = 0; dY = -1;
break;
case ConsoleKey.RightArrow:
dX = 0; dY = 1;
break;
}
}
Из нового сразу бросается то, что мы отключили мигающий курсор, чтобы не отвлекал. Точно также мы поступили, когда делали бродилку в консоли в этой статье.
Console.CursorVisible = false
Затем мы добавили булевую переменную для работы цикла и поставили её в положение «true».
bool playing = true
Дальше мы добавили две переменные.
int pacmanDX = 0, pacmanDY = 0
Что это за переменные? Как мы знаем Пакман постоянно в движении, и мы можем менять только его направление. Так вот «DX» это «direction». То есть направление движения по X или по Y.
Дальше уже известные функции из прошлой статьи «ReadMap» и «DrawMap». Их мы пропускаем. И начинается цикл. У нас ведь программа постоянно работает и проверяет изменения, поэтому и цикл. А то мы одну клавишу нажмём у нас всё и закроется. Но перед тем как мы начнём разбирать, что у нас написано в цикле, нам нужно разобрать две функции «Move» и «ChangeDirection».
Изменение направления движения.
Функция «ChangeDirection» у нас будет отвечать за смену направления при нажатии клавиши. Рассмотрим саму функцию подробнее.
static void ChangeDirection(ConsoleKeyInfo key, ref int dX, ref int dY)
{
switch (key.Key)
{
case ConsoleKey.UpArrow:
dX = -1; dY = 0;
break;
case ConsoleKey.DownArrow:
dX = 1; dY = 0;
break;
case ConsoleKey.LeftArrow:
dX = 0; dY = -1;
break;
case ConsoleKey.RightArrow:
dX = 0; dY = 1;
break;
}
}
Функция у нас будет невозвращаемая. В скобках мы пишем, что в ней будет проверяться нажатие клавиш.
ConsoleKeyInfo key
И так же добавляем две переменные «dX» и «dY», которые будут отвечать за смену направления.
ref int dX, ref int dY
«ref» используется, когда нужно обратиться не к переменной, а к памяти, на которую ссылается переменная. Короче для точности.
Дальше начинается обычный «swith». В условиях работы мы пишем, что будем считывать нажатие клавиши «switch (key.Key)».
А дальше просто в каждом кейсе мы описываем, что происходит, когда нажата та или иная клавиша. На примере одного кейса расскажу, что происходит. Остальные разбираются аналогично.
case ConsoleKey.UpArrow:
dX = -1; dY = 0;
break;
При нажатии на стрелку вверх «UpArrow», у нас по иксу меняются координаты на -1. Так как всё происходит в цикле, то есть по кругу, то координаты меняются постоянно, пока не нажмут другую клавишу. Всё просто.
Реализация движения Пакмана.
Но у нас пока не реализовано само движение. Мы только научились менять направление. Как раз для этого у нас появилась функция «Move».
static void Move(ref int x, ref int y, int dX, int dY)
{
Console.SetCursorPosition(y, x);
Console.Write(" ");
x += dX;
y += dY;
Console.SetCursorPosition(y, x);
Console.Write('@');
}
Функция эта достаточно коротка и простая. Используются переменные координат и переменные смены направления.
(ref int x, ref int y, int dX, int dY)
Внутри функции пишем, что там, где стоит курсор, у нас будет рисоваться пустое поле.
Console.SetCursorPosition(y, x);
Console.Write(" ");
После этого к координатам по X и Y мы прибавляем направление движения. А оно у нас принимает 3 положения: 0, 1, -1. В зависимости от нажатой клавиши.
x += dX;
y += dY;
И на месте новых координат рисуем нового Пакмана.
Console.SetCursorPosition(y, x);
Console.Write('@');
Самые внимательные заметили, что эта строчка стояла в конце кода в функции «Main», в прошлой статье. И мы её просто перенесли сюда.
В итоге у нас получается, что по старым координатам Пакман у нас стирается, а по новым рисуется и так по циклу.
Что происходит в цикле? Разбор.
Всё, теперь мы можем вернуться к циклу и разобрать что там написано.
while (playing)
{
if (Console.KeyAvailable)
{
ConsoleKeyInfo key = Console.ReadKey(true);
ChangeDirection(key, ref pacmanDX, ref pacmanDY);
}
if (map[pacmanX + pacmanDX, pacmanY + pacmanDY] != '#')
{
Move(ref pacmanX, ref pacmanY, pacmanDX, pacmanDY);
}
System.Threading.Thread.Sleep(150);
}
В условии цикла у нас стоит «playing», а он, как известно «true». Внутри цикла у нас отрабатывает два «if».
Первый «if» отвечает за направление движения.
if (Console.KeyAvailable)
{
ConsoleKeyInfo key = Console.ReadKey(true);
ChangeDirection(key, ref pacmanDX, ref pacmanDY);
}
Если его разобрать, то получается если нажата клавиша то выполняется условие. А дальше мы смотрим, какая клавиша нажата.
ConsoleKeyInfo key = Console.ReadKey(true)
Потом при помощи ранее написанной функции отрабатывает код в ней.
Второй «if» отвечает за движение.
if (map[pacmanX + pacmanDX, pacmanY + pacmanDY] != '#')
{
Move(ref pacmanX, ref pacmanY, pacmanDX, pacmanDY);
}
Здесь всё просто. Если по указанным координатам на карте не стоит «#», то у нас начинает работать функция движения.
Последняя сточка интересная и новая как для вас, так и для меня. Она отвечает за задержку работы цикла в миллисекундах.
System.Threading.Thread.Sleep(150)
Это нужно чтобы мы вообще замечали движение персонажа. А то так он просто будет в одном месте исчезать, а в другом появляться.
Видео Нет.
Видео я вставлять не буду, потому что не хочу недоделанный проект показывать на видео. Смотреть не на что. Послезавтра уже увидите конечный результат в видео.
На этой ноте я с вами прощаюсь и прошу обратной связи. Всё понятно? Есть пожелания по разборам кода? В общем подписывайтесь. Всё обсудим. Всем пока!
#it #разработкаигр #программированиеснуля #программированиедляначинающих #какстатьпрограммистом #csharp #сишарп