🎮 Пост №4: В бой с гоблином! Добавляем случайность и циклы в нашу RPG 🗡️

Привет, друзья! 👋 Рад видеть вас снова в блоге. Если вы следите за моим путешествием в 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("Монстр повержен!");

Мое домашнее задание: Бой с гоблином! 🗡️

Объедини всё в одну программу. Сценарий такой:

  1. Игрок создает персонажа (используй код из прошлого урока).
  2. После создания персонажа появляется Гоблин со здоровьем 50.
  3. Начинается пошаговый бой в цикле while, который идет, пока здоровье гоблина или игрока не станет ≤ 0.
  4. В каждом ходу:
    • Игрок атакует гоблина. Урон — случайное число (например, от 10 до 25).
    • Выводим сообщение: «Ты атаковал гоблина и нанес X урона. У гоблина осталось Y здоровья.»
    • Если гоблин жив — он атакует в ответ. Его урон — случайное число от 5 до 15.
    • Вычитаем урон из здоровья игрока и выводим сообщение.
    • Если игрок погиб — выходим из цикла.
  5. После цикла проверяем, кто победил (через 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)

  1. Логическая ловушка «УЙТИ»: Попробуй запустить свою программу и ввести «УЙТИ». Программа напишет «Вы ушли домой», прервет первый цикл (break), но… сразу после этого она начнет бой с гоблином! Почему? Потому что break выкидывает тебя только из первого цикла while(true), а код идет дальше, к генератору случайных чисел и второму циклу. Решение: Чтобы полностью завершить работу программы в этом месте, используй команду return; вместо break;. Она говорит: «Заверши метод Main прямо сейчас и закрой программу».
  2. Лишний багаж: Строка using System.ComponentModel.Design; тебе не нужна. Директива using — это как приглашение конкретного мастера в кузницу. System нужен всегда, а вот ComponentModel.Design — это инструменты для создания визуальных форм в старых приложениях, в консольной RPG они только занимают место. Смело удаляй её.
  3. Неиспользованный артефакт: Ты создал переменную string monster = "Гоблин";, но в тексте боя ты пишешь слово «гоблин» вручную. В будущем мы научимся вставлять переменные прямо в текст, чтобы было универсально!

🚀 Что дальше?

Впереди — массивы и коллекции. Они понадобятся, чтобы реализовать инвентарь, список врагов, диалоги и прокачку. А потом будем переносить эту логику в игровой движок (скорее всего, Unity).


А вы любите пошаговые бои или предпочитаете экшен в реальном времени? Какая боевая система кажется вам самой интересной? Может, у вас есть любимые RPG с крутой механикой боя — расскажите в комментариях, мне очень хочется вдохновиться! 👇

И ещё вопрос: какую следующую механику вы бы добавили в такую консольную RPG? Может, критические удары, уклонения или систему прокачки? Ваши идеи могут стать основой для следующего поста! 💡

Оставьте комментарий