Добавление пользовательских постоянных / временных поведений к базовому типу - PullRequest
2 голосов
/ 29 января 2012

У меня есть базовый интерфейс ядра под названием IComponent.

public interface IComponent
{
    void Initialize();

    void Update();

    void Draw(Color tint);

    BoundingBox BoundingBox { get; }

    bool Initialized { get; }
}

Он используется любым классом, который будет отображаться (кнопка, текст, текстура, ...)

Я хочуразработать набор эффектов, которые можно применить к любому IComponent, например:

Shake(IComponent component, TimeSpan duration)

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

Translate(IComponent component, Vector2 destination, TimeSpan timeToReach)

-> Заставляет компонент перемещаться в указанное место назначения через определенное время.Он работает путем постепенного смещения компонента при каждом обновлении.

Вы можете представить себе больше ...

Итак, предположим, что я хочу, чтобы определенный класс (Texture: IComponent) встряхивал при перемещении к определенномуточка.Я подумал о возможном использовании шаблона декоратора следующим образом:

Создать текстуру => Обернуть ее встряхиванием (продолжительность 5 секунд) => Обернуть ее переводчиком (продолжительность 10 секунд)

но есть некоторые проблемы с этим.

Прежде всего, обертка Shake прекрасно работала бы в одиночку со статической стационарной текстурой, но в сочетании с Translate она не работала бы, поскольку продолжала возвращать текстуру в исходное положение, и онане будет правильно переводить.

Во-вторых, хотя перевод займет 10 секунд, вибрация займет всего 5 секунд, поэтому впоследствии я не знаю, как автоматически удалить упаковку Shake изнутри цепи, ии, наконец, удалите Translate, когда все будет готово, оставив после себя оригинальную текстуру.

В-третьих, шаблон декоратора скрывает любые специфические функции обернутого объекта, поэтому после обертывания текстуры с помощью Shake я не смогу вызватьsetPixelColor () для Texture, если только я не создаю вторую прямую ссылкуПеред тем, как обернуть текстуру, добавьте текстуру.

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

Примечание : На практике я могу применить эти эффекты, вероятно, к 2% всех созданных объектов, которые являются IComponent.

Ответы [ 2 ]

1 голос
/ 04 февраля 2012

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

В вашем примере и встряхивание, и перевод - это анимационные эффекты.Все анимации нуждаются в некоторой форме продолжительности и применяются к компоненту.Что может привести к следующему первому примеру:

public interface IAnimationEffect
{
    IComponent targetComponent;
    int Duration { get; set; }
}

Длительность анимации может показаться хорошим начальным подходом, однако, когда вам нужно объединить анимацию, каждая анимация должна манипулировать BoundingRect своего инкапсулированного IComponent.за тот же период времени, что и другие анимации.Чтобы иметь возможность складывать анимации, подобные этим, необходимо проинструктировать методы рисования для эффектов анимации, чтобы нарисовать конкретный кадр анимации.

Улучшенная версия интерфейса IAnimationEffect будет выглядеть так:

public interface IAnimationEffect
{
    IComponent targetComponent;
    int StartFrame { get; set; }
    int EndFrame { get; set; }
    void CalculateFrame(int frame);
}

Какой бы класс не отвечал за отрисовку ваших IComponents (давайте назовем его сейчас DrawingEngine), теперь также отвечает за удержаниевнутренний список всех применимых эффектов анимации.Он также должен иметь некоторую временную шкалу и логику рендеринга в кадрах в секунду, чтобы можно было выполнять вычисления для конкретного кадра анимации.

public class ShakeAnimationEffect : IAnimationEffect
{
    public IComponent TargetComponent { get; set; }
    public int StartFrame { get; set; }
    public int EndFrame { get; set; }
    // Some shake specific properties can be added, to control the type of vibration etc 
    // (could be a rotating vibration an updown shake), but these really should have dedicated classes of their own.

    public void CalculateFrame(int frame)
    {
        // your maths manipulations for calculating the bounds of the IComponent go here
    }
}

public class TranslateAnimation : IAnimationEffect
{
    public IComponent targetComponent { get; set; }
    public int StartFrame { get; set; }
    public int EndFrame { get; set; }
    public int TranslateX { get; set; } 
    public int TranslateY { get; set; }

    public void CalculateFrame(int frame)
    {
        // your maths manipulations for calculating the bounds of the IComponent go here
    }
}

Как предложено выше, ваши классы AnimationEffects не несут никакой ответственностиза рисование IComponents, это остается ответственностью DrawingEngine.

То, что вам нужно было бы ввести непосредственно перед вашим DrawingLoop, - это цикл для запуска всех эффектов анимации, все, что нужно сделать, - это обновить границы объектов, с которыми связан эффект анимации.Затем вы продолжаете рисовать объект как обычно.

Предположим, что у вас есть что-то вроде этого:

        // suppose your drawing engine has the following:
       List<IComponent> components = new List<IComponent>();

        //now just add the following 
       List<IAnimationEffect> animationEffects = new List<IAnimationEffect>(); 

        // create some animation effects and register them to your list
        animationEffects.Add(new ShakeAnimationEffect
          {
              TargetComponent = Lorry,
              StartFrame = 0,
              EndFrame = 150, //At 30 frames per second, this would be a 5sec animation
          }
        );

        //sample pseudecode
        RunAnimationCalculations();
        RunDrawingLoop();

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

Удачи!

0 голосов
/ 29 января 2012

Можно примерно так:

public Shake : ITransformation 
{
}

public Translate : ITransformation
{
}

IComponent интерфейс определяет также набор примененных к нему преобразований:

public interface IComponent
{
    public IEnumerable<ITransformation> GetTransformation();
}

в какой-то конкретной реализации Component

public MyCompo : IComponennt 
{
    public Draw(Color tint) {

        //apply here transformations available in list of GetTransformation()

        .... 
        // draw
    }
}

Просто идея.

Надеюсь, это поможет.

...