Очистите управляемые данными системы и контент в видеоиграх

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

При чтении поста необходимо учитывать следующее:

В интересах этого поста мы собираемся обобщить и сказать, что игра — это часть программного обеспечения, которую можно разделить на две части: системы и данные (контент). Системы — это части программного обеспечения, которые при наличии некоторых данных будут вести себя тем или иным образом. Например, текстовой диалоговой системе может быть предоставлен фрагмент текста, который необходимо воспроизвести, или NPC может быть задано поведение ИИ для выполнения. Какими бы ни были сложность и конкретный вариант использования, эти две части работают вместе, чтобы предоставить игроку желаемый опыт.

Случаи использования

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

Один

  • Есть NPC
  • У NPC есть система передвижения.
  • Система передвижения может перемещать NPC только одним способом: по некоторым путевым точкам.
  • У нас есть часть данных с путевыми точками.
  • Данные передаются системе передвижения, чтобы она могла двигаться.

У нас есть только одна система, и она может делать только одно. Для этого системе предоставляются данные, необходимые для этого.

public class LocomotionSystemData
{
    public IReadOnlyList<Position> Positions { get; }
}
public class LocomotionSystem
{
    private LocomotionSystemData locomotionSystemData;
    public void Move(LocomotionSystemData locomotionSystemData)
    {
        this.locomotionSystemData = locomotionSystemData;
    }
    //Method called once per frame
    public void Tick()
    {
        //Movement code using data here...
    }
}

На этом фрагменте кода мы можем наблюдать:

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

Несколько статических

  • Есть NPC
  • У NPC есть система передвижения.
  • Существует множество реализаций систем передвижения: следование по некоторым путевым точкам и следование за целью на расстоянии.
  • У нас есть несколько фрагментов данных, каждый из которых содержит данные для каждой реализации.
  • Данные передаются системе передвижения, чтобы она могла двигаться.

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

public class WaypointLocomotionSystemData
{
    public IReadOnlyList<Position> Positions { get; }
}
public class TargetAtDistanceLocomotionSystemData
{
    public Position TargetPosition { get; }
    public float Distance { get; }
}
public class WaypointLocomotionSystem
{
    private WaypointLocomotionSystemData waypointData;
    public void Move(WaypointLocomotionSystemData waypointData)
    {
        this.waypointData = waypointData;
    }
    public void Tick()
    {
        //Movement code for waypoint...
    }
}
public class TargetAtDistanceLocomotionSystem
{
    private TargetAtDistanceLocomotionSystemData targetAtDistanceData;
    public void Move(TargetAtDistanceLocomotionSystemData targetAtDistanceData)
    {
        this.targetAtDistanceData = targetAtDistanceData; 
    }
    public void Tick()
    {
        //Movement code for target at distance...
    }
}

На этом фрагменте кода мы можем наблюдать:

  • Для каждой стратегии движения существует класс данных
  • Для каждой стратегии движения есть системный класс
  • Каждая системная стратегия имеет доступ к соответствующим данным

Несколько динамических

  • Есть NPC
  • У NPC есть система передвижения.
  • Система передвижения может двигаться разными способами: по некоторым путевым точкам и по цели на расстоянии.
  • У нас есть несколько фрагментов данных, каждый из которых содержит данные для каждого поведения.
  • Данные передаются системе передвижения, чтобы она могла двигаться.

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

public interface ILocomotionSystemData
{
}
public class WaypointLocomotionSystemData : ILocomotionSystemData
{
    public IReadOnlyList<Position> Positions { get; }
}
public class TargetAtDistanceLocomotionSystemData : ILocomotionSystemData
{
    public Position TargetPosition { get; }
    public float Distance { get; }
}
public interface ILocomotionSystemBehaviour
{
    void Tick();
}
public class WaypointLocomotionSystemBehaviour : ILocomotionSystemBehaviour
{
    private readonly WaypointLocomotionSystemData waypointData;
    public WaypointLocomotionSystemBehaviour(WaypointLocomotionSystemData waypointData)
    {
        this.waypointData = waypointData; 
    }
    public void Tick()
    {
        //Movement code for waypoint...
    }
}
public class TargetAtDistanceLocomotionSystemBehaviour : ILocomotionSystemBehaviour
{
    private readonly TargetAtDistanceLocomotionSystemData targetAtDistanceData;
    public TargetAtDistanceLocomotionSystemBehaviour(TargetAtDistanceLocomotionSystemData targetAtDistanceData)
    {
        this.targetAtDistanceData = targetAtDistanceData; 
    }
    public void Tick()
    {
        //Movement code for target at distance...
    }
}
public class LocomotionSystem
{
    private ILocomotionSystemBehaviour locomotionSystemBehaviour;
    public void Move(ILocomotionSystemData locomotionSystemData)
    {
        switch(locomotionSystemData)
        {
            case WaypointLocomotionSystemBehaviour waypointData:
                locomotionSystemBehaviour = new WaypointLocomotionSystemBehaviour(locomotionSystemData);
                break;
case TargetAtDistanceLocomotionSystemBehaviour targetAtDistanceData:
                locomotionSystemBehaviour = new TargetAtDistanceLocomotionSystemBehaviour(targetAtDistanceData);
                break;
            default:
                throw new ArgumentOutOfRange();
        }
    }
    //Method called once per frame
    public void Tick()
    {
        locomotionSystemBehaviour.Tick();
    }
}

На этом фрагменте кода мы можем наблюдать:

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

Выводы

Single следует использовать, когда нужна единая стратегия.

Несколько статических стратегий следует использовать, когда существует несколько стратегий, и они выбраны при первом создании.

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

Дополнительные замечания и рекомендации:

  • Один и Несколько статических имеют одинаковую реализацию, но дизайнерское решение было принято при выборе Несколько статических вместо Несколько динамических. сильный>.
  • Если вы обнаружите, что внедряете некоторые функции, управляемые данными, и в настоящее время у вас есть единая стратегия для реализации, реализуйте ее с помощью Single, а когда требования изменятся, функциональность можно легко преобразовать в Multiple dynamic. .

Дальнейшее чтение в моем личном блоге