Download

Component based design in Unreal

I really like the idea of using components but there is one thing that bugs me a little bit.

To access components on actors I have to use


UActorComponent* AActor::GetComponentByClass(TSubclassOf<UActorComponent> ComponentClass)
{
	UActorComponent* FoundComponent = NULL;
	for (UActorComponent* Component : OwnedComponents)
	{
		if (Component && Component->IsA(ComponentClass))
		{
			FoundComponent = Component;
			break;
		}
	}

	return FoundComponent;
}

Which seems that it could become a bottleneck in the future. At the moment I am implementing one interface per component like this


class UHealthComponent;
UINTERFACE(MinimalAPI)
class UHealthInterface : public UInterface
{
	GENERATED_UINTERFACE_BODY()
};
 
class IHealthInterface
{
	GENERATED_IINTERFACE_BODY()
 
	virtual UHealthComponent* Get() const;
};

Then I can cast the actor to this interface and get the component in constant time. But in Unreal I have to create one interface per file and this would also be quite annoying to maintain. I could auto generate the interface files with a tool but I would like to avoid this.

Initially I thought I could just create a templated interface like



...
template<typename T>
class IComponentInterface
{
	GENERATED_IINTERFACE_BODY()
 
	virtual T* Get() const;
};



UCLASS(config=Game)
class AShooterCharacter : public ACharacter, public IComponentInterface<UHealthComponent>

but templates don’t play nice with virtual functions in C++.

Technically this would be premature optimization because I don’t even know if it might become a bottleneck in the future but if it would I would have to do a lot of refactoring.

So I am wondering how you are using components?

Typically if I need a component reference I do it once on init, beginplay or possess and cache it to a weak pointer.

Yes that is what I also do, but I am talking about actors where you don’t have any information. For example


void AShooterProjectile::OnHit(AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
{

	if ((OtherActor != NULL) && (OtherActor != this) && (OtherComp != NULL))
	{

		ApplyToComp<IHealthInterface, UHealthComponent>(OtherActor, &](UHealthComponent* HealthComponent){
			HealthComponent->SetHealth(HealthComponent->Health - 20);
		});
		Destroy();
	}
}

I wrote a simple helper function that casts the actor to an ‘IHealthInterface’ and retrieves the component.

But I think I will just use ‘GetComponentByClass’ instead of creating tons of interfaces. Maybe this could be automated with UHT but I haven’t looked at it.

It could look like


UPROPERTY(WithInterface)
USomeComponent* Comp;

...

Cast<ISomeComponentInterface>(OtherActor);