Search Unity

Custom GetComponent with Generic for nonmonobehaviour class?

Discussion in 'Scripting' started by mahdiii, Aug 20, 2017.

  1. mahdiii

    mahdiii

    Joined:
    Oct 30, 2014
    Posts:
    856
    Hi all
    I have several classes(non monobehaviour) with some same fields.I need to access them into loops.
    How can I implement GetComponent(generic) for them or convert my classes to monobehaviour classes?
     
  2. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    You can't attach non-component classes to game objects, so you can't call GetComponent to get them. You make something inherit MonoBehaviour by adding " : MonoBehaviour" after the class name.

    Code (csharp):
    1. public class MyClassName : MonoBehaviour {
     
  3. mahdiii

    mahdiii

    Joined:
    Oct 30, 2014
    Posts:
    856
    thx but I said "Custom GetComponent" using generic. suppose we implement in c# .net not unity.
     
  4. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    Maybe I don't understand what you're asking. GetComponent already is generic:

    Code (csharp):
    1. var thing = gameObject.GetComponent<MyClassName>();
    But you have to make your class a Component by implementing MonoBehaviour. That's the only way to attach it to a game object, and GetComponent is for getting things attached to a game object.
     
  5. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    This screams interfaces.
     
  6. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,187
    interfaces can't contain fields. Sounds like a base class that inherits from monobehavior might be better if GetComponet is needed.

    @mahdiii What is the purpose of these classes? Do they just contain data? Are they suppose to be attached to a gameobject?
     
  7. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Interfaces can contain properties. Which is close enough to fields as to not make a difference.
     
    io-games and MadeFromPolygons like this.
  8. mahdiii

    mahdiii

    Joined:
    Oct 30, 2014
    Posts:
    856
    you don't know my question:)
    I said you forgot getcomponent function and unity. Suppose I write in c#(console application or winform) for one application.
    I have some classes with some same fields(that are interface objects like IDisplayable,IAttackable,IMovable,...)
    one class has (IDisplayable,IAttackable,IMovable), other class (IDisplayable,IMovable) other class (IAttackable,IMovable) etc
    I know I can use interface inheritance like below instead of component based design pattern:
    Code (CSharp):
    1. public class CSoldior1:IMovable,IAttackable{
    2. public void Move(){
    3.  
    4. }
    5. public void Attack(){
    6.  
    7. }
    8. }
    9.  
    10. public class CSoldior2:IMovable,IAttackable,IDisplayable{
    11. public void Move(){
    12.  
    13. }
    14. public void Attack(){
    15.  
    16. }
    17. public void Display(){
    18.  
    19. }
    20. }
    21. public class CSoldior3:IMovable,IDisplayable{
    22. public void Move(){
    23.  
    24. }
    25. public void Display(){
    26.  
    27. }
    28. }
    29.  
    30.  
    but it is not component based design pattern and can create duplicate codes (methods like move method etc)
    So I would like to define interface fields into the classes:
    Code (CSharp):
    1. public class Soldior1{
    2. IMovable m_movable;
    3. IAttackable m_attackable;
    4. }
    5. public class Soldior2{
    6. IMovable m_movable;
    7. IAttackable m_attackable;
    8. IDisplable m_displayable;
    9. }
    10. public class Soldior3{
    11. IMovable m_movable;
    12. IDisplable m_displayable;
    13. }
    14.  
    and loop for some method like below:
    Code (CSharp):
    1. foreach(IMovable movable in MovableList){//Movablelist contain IMovable interfaces of different classes
    2. movable.move();
    3. }
    Therefore I need to use custom getcomponent to access to special interface fields of different classes (for example all IMovable fields)
    I read somewhere that we can use generics and dynamic to get components in classic classes.
     
  9. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    I honestly don't think you do either. :p

    Are you trying to duplicate the component architecture of Unity in pure C#? There is probably a library for that. In that case GetComponent will look something like this:

    Code (CSharp):
    1. // GameObject analouge
    2. public class ComponentContainer {
    3.     List<Component> components ();
    4.  
    5.     public T GetComponent<T> () where T : Component {
    6.         foreach(Component component in components){
    7.             T componentAsT = component as T;
    8.             if (componentAsT != null) return componentAsT;
    9.         }
    10.     return null;
    11.     }
    12. }
    13.  
    14. // UnityEngine.Component analouge. All components inherit from this
    15. public class Component {
    16. }
    You'll also need to make analouges for all the other useful methods Unity provides, such as AddComponent, GetComponents, Destroy and so on.
     
    eses, mahdiii and MitchStan like this.
  10. mahdiii

    mahdiii

    Joined:
    Oct 30, 2014
    Posts:
    856
    Perfect Thank you. it is really I want.
    So I firstly need to create a component class and Movable,Attackable etc classes inherit from it. Then keep the list of the components and then access to them with custom getcomponent method.
    Is it the only way to prevent duplicate codes(duplicate methods) in different classes and can loop for special method of classes?
     
  11. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,187
    Well, no...You can create a base class, have other classes inherit from the base class, and then create a list of the base class which you can loop through. The base class would define shared methods while the other classes would have code unique to them. Which honestly is no different then what you are trying to do...

    Code (CSharp):
    1. public class Animal: Monobehaviour
    2. {
    3.    public void Move()
    4.    { }
    5. }
    6.  
    7. public class Dog: Animal
    8. { }
    9.  
    10. public class Cat: Animal
    11. { }
    12.  
    13. public List<Animal> myAnimals; //Can add dog and cat and loop through and access Move method.
    You can then use GetComponent on Animal to get access to the shared code. You will have to cast it to get access to stuff unique to dog or cat, but that's true of anything like this. Also note this allows you to put cat and dog on gameobjects in the scene.
     
    Last edited: Aug 22, 2017
  12. mahdiii

    mahdiii

    Joined:
    Oct 30, 2014
    Posts:
    856
    plz read accurately, it was not my problem! yes in your simple example it is ok but I said I have several classes with some same object fields(components), if I use inheritance I will definitely have some duplicate codes(methods). it is the classic problem of inheritance

    if your meaning is virtual or abstract functions like move in animal class, you need to override or implement it for all children that can create duplicate codes. So I prefer to implement components only one time and use them inside classes
     
  13. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,187
    No, you don't duplicate move. It's in the base class. That's why your list is of type Animal and not cat/dog. You can clearly see I didn't implement move in cat or dog and there is no override.

    Technically, your base class can implement all your "components" and your other classes can inherit those as well.

    @Kiwasi method will work just fine for you though, so no reason to worry about it if that is what you want.
     
    Last edited: Aug 22, 2017
    mahdiii likes this.
  14. mahdiii

    mahdiii

    Joined:
    Oct 30, 2014
    Posts:
    856
    plz write more complete, I see only a move method in the parent and the children dog and cat have it
     
  15. mahdiii

    mahdiii

    Joined:
    Oct 30, 2014
    Posts:
    856
    "Technically, your base class can implement all your "components" and your other classes can inherit those as well."
    you say we can implement all components in the base class and because the children inherit all components?!
    I said one children has several components and the other children has different components with some same components
    plz reimplement my classes soldiers above/ thx

    my problem:
    I have soldiers that have different types. all of them can move but differently (different implementation), some of them can attack but differently and some of them can display something but differently. hint: it is possible some soldiers move the same but attack differently or other combinations
     
    Last edited: Aug 22, 2017
  16. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    What are you asking?

    The set up you have with Soldier is good. It has a move interface field that you can access to make it move. You're trying to come up with a generic way to get that interface? Or other interfaces like it?
     
    mahdiii likes this.
  17. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    Why don't you just use actual components? Just make them MonoBehaviours and drag and drop the ones you want onto the different soldiers.
     
    Kiwasi and mahdiii like this.
  18. Deleted User

    Deleted User

    Guest

    For now this is probably the answer OP needs.

    OP, I've learned one thing doing this for a year: it's that Unity can handle hundreds of BS MonoB scripts. I mean it, hundreds. Some people just do whole involved games with every script being a MonoB all the way down to little trivial single-use things like a "destroy myself on command" script.

    The key takeaway is that if a MonoB does not include an event-subscribing function (Update, FixedUpdate) it doesn't get added to whatever call stack calls all those update event functions you write.

    I know it seems like it will become unruly fast, but just keep separation of concerns in mind and "encapsulate" each script to its entity. For example let the UI require a plug reference to the player (for obvious usage); don't write embedded logic in the player that updates the UI.

    This is the pattern I like, personally.

    Several interfaces. ISoldierMovement, ISoldierAttack, ISoldierDisplay. A soldier gets one script of each of these types. Think of interfaced scripts as sharing a type. You can even make new ones or change them on the fly. Each soldier has one MonoB, its MonoB calls some function updating each non-mono-behavior script.

    The only thing this laves you high and dry on is that your interface scripts can't start Unity's coroutines. If you take the time to learn IEnumerable though, you can just do the same thing yourself.
     
    Last edited by a moderator: Aug 22, 2017
    mahdiii likes this.
  19. mahdiii

    mahdiii

    Joined:
    Oct 30, 2014
    Posts:
    856
    thank you all. yes with components it is ok but Brathnann said we can use inheritance like above but I think it can generate duplicate codes

    because I don't need trigger functions like update,start,onEnable,etc. I don't want to inherit from monobehaviour only for using getcomponent. I maybe can use component unity class that has getcomponent function. The other problem is that you can only inherit from one class, my soldiers inherit from a base soldierBase class but it can be solved with inheritance branch : Monobehaviour->SoldierBase->Soldiers classes

     
  20. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    Then don't use them?

    Soldier should be a prefab that is composed of the different components that a soldier would need to do soldier-like things.
     
  21. mahdiii

    mahdiii

    Joined:
    Oct 30, 2014
    Posts:
    856
    yes I would like to access to all special interface fields like IMovable of all classes . So I can call the function move simply inside "for" loop that Kiwasi answered
     
  22. mahdiii

    mahdiii

    Joined:
    Oct 30, 2014
    Posts:
    856
    so I use monobehaviour components :) but it was cool to implement getcomponent and other methods like addcomponent etc yourself in another non unity applications
     
  23. Deleted User

    Deleted User

    Guest

    Consider that the solider itself may instantiate those interface scripts, and is a MonoB. In the pattern I described the interfaced movement, attack, and UI scripts can receive a reference to the instantiating MonoB through constructor injection.

    They can then have the solider get any component for them by calling the soldier's GetComponent. I think. Usually when I make something its initialize or constructor collects all references.

    Again though frankly I would just build something and refactor it when it can't run, because I've seen literally four hundred MonoB's all running multiple vector normalization functions in a usable way.
     
    mahdiii likes this.
  24. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    But you do need access to a game object, transform, the ability to call GetComponent, etc. You shouldn't waste time writing a new version of Component from scratch if the only point of it is to have less functionality than the existing one.


    Like Kelso said, I meant you should make the soldier a game object and put different components on it; that's the Unity way. You're probably going to need some kind of Component attached to the actual soldier object at some point anyway; how do you plan on linking your non-component soldier class to its game object and other components?
     
    mahdiii likes this.
  25. mahdiii

    mahdiii

    Joined:
    Oct 30, 2014
    Posts:
    856
    Surely I need one monobehaviour Soldier controller script to handle gameobject but I dont need to make all monobehaviour components.
    they can be all non monobehaviours and create into one script with "new" operation. it is definitely possible but need to implement new getcomponent and etc functions.

    Code (CSharp):
    1. public class CSoldier1:Monobehaviour{
    2. IMovable m_movable;
    3. IAttackable m_attackable;
    4. void Start(){
    5.    movable=new CMovableClass();
    6.    attackable=new CAttackable(55);
    7. }
    8. }
     
    Last edited: Aug 22, 2017
  26. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    No you don't.

    Code (csharp):
    1.  
    2. abstract class Entity : MonoBehaviour
    3. {
    4.     protected IMovable move;
    5.  
    6.     public IMovable GetMove() { return move; }
    7. }
    8.  
    9. public class Soldier : Entity
    10. {
    11.     // use GetMove() instead of GetComponent<IMovable>();
    12. }
    13.  
    With that said - I would still challenge the notion that you need a Soldier manager class at all.
     
    mahdiii likes this.
  27. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    But how is "CMovableClass" going to access its transform to actually move itself, if it doesn't have a reference to its own game object?

    I'm not saying it can't be done, I just don't see why you would want to basically rewrite the entire component system for no reason. If the only reason is "I don't need Start and Update on some of them", then that's not a very good reason... you don't have to implement Start and Update. Just leave them out.
     
    mahdiii likes this.
  28. mahdiii

    mahdiii

    Joined:
    Oct 30, 2014
    Posts:
    856
    :|
    My classname is soldiercontroller, it is not manager. it is a soldier class. I corrected it.
    he said we need all components are mono I said no. only one class soldier must be mono.
    yes it is completely true but need to use unity getcomponent() or custom getcomponent (Kiwasi said) to access to all interface fields of different classes. Because you wrote IMovable to the base class, yes we can easily call Getmove().move() for all children classes inside for loop but the problem was not that.some classes have attack class some not or etc so we must use getcomponent
     
    Last edited: Aug 22, 2017
  29. mahdiii

    mahdiii

    Joined:
    Oct 30, 2014
    Posts:
    856
    I didnt write accurately. You can send as a parameter of the class constructor whatever you want like transform
     
  30. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    Again, no you don't :)

    GetMove is your GetComponent in this case. If it returns null, then whatever you're trying to access doesn't exist....