Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unity: GetComponent<BaseClass<T>> - are there any workarounds for this?

Tags:

unity3d

If an object has a component with a baseclass BaseClass<T>, calling GetComponent<BaseClass<T>>() will not return that component. The generic argument seems to throw it off, as a BaseClass without using generics will correctly return the derived class as a component when GetComponent<BaseClass>() is called.

Does anyone know a solid workaround for this? The use of a generic arg in this class is somewhat important, so I'd obviously rather not re-write the program's class structure just to accommodate this.

Here's a rough sketch of the classes in question

//the base class that I'd like to be able to fetch the subclasses of using GetComponent
public abstract class BaseUIClass<T> : MonoBehaviour where T :BaseEntity {}

//EntityType1&2 are derived from BaseEntity
public class DerivedUIClass1 : BaseUIClass<EntityType1> {}
public class DerivedUIClass2 : BaseUIClass<EntityType2> {}

BaseUIClass has this method:

public virtual void Setup(T entity) {}

Which needs to be called shortly after the component is added to a GO.

EDIT:

Effectively what I'm looking to do is the following, without having to hard-code in (I want to actually make use of the generic args I've defined)

if(uiClassObj is typeof(DerivedUIClass1) go.GetComponent<BaseUIClass<EntityType1>>();

else if(uiClassObj is typeof(DerivedUIClass2) go.GetComponent<BaseUIClass<EntityType2>>();
//etc

But considering that there is a component of type BaseUIClass<BaseEntity> on the go, and the two+ derived classes I'm interested in are defined by DerivedUIClass1<EntityType1> and DerivedUIClass2<EntityType2>, the conversion should surely just be implicit?

like image 831
Cerzi Avatar asked Mar 07 '18 13:03

Cerzi


2 Answers

You can't do what you want the way you want, because Unity doesn't accept components which are generic classes.

I.E., if you have:

public class MyGenericClass<T> : MonoBehaviour {}

you will not be able to add it as a component unless you specify T via a subclass inheriting from it:

public class MySpecifiedClass : MyGenericClass<[specificType]> {}

So, to solve your problem, you should simply implement a specific interface for everything that should be done when the base class is added as a component. I'll show you an example with float and int derived types, you can extend easily to any type you need.

BaseClass

using UnityEngine;

public interface ISetup {
    void CallSetup();
}

public class BaseClass<T> : MonoBehaviour, ISetup {
    public T myEntity;

    public void CallSetup() {
        Setup(myEntity);
    }

    private void Setup(T entity) {
        Debug.Log(entity);
        //Your setup code
    }
}

Your components classes

public class BaseClassInt : BaseClass<int> {
    private void Awake() {
        myEntity = 25;
    }
}
public class BaseClassFloat : BaseClass<float> {
    private void Awake() {
        myEntity = 10.6f;
    }
}

Code that gets the interface and calls Setup()

var componentsWithSetup = GetComponents<ISetup>();
foreach (var component in componentsWithSetup) {
    component.CallSetup();
}
like image 85
Galandil Avatar answered Sep 30 '22 13:09

Galandil


One workaround would be to use a specific type instead of a generic type at the top level for each type that you need the base class for.

For example:

public class SpecificToFirstTypeClass : BaseClass<FirstType>
{
}

and then use GetComponent<SpecificToFirstTypeClass>

Based on a suggestion from this answers.unity.com question

like image 41
silleknarf Avatar answered Sep 30 '22 14:09

silleknarf