实体组件系统中的事件和事件处理程序

时间:2015-09-13 06:22:19

标签: java events event-handling polymorphism entity

简介

我正在使用实体组件系统处理应用程序。系统在很大程度上依赖于两个抽象类,即组件和处理器,它们共享一对一的关系。每个Processor实现一个基本的Subscriber接口,允许EventManager类在创建新事件时通知它们。

事件

我有一个Event抽象类,它存储事件类型ID,如下所示:

public abstract class Event {

    public static final int NONE = 0;

    private final int eventID;

    public Event(int eventID) {
        this.eventID = eventID;
    }

    public int getEventID() {
        return eventID;
    }
}

然后我有一个扩展Event的Factory类,如下所示:

public abstract class BaseEvents extends Event {

    public static final int TRANSFORM = 1;

    public BaseEvents(int eventID) {
        super(eventID);
    }

    public static class Transform extends BaseEvents {

        private final float rotation,
                tx,
                ty;

        public Transform(float rotation, float tx, float ty) {
            super(TRANSFORM);
            this.rotation = rotation;
            this.tx = tx;
            this.ty = ty;
        }

        public float getRotation() {
            return rotation;
        }

        public float getTx() {
            return tx;
        }

        public float getTy() {
        return ty;
        }
    }
}

我试图通过进一步扩展BaseEvents来允许创建新事件的能力。我主要使用静态内部类来组织。我不想为每个活动创建一个单独的课程。

事件处理程序

为了避免使用EventHandler类对给定处理器中的每个事件进行硬编码处理。我的计划是每个事件都有一个EventHandler类。我再次使用静态内部类进行组织。

这里是EventHandler抽象类:

public abstract class EventHandler {

    public void handle(Event event, Component component) {
    }
}

这里是BaseEventHandlers"工厂"类:

public class BaseEventHandlers extends EventHandler {

    public static class Transform extends BaseEventHandlers {

        @Override
        public void handle(Event event, Component component) {
            BaseEvents.Transform e = (BaseEvents.Transform) event;
            RenderComponent c = (RenderComponent) component;
            c.setRotation(e.getRotation());
            c.setTx(e.getTx());
            c.setTy(e.getTy());
        }
    }
}

示例代码I显示的是RenderComponent / RenderProcessor配对。我们关心转换事件,因为它们告诉我们重新绘制对象的位置。

当EventManager通知RenderProcessor一个新事件时,我们使用事件ID检查Map以获取相应的EventHandler,并调用其handle方法。注意,事件实际上存储在处理器队列中并在更新调用期间处理,如下所示:

...
protected final Map<Integer, EventHandler> eventHandlers = new HashMap<>();
...
    @Override
    public void update() {
        events.stream()
                .forEach(event -> {
                    eventHandlers.get(event.getEventID()).handle(event, components.get(event.getSourceEntityID()));
                });
        events.clear();
    }
...

我的问题

在EventHandler中,我需要将Event和Component抽象类都转换为适当的具体类。我只限于一种类型的活动,但我不能将自己限制在一种类型的组件中。我想要的是使用多态和重载,以便我有类似的东西:

    ...
        public void handle(Event event, RenderComponent component) {
            BaseEvents.Transform e = (BaseEvents.Transform) event;
            component.setRotation(e.getRotation());
            component.setTx(e.getTx());
            component.setTy(e.getTy());
        }

        public void handle(Event event, OtherComponent component) {
            BaseEvents.Transform e = (BaseEvents.Transform) event;
            component.setRotation(e.getRotation());
            component.setTx(e.getTx());
            component.setTy(e.getTy());
        }
    ...

这不会起作用,因为我只能访问抽象类的句柄(事件事件,组件组件)方法(重载似乎并不关心子类)。一个想法是创建一个像Transformable这样的接口,让RenderComponent和OtherComponent实现它。当事件中包含的数据适用于两个组件时,这种方法很有效,但对于某些事件而言,情况很容易发生。

例如,假设我有一个关闭一个名为WindowClosed的窗口的事件。我需要播放声音,绘制关闭动画,并更新打开的窗口数量。这是三个不同的组件/处理器需要处理的一个事件。在这个例子中,唯一可以使用的接口是覆盖每个Component所需的所有方法的接口,在我看来,这会导致凌乱,杂乱的意大利面条代码。

是否有一种聪明的方法可以使用泛型来解决这个问题?

0 个答案:

没有答案