Привет, друзья! 👋 Рад видеть вас снова в блоге. Если вы следите за моим путешествием в C#, то знаете: мы уже создали героя и дали игроку возможность выбирать класс. А сегодня… мы наконец-то вступаем в бой! 💥
Сегодняшний урок — про настоящие игровые механики: случайные числа (чтобы броски кубика были непредсказуемыми) и циклы (чтобы бой шёл шаг за шагом, пока кто-то не проиграет). И всё это — под руководством моего ИИ‑наставника.
🎲 Случайность как основа игры
В любой RPG важна доля удачи. Удастся ли нанести критический удар? Промахнётся ли враг? Для этого в C# есть класс Random — он работает как виртуальный кубик.
Пример:
Random random = new Random(); // Создаем "генератор кубиков"
int damage = random.Next(5, 21); // Бросаем кубик: число от 5 до 20 (21 не включается!)
Console.WriteLine("Ты нанес " + damage + " урона!");
Сначала было непривычно: я думал, что можно просто «взять случайное число» без подготовки, но оказалось, что сначала нужно создать объект Random, а уже потом просить у него числа.
🔁 Цикл while: бой продолжается, пока кто-то жив
Чтобы бой не закончился после одного удара, мы использовали цикл while. Его логика простая: «Пока здоровье игрока и гоблина больше 0 — продолжаем бой».
Но тут наставник сразу предупредил про опасность: если забыть менять здоровье внутри цикла, условие никогда не станет ложным — и программа зависнет в бесконечном цикле. Поэтому я специально следил, чтобы после каждого удара здоровье уменьшалось.
Пример:
int monsterHealth = 50;
while (monsterHealth > 0) // ПОКА здоровье монстра больше нуля
{
int damage = random.Next(10, 26);
monsterHealth = monsterHealth - damage; // Можно записать короче: monsterHealth -= damage;
Console.WriteLine("Ты атакуешь монстра! Урон: " + damage);
Console.WriteLine("Здоровье монстра: " + monsterHealth);
}
Console.WriteLine("Монстр повержен!");
Мое домашнее задание: Бой с гоблином! 🗡️
Объедини всё в одну программу. Сценарий такой:
- Игрок создает персонажа (используй код из прошлого урока).
- После создания персонажа появляется Гоблин со здоровьем 50.
- Начинается пошаговый бой в цикле
while, который идет, пока здоровье гоблина или игрока не станет ≤ 0. - В каждом ходу:
- Игрок атакует гоблина. Урон — случайное число (например, от 10 до 25).
- Выводим сообщение: «Ты атаковал гоблина и нанес X урона. У гоблина осталось Y здоровья.»
- Если гоблин жив — он атакует в ответ. Его урон — случайное число от 5 до 15.
- Вычитаем урон из здоровья игрока и выводим сообщение.
- Если игрок погиб — выходим из цикла.
- После цикла проверяем, кто победил (через
if), и выводим красивое сообщение о победе или поражении.
Подсказка для выхода из цикла: Существует специальная команда break;, которая моментально завершает цикл. Её можно применять, если гоблин погибает, чтобы предотвратить его ответный удар.
Пример структуры:
while (heroHealth > 0 && monsterHealth > 0) // Пока оба живы
{
// Ход игрока
// ...
if (monsterHealth <= 0) break; // Монстр умер, выходим
// Ход монстра
// ...
}
Удачи в битве с гоблином! 🛡️⚔️
💻 Мой код: пошаговый бой с гоблином
Вот что у меня получилось по заданию:
using System;
using System.ComponentModel.Design; //Эта строчка подключает базовые инструменты C#. Она должна быть сверху.
namespace MyFirstRPG // Это название нашего проекта, как название папки с заклинаниями
{
class Program // Главный класс. Пока просто знай, что он нужен.
{
// Это точка входа. С этой строчки начинает выполняться наш код.
static void Main(string[] args)
{
// Создаем коробочки (переменные) для нашего героя и кладем в них данные
int heroLevel = 1; // Уровень героя (число)
int heroHealth = 100; // Здоровье героя (число)
int heroMana = 50; // Мана героя (число)
// Знакомство с игроком
Console.WriteLine("Добро пожаловать в мою первую RPG!");
Console.WriteLine("Как тебя зовут?");
string heroName = Console.ReadLine(); // Программа ждет ввода и сохранит его в переменную
Console.WriteLine("Привет, " + heroName + "!");
// Выбираем класс и выведем информацию
Console.WriteLine("Выбери класс за который будешь играть: 1 - Воин (много здоровья), 2 - Маг (много маны)");
string heroClass = Console.ReadLine(); // Вводим класс героя и сохраняем в переменную
// Начало условия
if (heroClass == "1") // Условия для выбора класса героя
{
heroHealth = 150;
heroMana = 20;
heroClass = "Воин";
Console.WriteLine("Вы выбрали воина");
}
else if (heroClass == "2")
{
heroHealth = 80;
heroMana = 150;
heroClass = "Маг";
Console.WriteLine("Вы выбрали мага");
}
else
{
heroHealth = 100;
heroMana = 50;
heroClass = "Человек";
Console.WriteLine("Такого класса нет!!! Получи стандартные хр-ки");
}
// Конец условия
// Вывод результата
Console.WriteLine("Герой: [" + heroName + "] готов к бою!");
Console.WriteLine("Класс: " + heroClass);
Console.WriteLine("Здоровье: " + heroHealth);
Console.WriteLine("Мана: " + heroMana);
Console.WriteLine("Уровень: " + heroLevel);
// Бой с монстром новый урок от Qwen
Console.WriteLine("Вы появились на поляне и видите, как перед вами бегает злобный гоблин. Ваши действия?");
string monster = "Гоблин";
int monsterHealth = 50;
Console.WriteLine("Для атаки напиши: НАПАСТЬ, если не хочешь драться, напиши: УЙТИ");
while (true) // Начало цикла
{
string Battle = Console.ReadLine(); // Эксперемент с циклом, для закрепления
if (Battle.Equals("НАПАСТЬ", StringComparison.OrdinalIgnoreCase)) // Узнал про команду в интернете, чтобы не учитывал регистр
{
Console.WriteLine("Вы вступили в бой с гоблином");
break;
}
else if (Battle.Equals("УЙТИ", StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine("Вы ушли домой");
break;
}
else
{
Console.WriteLine("Ошибка, вы ввели неправильную команду");
}
} // Конец цикла
Random random = new Random(); // Создаем "генератор кубиков"
while (heroHealth > 0 && monsterHealth > 0) // Пока оба живы
{
Console.WriteLine("Твой ход");
int hdamage = random.Next(10, 26);
monsterHealth = monsterHealth - hdamage;
Console.WriteLine("Ты атаковал гоблина и нанес " + hdamage + " урона. У гоблина осталось " + monsterHealth + " здоровья.");
if (monsterHealth <= 0)
{
Console.WriteLine("Гоблин мертв");
break;
}
if (monsterHealth > 0)
{
Console.WriteLine("Ход гоблина");
int mdamage = random.Next(5, 16);
heroHealth = heroHealth - mdamage;
Console.WriteLine("Гоблин атаковал героя и нанес " + mdamage + " урона. У героя осталось " + heroHealth + " здоровья.");
}
}
if (heroHealth <= 0)
{
Console.WriteLine("Победил гоблин");
}
else if (monsterHealth <= 0)
{
Console.WriteLine("Ты победил");
}
// Эта команда ждет нажатия любой клавиши, чтобы окно не закрылось мгновенно
Console.ReadKey();
}
}
}
Когда я запустил этот код, впервые почувствовал настоящий игровой ритм: ход игрока, ход врага, нарастающее напряжение, когда здоровье падает. И каждый запуск — новый результат, потому что урон случайный. Это уже похоже на настоящую игру! 🤩
🧠 Что было сложно и как я с этим справился
Сначала я забыл добавить break после смерти гоблина — и в одном из тестов мёртвый монстр всё равно ударил в ответ. Наставник сразу указал на эту логическую дыру, и я понял: в пошаговых боях важно проверять состояние после каждого действия.
Ещё я чуть не создал бесконечный цикл, забыв уменьшать здоровье. К счастью, программа быстро «зависла» в моём тесте, и я сразу это заметил. Теперь я всегда проверяю: меняется ли условие выхода из цикла внутри него.
Также наставник обратил внимание на мелочи: опечатки в тексте сообщений и логику блока else. Оказалось, что даже если опечатка не ломает код, она портит впечатление от игры. Теперь я стараюсь писать тексты аккуратно, как будто это уже финальная версия.
ИИ устроил мне следующее:
🔍 Разбор полетов (Code Review)
- Логическая ловушка «УЙТИ»: Попробуй запустить свою программу и ввести «УЙТИ». Программа напишет «Вы ушли домой», прервет первый цикл (
break), но… сразу после этого она начнет бой с гоблином! Почему? Потому чтоbreakвыкидывает тебя только из первого циклаwhile(true), а код идет дальше, к генератору случайных чисел и второму циклу. Решение: Чтобы полностью завершить работу программы в этом месте, используй командуreturn;вместоbreak;. Она говорит: «Заверши методMainпрямо сейчас и закрой программу». - Лишний багаж: Строка
using System.ComponentModel.Design;тебе не нужна. Директиваusing— это как приглашение конкретного мастера в кузницу.Systemнужен всегда, а вотComponentModel.Design— это инструменты для создания визуальных форм в старых приложениях, в консольной RPG они только занимают место. Смело удаляй её. - Неиспользованный артефакт: Ты создал переменную
string monster = "Гоблин";, но в тексте боя ты пишешь слово «гоблин» вручную. В будущем мы научимся вставлять переменные прямо в текст, чтобы было универсально!
🚀 Что дальше?
Впереди — массивы и коллекции. Они понадобятся, чтобы реализовать инвентарь, список врагов, диалоги и прокачку. А потом будем переносить эту логику в игровой движок (скорее всего, Unity).
А вы любите пошаговые бои или предпочитаете экшен в реальном времени? Какая боевая система кажется вам самой интересной? Может, у вас есть любимые RPG с крутой механикой боя — расскажите в комментариях, мне очень хочется вдохновиться! 👇
И ещё вопрос: какую следующую механику вы бы добавили в такую консольную RPG? Может, критические удары, уклонения или систему прокачки? Ваши идеи могут стать основой для следующего поста! 💡