Найти тему

Пакман Финал! 3-я Часть разбора кода игры в консоли.

Оглавление
Скаченно с Яндекс картинок
Скаченно с Яндекс картинок

Всем Хай! Сегодня будет самая длинная и сложная статья из всех на канале. Может в будущем будет длиннее, но я постараюсь в следующий раз так сильно не растягивать. Всё из-за того, что добавляется много нового кода. Естественно в статью я его запихивать не буду. Приложу отдельным файлом.

Скачать файл с кодом игры Пакман.

Когда вы откроите код то заметите, что появились новые функции и были внесены изменения в старые функции. И в целом было практически везде по чуть-чуть что-то добавлено или убрано.

Сегодня у нас несколько задач.

  1. Реализовать сбор монет Пакманом.
  2. Реализовать самостоятельное движение врага в разных направлениях.
  3. Реализовать законченность игры. Либо собрать монеты, либо умереть.

Новые функции и обновление старых.

Для простоты понимания я решил сначала показать вам новые функции. Их у нас две.

  • CollectCoin – отвечает за сбор монет.
  • ChangeDirection – отвечает за смену направления врага. Внимательные скажут, что это не новая функция, а старая. Именно с таким именем мы делали функцию в прошлой статье для нашего героя. В чём фишка я расскажу чуть позже.

Делаем сбор монет Пакманом.

Также претерпела изменение функция «ReadMap». В неё были добавлены «враг» и «монеты». С неё и начнём.

static char[,] ReadMap(string mapName, out int positionX, out int positionY, out int ghostX, out int ghostY, ref int allCoin)
{
positionX = 0;
positionY = 0;
ghostX = 0;
ghostY = 0;
string[] line = File.ReadAllLines($"Maps/{mapName}.txt");
char[,] result = new char[line.Length, line[0].Length];
for (int i = 0; i < line.Length; i++)
{
for (int j = 0; j < line[0].Length; j++)
{
result[i, j] = line[i][j];
if (result[i, j] == '@')
{
positionX = i;
positionY = j;
result[i, j] = ' ';
}
else if (result[i, j] == '$')
{
ghostX = i;
ghostY = j;
result[i, j] = '.';
}
if (result[i, j] == ' ')
{
result[i, j] = '.';
allCoin++;
}
}
}
return result;
}

В ней появилось три переменные.

Координаты призрака(враг).

out int ghostX, out int ghostY

И все монеты на карте.

ref int allCoin

Координаты как в случае с героем сразу выставляем на 0. Программа сама их определит.

ghostX = 0;
ghostY = 0;

Дальше мы добавили три условия.

  • В первом мы находим нашего героя, запоминаем его координаты и рисуем там пустое место.
if (result[i, j] == '@')
{
positionX = i;
positionY = j;
result[i, j] = ' ';
}
  • Во втором мы находим нашего врага, запоминаем его координаты и рисуем там монету.
else if (result[i, j] == '$')
{
ghostX = i;
ghostY = j;
result[i, j] = '.';
}
  • И третьим, отдельным условием мы находим все пустые места на карте и ставим там по монете. При этом каждый раз добавляем единицу к общему числу монет на карте. Это для того, чтобы понять, сколько нам нужно собрать монет для победы и не расставлять все монеты на картах ручками.
if (result[i, j] == ' ')
{
result[i, j] = '.';
allCoin++;
}

Всё, переходим к функции отвечающей за сбор монет «CollectCoin».

static void CollectCoin(char[,] map, int pacmanX, int pacmanY, ref int coin)
{
if (map[pacmanX, pacmanY] == '.')
{
coin++;
map[pacmanX, pacmanY] = ' ';
}
}

Здесь всё просто. Если координаты Пакмана равняются координатам монеты, то мы рисуем там пустое место и прибавляем в нашу копилку одну монету. Всё.

Реализация самостоятельного движения Врага(Призрака) в разных направлениях.

И наконец, мы переходим к движению нашего врага. Если для передвижения мы можем использовать туже функцию, как и у Пакмана, то для смены направления это уже не подходит. Вы помните, что Пакман управляется клавиатурой, а наш Призрак должен сам ходить и поворачивать.

Для реализации смены направления движения нам подходит рандом. То есть если наш призрак упрётся в стену, то он рандомно меняет направление сам. Это можно сказать зачатки примитивного искусственного интеллекта для врага. А прописано это так.

static void ChangeDirection(Random random, ref int dX, ref int dY)
{
int ghostDir = random.Next(1, 5);
switch (ghostDir)
{
case 1:
dX = -1; dY = 0;
break;
case 2:
dX = 1; dY = 0;
break;
case 3:
dX = 0; dY = -1;
break;
case 4:
dX = 0; dY = 1;
break;
}
}

Мы использовали тоже название функции, что и для функции смены направления для Пакмана «ChangeDirection». Только в скобках изменили свойства. Это называется сделать перегрузку функции. При запуске кода компилятор в зависимости от условий в скобках сам поймет, какую функцию использовать в коде.

Здесь мы использовали рандом от 1 до 5. А 5, как известно не используется, поэтому у нас 4 цифры. Как раз четыре направления. Вверх, вниз, влево, вправо. И в зависимости от того какая цифра выпадает на то направление призрак и переключается.

Реализация условий выигрыша и проигрыша Пакмана.

Осталось прописать все нововведения в основной функции «Main». Давайте по порядку.

У нас добавилась строчка с рандомом для изменения направления врага.

Random random = new Random()

Потом булевая переменная, которая показывает, жив Пакман или нет. По умолчанию он жив.

bool isAlive = true

Дальше добавили две переменные для подсчёта всего количества монет и монет, которые мы собрали. Поначалу всё по 0. Общее количество монет функция «ReadMap» сама посчитает и выведет на карту.

int allCoin = 0;
int coin = 0;

Мы добавили координаты нашего врага, которые функция «ReadMap» сама найдёт и подставит.

int ghostX, ghostY;

И добавили две переменные изменения направления по X и Y, как у Пакмана. Причём сразу прописываем 1 по Y чтобы при запуске он сразу начал движение вправо.

int ghostDX = 0, ghostDY = 1;

Всё считываем и выводим на экран функциями «ReadMap» и «DrawMap». И переходим в цикл.

Этот цикл с новыми вводными тоже получил обновление. И начинаются они со второго «if».

if (map[pacmanX + pacmanDX, pacmanY + pacmanDY] != '#')
{
Move(map, '@', ref pacmanX, ref pacmanY, pacmanDX, pacmanDY);
CollectCoin(map, pacmanX, pacmanY, ref coin);
}

Здесь у нас добавилась ранее созданная функция для сбора монет «CollectCoin». В условии мы прописали наш двумерный массив карту «map», координаты Пакмана и количество собранных монет «ref coin».

Затем мы прописали движение Призрака также как и у Пакмана.

if (map[ghostX + ghostDX, ghostY + ghostDY] != '#')
{
Move(map, '$', ref ghostX, ref ghostY, ghostDX, ghostDY);
}

И в случае встречи со стеной он будет менять направление.

else
{
ChangeDirection(random, ref ghostDX, ref ghostDY);
}

Потом мы ставим курсор по координатам ниже карты и пишем, сколько мы монет собрали из общего числа их на карте.

Console.SetCursorPosition(0, 21);
Console.Write($"Собранно монет {coin - 1} из {allCoin}");

Теперь осталось прописать условия победы и поражения Пакмана.

Если координаты Пакмана равняются координатам Призрака, то мы умираем.

if (pacmanX == ghostX && pacmanY == ghostY)
{
isAlive = false;
}

И если количество собранных монет будет ровняться общему количество монет на карте или мы не живы, то игра заканчивается.

if (coin - 1 == allCoin || !isAlive)
{
playing = false;
}

Всё, осталось проверить, при каких условиях закончилась игра и вывести соответствующую запись на заданных координатах.

Если мы собрали монеты то одна запись.

if (coin - 1 == allCoin)
{
Console.Write("Поздравляю! Вы собрали все монеты!\n");
}

А если мы не живы то другая запись.

else if (!isAlive)
{
Console.Write("Вас съели!\n");
}

На этом игра заканчивается. Смотрите видео работы кода.

Смотреть видео Игры в Пакмана.

На первый взгляд игра покажется простой, но если сделать карту более запутанной и добавить больше врагов, то пройти её будет не просто.

Ну а на этом у меня всё. Если возникли вопросы, пишите в комментариях. Ставьте лайк и подписывайтесь на канал. Мы скоро закончим с программирование и перейдём в Юнити, а там будут уже совсем другие игры. 😉 Всем пока!

#it #разработкаигр #программированиеснуля #программированиедляначинающих #какстатьпрограммистом #csharp #сишарп