首页 > 解决方案 > 如何实现 Unity 风格的 GetComponentJavaFX 中的 () 函数?

问题描述

我正在尝试制作具有实体组件系统风格的游戏。实体类有一个不同组件的列表,它们都扩展了 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(); 功能?

标签: javaunity3djavafxentity-component-system

解决方案


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

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

    public interface Component {

    }

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

    public interface ComponentFactory<IN, T extends Component> {

        Class<T> getComponentClass();
        T createComponent(IN input);
    }

下一个类是Lazy,它只是添加了对 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。实体现在不会接收在其构造函数中传递的组件,而是它可能在以后用于构造实际组件的工厂。

    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 都有自己的工厂的公共静态最终变量。

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

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

推荐阅读