问题描述
我正在尝试使用实体组件系统风格制作游戏。实体类有一个不同组件的列表,所有组件都扩展了 Component 类。如何使函数返回指定类的组件? (变换、物理等)。来自 Unity 的示例:转换 tf = player.GetComponent();
@SuppressWarnings("unchecked")
public <T> T GetComponent(Class<T> T)
{
for (Component component : components)
{
if (component.getClass() == T)
{
System.out.println("Returned a component.");
return (T)component;
}
}
return null;
}
在 PhysicsComponent 中,我试图像这样获取对 TransformComponent 的引用:
public PhysicsComponent(Entity player)
{
this.tf = new TransformComponent(0,0);
this.tf = player.GetComponent(tf.getClass());
}
getComponent 函数总是返回 null,因为我必须将一个组件作为参数传递给播放器构造函数以显示我希望播放器拥有的组件类,所以我不能在任何组件中引用播放器构造函数,因为我需要在播放器之前制作虚拟组件。
我如何像 Unity 那样使用简单的 GetComponent() 来实现它;功能?
解决方法
有很多方法可以或多或少地做您正在寻找的事情....我不熟悉“实体组件系统”或 Unity,但我构建了一个可能适合您需求的示例。
它从一个组件类型开始,我为此创建了一个空接口,但您可以将其设置为抽象类或任何适合您需要的类:
public interface Component {
}
我添加的下一个类是 ComponentFactory
。一个 ComponentFactory 创建一个固定类型的组件对象,给定一些参数。
public interface ComponentFactory<IN,T extends Component> {
Class<T> getComponentClass();
T createComponent(IN input);
}
下一个类是 Lazy
,它只是添加了对 Lazy but only-one 初始化的支持。以后会用到。
public static class Lazy<T> {
private volatile T value;
private final Supplier<? extends T> supplier;
public Lazy(Supplier<? extends T> supplier) {
this.supplier = supplier;
}
public T getValue() {
if (this.value == null) {
synchronized (this) {
if (this.value == null) {
this.value = this.supplier.get();
}
}
}
return this.value;
}
}
下一个类是Entity
。 Entity 是您似乎已经拥有的类之一,但您很挣扎,因为 Entity 需要将其组件传递给构造函数,但组件也需要在构造函数中传递的 Entity。实体现在不会接收在其构造函数中传递的组件,而是接收工厂,它可以在以后的某个时间点使用它来构造实际的组件。
public static class Entity {
private final Map<Class<? extends Component>,Lazy<Component>> components;
public Entity(List<ComponentFactory<? super Entity,?>> componentFactories) {
// do all of the basic construction
final Map<Class<? extends Component>,Lazy<Component>> components = new HashMap<>();
for (ComponentFactory<? super Entity,?> factory : componentFactories) {
components.put(factory.getComponentClass(),new Lazy<>(() -> factory.createComponent(this)));
}
this.components = Collections.unmodifiableMap(components);
}
public <T extends Component> T getComponent(Class<T> componentClass) {
final Lazy<Component> lazy = this.components.get(componentClass);
final T component;
if (lazy != null) {
component = componentClass.cast(lazy.getValue());
} else {
component = null;
}
return component;
}
}
示例
作为一个例子,我创建了两个类“TransformComponent”,它接受一个实体,而“PhysicsComponent”接受一个实体并使用该实体来接收给定实体的 TransformComponent。
public static class PhysicsComponent implements Component {
public static final ComponentFactory<Entity,PhysicsComponent> FACTORY = new ComponentFactory<>() {
@Override
public Class<PhysicsComponent> getComponentClass() {
return PhysicsComponent.class;
}
@Override
public PhysicsComponent createComponent(Entity input) {
return new PhysicsComponent(input);
}
};
private final TransformComponent tf;
public PhysicsComponent(Entity player) {
this.tf = player.getComponent(TransformComponent.class);
}
}
public static class TransformComponent implements Component {
public static final ComponentFactory<Entity,TransformComponent> FACTORY = new ComponentFactory<>() {
@Override
public Class<TransformComponent> getComponentClass() {
return TransformComponent.class;
}
@Override
public TransformComponent createComponent(Entity input) {
return new TransformComponent(input);
}
};
public TransformComponent(Entity player) {
}
}
请注意,两个组件实现都有一个公共静态最终变量用于它们自己的工厂。
最后,我们可以创建我们的玩家实体:
public static void example() {
final Entity player = new Entity(List.of(TransformComponent.FACTORY,PhysicsComponent.FACTORY));
final PhysicsComponent physicsComponent = player.getComponent(PhysicsComponent.class);
}