【问题标题】:How to implement a Unity-style GetComponent<TYPE>() function in JavaFX?如何在 JavaFX 中实现 Unity 风格的 GetComponent<TYPE>() 函数?
【发布时间】:2021-05-01 15:33:39
【问题描述】:

我正在尝试制作具有实体-组件-系统风格的游戏。实体类有一个不同组件的列表,它们都扩展了 Component 类。如何创建一个函数来返回指定类的组件? (变换、物理等)。来自 Unity 的示例:Transform 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, 0, 0);
    this.tf = player.GetComponent(tf.getClass());
}

getComponent 函数总是返回 null,因为我必须将组件作为参数传递给播放器构造函数以显示我希望播放器拥有的组件类,因此我无法在任何组件中引用播放器构造函数,因为我需要在播放器之前制作虚拟组件。

我怎样才能像 Unity 一样使用简单的 GetComponent();功能?

【问题讨论】:

    标签: java unity3d javafx entity-component-system


    【解决方案1】:

    有很多方法可以或多或少地完成您正在寻找的事情......我不熟悉“实体组件系统”或 Unity,但我构建了一个可能满足您需求的示例。

    它以 Component 类型开始,我为此创建了一个空接口,但您可以将其设为抽象类或任何适合您需要的类型:

        public interface Component {
    
        }
    

    我添加的下一个类是ComponentFactory。一个 ComponentFactory 创建一个固定类型的组件对象,给定一些参数。

        public interface ComponentFactory<IN, T extends Component> {
    
            Class<T> getComponentClass();
            T createComponent(IN input);
        }
    

    下一个类是Lazy,它只是添加了对惰性但只对任何东西进行一次初始化的支持。以后会用到。

        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。 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) {
    
            }
        }
    

    请注意,两个 Component-Implementation 都有一个公共静态 final 变量用于它们自己的工厂。

    最后,我们可以创建我们的玩家实体:

        public static void example() {
            final Entity player = new Entity(List.of(TransformComponent.FACTORY, PhysicsComponent.FACTORY));
            final PhysicsComponent physicsComponent = player.getComponent(PhysicsComponent.class);
        }
    

    【讨论】:

    • 感谢您抽出宝贵时间回答示例。为了让项目中的事情顺利进行,我做了一个临时解决方案,为每个组件创建单独的 get 方法,并使初始化有点麻烦。我稍后会重新审视这些想法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-09-06
    • 1970-01-01
    • 2020-10-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-24
    相关资源
    最近更新 更多