Текстовая игра на C#

Приветствую! Здесь вы наверняка найдете, что ищете. Примеры в лаборатории рассчитаны на то, что мы разбираем что-то конкретное.

Текущая статья посвящена текстовой игре на C# — состояние, сцены, ввод пользователя и циклы.

Поэтому за теорией по текущей теме вам — в энциклопедию. Если ещё не погружались, то маршрут прост:

  1. Основы
  2. Система и сеть
  3. Данные и разметка
  4. Код и разработка
  5. Языки
  6. Искусственный интеллект
  7. Проект
  8. Инфраструктура и безопасность
  9. Спин-офф

Обязательно пройдитесь.

А теперь приступим к нашему предмету.

Теория и соседние материалы

Текстовая игра на C#

Текстовая игра — это классический формат интерактивного программного обеспечения, в котором взаимодействие с пользователем происходит исключительно через текстовый ввод и вывод. Такие игры не требуют графического интерфейса, а игровой процесс строится на описаниях, выборе действий и логике принятия решений. В среде обучения программированию текстовые игры часто используются как практический способ освоить базовые конструкции языка: циклы, условия, методы, коллекции и работу с пользовательским вводом.

В данном примере реализуется консольная текстовая игра на C# с использованием современных возможностей .NET: асинхронности, объектно-ориентированного подхода, инкапсуляции логики и модульной структуры кода. Игра демонстрирует принципы проектирования небольших приложений, которые можно легко расширять и модифицировать.


Архитектура игры

Текстовая игра состоит из следующих ключевых компонентов:

  • GameLoop — основной цикл игры, управляющий последовательностью событий;
  • GameState — объект, хранящий текущее состояние игры (здоровье героя, уровень, инвентарь и т.д.);
  • Narrator — компонент, отвечающий за вывод текстовых сообщений;
  • PlayerInput — обработчик пользовательского ввода;
  • Scene — абстракция игровой сцены или локации;
  • Choice — вариант действия, доступный игроку в текущей сцене.

Такая структура позволяет отделить логику игры от её представления и упрощает добавление новых сцен, персонажей или механик.


Базовые классы и модели

Класс GameState

public class GameState
{
    public string PlayerName { get; set; } = "Игрок";
    public int Health { get; set; } = 100;
    public int Score { get; set; } = 0;
    public List<string> Inventory { get; set; } = new();
    public string CurrentSceneId { get; set; } = "start";
}

Этот класс содержит всё, что определяет текущее положение игрока в мире игры. Он передаётся между сценами и изменяется по мере развития сюжета.


Класс Choice

public class Choice
{
    public string Text { get; set; } = string.Empty;
    public string TargetSceneId { get; set; } = string.Empty;
    public Action<GameState>? OnSelect { get; set; }
}

Каждый выбор содержит:

  • текст, который видит игрок;
  • идентификатор следующей сцены;
  • необязательное действие, которое выполняется при выборе (например, изменение здоровья или добавление предмета в инвентарь).

Абстрактный класс Scene

public abstract class Scene
{
    public string Id { get; init; } = string.Empty;
    public abstract string Description { get; }
    public abstract List<Choice> GetChoices(GameState state);
}

Каждая конкретная сцена наследуется от этого класса и реализует два метода:

  • Description — возвращает описание локации;
  • GetChoices — возвращает доступные действия в зависимости от состояния игры.

Реализация конкретных сцен

Стартовая сцена


Сцена красной комнаты

Обратите внимание: при открытии сундука выполняется действие OnSelect, которое проверяет наличие предмета и добавляет его, если его ещё нет. Это предотвращает дублирование и обеспечивает согласованность состояния.


Сцена инвентаря

Здесь используется простой приём: описание генерируется динамически на основе текущего состояния. Это позволяет отображать актуальную информацию без необходимости хранить её отдельно.


Компонент Narrator

Narrator отвечает только за вывод. Он не знает ничего о логике игры — это соответствует принципу разделения ответственности.


Обработка ввода

Метод ReadChoiceAsync запрашивает у пользователя число, проверяет его корректность и возвращает индекс выбранного варианта. Использование async/await делает код совместимым с потенциально асинхронными источниками ввода (например, сетевыми клиентами в будущем).


Основной игровой цикл

Цикл игры:

  1. Запрашивает имя игрока.
  2. Пока здоровье больше нуля:
    • загружает текущую сцену;
    • выводит описание;
    • показывает варианты выбора;
    • читает ввод;
    • выполняет действие (если есть);
    • обновляет текущую сцену.
  3. Завершает игру при достижении финальной сцены или смерти.

Запуск игры

// Program.cs
var game = new GameLoop();
await game.RunAsync();

В проекте .NET 6+ или выше достаточно поместить этот код в файл Program.cs. Приложение будет работать как полноценная консольная программа.


Расширяемость и модификация

Такая архитектура легко расширяется:

  • Добавление новых сцен: создаётся новый класс, наследующий Scene, и регистрируется в GameLoop.
  • Сложные условия: в GetChoices можно добавлять проверки (if (state.Inventory.Contains("ключ"))).
  • Случайные события: использование Random для генерации разных исходов.
  • Сохранение прогресса: сериализация GameState в JSON и запись в файл.
  • Локализация: вынос текстов в ресурсы или словари.

Пример сохранения:

public static void SaveGame(GameState state, string path)
{
    var json = JsonSerializer.Serialize(state, new JsonSerializerOptions { WriteIndented = true });
    File.WriteAllText(path, json);
}

Преимущества подхода

  • Читаемость: каждая сцена — самостоятельный класс.
  • Тестируемость: можно писать unit-тесты для GetChoices.
  • Гибкость: логика выбора полностью отделима от вывода.
  • Поддерживаемость: изменения в одной сцене не влияют на другие.

Текстовая игра на C# — это не просто развлечение, а учебный проект, демонстрирующий фундаментальные принципы разработки: инкапсуляцию, абстракцию, управление состоянием и проектирование взаимодействия с пользователем.