Всем Хай! Сегодня будет самая длинная и сложная статья из всех на канале. Может в будущем будет длиннее, но я постараюсь в следующий раз так сильно не растягивать. Всё из-за того, что добавляется много нового кода. Естественно в статью я его запихивать не буду. Приложу отдельным файлом.
Скачать файл с кодом игры Пакман.
Когда вы откроите код то заметите, что появились новые функции и были внесены изменения в старые функции. И в целом было практически везде по чуть-чуть что-то добавлено или убрано.
Сегодня у нас несколько задач.
- Реализовать сбор монет Пакманом.
- Реализовать самостоятельное движение врага в разных направлениях.
- Реализовать законченность игры. Либо собрать монеты, либо умереть.
Новые функции и обновление старых.
Для простоты понимания я решил сначала показать вам новые функции. Их у нас две.
- 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 #сишарп