Запрос относительно дизайна основанной на классах текстовой игры приключения.

Я изучал C# за лето и теперь испытываю желание делать маленький проект из того, что я сделал до сих пор. Я выбрал своего рода основанную на тексте игру приключения.

Базовая структура игры включит наличие многих секторов (или комнаты). После записи в комнату описание будет произведено и много действий, и такой можно взять; способность исследовать, возьмите, используйте материал в той комнате; возможно система сражения, и т.д. и т.д. Сектор может быть соединен до 4 других секторов.

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

Я выбрал класс плеера и класс 'уровня', который представляет уровень/темницу/область. Этот класс уровня состоял бы из многих взаимосвязанных 'секторов'. В любой момент времени плеер присутствовал бы в одном определенном секторе на уровне.

Таким образом, вот беспорядок:

Логически, можно было бы ожидать метод такой как player.Move(Dir d)
Такой метод должен изменить 'текущий сектор' поле в объекте уровня. Это означает, что класс Плеер должен был бы знать о классе Уровень. Хм. И Уровню, вероятно, придется управлять объектом Плеера (например, плеер вводит комнату, которой что-то устраивает засаду, теряет что-то из материально-технических ресурсов.) Поэтому теперь Уровень также должен содержать ссылку на объект Плеера?

Это не чувствует себя прекрасно; все имеющее необходимость содержать ссылку на все остальное.

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

Это дало мне идею разработать классы следующим образом:

Плеер:

class Player
{
    //...

    public delegate void Movement(Dir d);   //enum Dir{NORTH, SOUTH, ...}

    public event Movement PlayerMoved;

    public void Move(Dir d)
    {        
        PlayerMoved(d);

        //Other code...
    }

}

Уровень:

class Level
{
    private Sector currSector;
    private Player p;
    //etc etc...

    private void OnMove(Dir d)
    {
        switch (d)
        {
            case Dir.NORTH:
                //change currSector
                //other code
                break;

                //other cases
        }
    }

    public Level(Player p)
    {
        p.PlayerMoved += OnMove;  
        currSector = START_SECTOR;
        //other code
    }

    //etc...
}

Действительно ли это - в порядке способ сделать это?
Если бы глава делегата не была представлена путем, это было, я не думал бы об использовании таких 'событий'. Таким образом, каков был бы хороший способ реализовать это, не используя обратные вызовы?

У меня есть привычка к тому, чтобы заставлять очень подробные сообщения... извините v __ v

14
задан Textmode 29 August 2013 в 09:11
поделиться

5 ответов

А как насчет класса Game, который будет содержать большую часть информации, такой как Player и текущая комната. Для такой операции, как перемещение игрока, класс Game может переместить игрока в другую комнату в зависимости от карты уровня комнаты.

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

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

UDPATE:

Чтобы сделать код более управляемым, вы можете смоделировать некоторые взаимодействия между основными классами как сами классы, например, класс Fight. Используйте интерфейсы, чтобы позволить вашим основным классам выполнять определенные взаимодействия. (Обратите внимание, что я взял на себя смелость изобрести несколько вещей, которые могут вам не понадобиться в вашей игре).

Например:

// Supports existance in a room.
interface IExistInRoom { Room GetCurrentRoom(); }

// Supports moving from one room to another.
interface IMoveable : IExistInRoom { void SetCurrentRoom(Room room); }

// Supports being involved in a fight.
interface IFightable
{
  Int32 HitPoints { get; set; }
  Int32 Skill { get; }
  Int32 Luck { get; }
}

// Example class declarations.
class RoomFeature : IExistInRoom
class Player : IMoveable, IFightable
class Monster : IMoveable, IFightable

// I'd proably choose to have this method in Game, as it alters the
// games state over one turn only.
void Move(IMoveable m, Direction d)
{
  // TODO: Check whether move is valid, if so perform move by
  // setting the player's location.
}

// I'd choose to put a fight in its own class because it might
// last more than one turn, and may contain some complex logic
// and involve player input.
class Fight
{
  public Fight(IFightable[] participants)

  public void Fight()
  {
    // TODO: Logic to perform the fight between the participants.
  }
}

В своем вопросе вы определили тот факт, что у вас будет много классов, которые должны знать друг о друге, если вы закрепите что-то вроде метода Move в своем классе Player. Это потому, что что-то вроде хода не принадлежит ни игроку, ни комнате - ход влияет на оба объекта взаимно. Моделируя «взаимодействия» между основными объектами, вы можете избежать многих из этих зависимостей.

5
ответ дан 1 December 2019 в 15:20
поделиться

Похоже на сценарий, для которого я часто использую класс Command или класс Service. Например, я мог бы создать класс MoveCommand, который выполняет операции и координацию между уровнями и людьми.

Этот шаблон имеет то преимущество, что дополнительно усиливает принцип единой ответственности (SRP). SRP говорит, что у класса должна быть только одна причина для изменения. Если за перемещение отвечает класс Person, у него, несомненно, будет несколько причин для изменения. Разбивая логику Move off на отдельный класс, он лучше инкапсулируется.

Есть несколько способов реализовать класс Command, каждый из которых лучше подходит для разных сценариев. Командные классы могут иметь метод Execute, который принимает все необходимые параметры:

 public class MoveCommand {
    public void Execute(Player currentPlayer, Level currentLevel) { ... }
 }

 public static void Main() {
     var cmd = new MoveCommand();
     cmd.Execute(player, currentLevel);
}

Или, иногда мне кажется более простым и гибким использовать свойства в командном объекте, но это облегчает клиентскому коду неправильное использование класса, забывая для установки свойств - но преимущество в том, что у вас есть одна и та же сигнатура функции для Execute для всех классов команд, поэтому вы можете создать интерфейс для этого метода и работать с абстрактными командами:

 public class MoveCommand {
    public Player CurrentPlayer { get; set; } 
    public Level CurrentLevel { get; set; }
    public void Execute() { ... }
 }

 public static void Main() {
     var cmd = new MoveCommand();
     cmd.CurrentPlayer = currentPlayer;
     cmd.CurrentLevel = currentLevel;
     cmd.Execute();
}

Наконец, вы можете предоставить параметры в качестве аргументов конструктора в класс Command, но я откажусь от этого кода.

В любом случае, я считаю использование команд или служб очень мощным способом обработки операций, таких как Move.

2
ответ дан 1 December 2019 в 15:20
поделиться

Для текстовой игры у вас почти наверняка будет объект CommandInterpretor (или аналогичный), который оценивает вводимые пользователем команды. С таким уровнем абстракции вам не нужно реализовывать все возможные действия с вашим объектом Player. Ваш интерпретатор может передать некоторые набранные команды вашему объекту Player («показать инвентарь»), некоторые команды - объекту сектора, занятому в данный момент («список выходов»), некоторые команды - объекту уровня («переместить игрока на север»), а некоторые команды для специальных объектов («атака» может быть передана объекту CombatManager).

Таким образом, объект Player становится больше похож на персонажа, а CommandInterpretor больше соответствует реальному человеку-игроку, сидящему за клавиатурой.

1
ответ дан 1 December 2019 в 15:20
поделиться

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

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

Уровень "содержит" все, что находится внутри него. Вы можете начать с него. Уровень не обязательно должен управлять всем, но все находится в уровне.

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

Уровень не забирает у игрока предметы и не наносит урон. Эти действия выполняют другие объекты на уровне. Эти другие объекты должны искать игрока, или, возможно, им будет сообщено о близости игрока, и тогда они смогут делать то, что хотят, непосредственно с игроком.

Это нормально, что уровень "владеет" игроком и что игрок имеет ссылку на свой уровень. Это "имеет смысл" с точки зрения ОО; вы стоите на планете Земля и можете влиять на нее, но она таскает вас по вселенной, пока вы копаете ямы.

Делайте простые вещи. Каждый раз, когда что-то становится сложным, придумайте, как сделать это проще. С простым кодом легче работать, и он более устойчив к ошибкам.

1
ответ дан 1 December 2019 в 15:20
поделиться

Итак, во-первых, это хороший способ сделать это?

Совершенно верно!

Во-вторых, если глава делегата не представили так, как это было, я бы не думал об использовании таких 'Мероприятия'. Итак, что было бы хорошим способом реализовать это без использования обратные вызовы?

Я знаю много других способов реализовать это, но нет другого хорошего способа без какого-либо механизма обратного вызова. ИМХО, это наиболее естественный способ создания несвязанной реализации.

0
ответ дан 1 December 2019 в 15:20
поделиться
Другие вопросы по тегам:

Похожие вопросы: