Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

How can I create factory pattern with monobehavior components

Discussion in 'Scripting' started by WizardGameDev, Jan 6, 2015.

  1. WizardGameDev

    WizardGameDev

    Joined:
    Jul 25, 2012
    Posts:
    62
    Greetings,

    I'm attempting to implement a basic factory pattern in which I could have several different classes all from the same super class and I want to assign them at runtime. So depending on the user selection for example I want my 'mage' object to be one of several sub classes. Yet I want my component to have the generic name 'mage' regardless of which sub class I use.

    So when I'm trying to reference my class I need to use the generic reference GameObject.GetComponent<Mage>(). But how can I cast my "BattleMage" class or "FireMage" class into that generic container at runtime? When I use generic c# design patterns this is easy for me as I can just recast the object in the constructor. When I try this in Unity though it doesn't like using new with monobehavior. Is there a way for Instantiate to do this with the same functionality?

    Obviously there is a simple pattern specific to Unity that makes this possible and I'm hoping someone out there has an idea of the best way to implement something like this.

    Many thanks in advance.

    UPDATE: I'm not 100% sure but I think the "AS" clause on the Instantiate method is the key to implementing this sort of pattern. I discovered it looking at how I was using pooling to do some other things in my game. It's amazing how many times you end up learning to do the same things when trying to tie everything together.
     
    Last edited: Jan 6, 2015
  2. WizardGameDev

    WizardGameDev

    Joined:
    Jul 25, 2012
    Posts:
    62
    I've had a time to look a bit more into this and find myself going more in circles.

    The problem with using Instantiate to create the GameObject is that it is the entire prefab. What I'm needing is to actually swap out a component on the gameobject but not change out the name of that component.... I need the name to remain the same so my code doesn't have to be changed in a ton of places just because I'm swapping out one of the components.

    So that is probably the base question. "How do you swap out a component with an alternate class will still using a generic reference for that component?"

    For example... I need a consistent call to GameObject.GetComponent<Mage>()... This is what I need to use throughout all the interfaces that work with my mage object. But I don't want just one type of Mage. I may have four different classes just to start out (FireMage, IceMage, ConjuringMage, EarthMage). Because I use invoke repeating a lot through my classes they inherit from monobehavior which I believe is what kills the pure GOF factory pattern and other straight forward patterns that would solve this issue.

    Now one potential hack is to create a switch that would find the right component on a generically named gameobject. I suppose this is a workable solution but a better patter would be ideal. Hopefully this pseduo code explains better what I'm trying to accomplish.


    switch (GlobalMageType):
    {
    case MageType.FireMage:
    oObj = GameObject.Find("Mage").GetComponent<FireMage>();
    case MageType,IceMage:
    oObj = GameObject.Find("Mage").GetComponent<IceMage>();
    case MageType,ConjuringMage:
    oObj = GameObject.Find("Mage").GetComponent<ConjuringMage>();
    case MageType,EarthMage:
    oObj = GameObject.Find("Mage").GetComponent<EarthMage>();
    }

    oObj.CastSpell() // Now casts spell with the correct subclass of mage rather than super class.

    I know there has to be an easier way to accomplish this behavior and I'm missing something very simple to solve this problem. Any help is greatly appreciated.
     
  3. Glurth

    Glurth

    Joined:
    Dec 29, 2014
    Posts:
    109
    I would use the single super class Mage in all the GetComponent<Mage> calls.
    Each sub-class would OVERRIDE the CastSpell() function. So that if the Mage object returned by GetComponent was instantiated as a FireMage then, oObj.CastSpell() would call the firemage version of the function, automatically.
    (this is my favorite part of inheritance)
     
    Mycroft likes this.
  4. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    Assuming a base class called Mage that derives from MonoBehaviour and then specific mage types that derive from Mage.

    Code (csharp):
    1.  
    2. public static T GetMageComponent<T>(this GameObject gameObject) where T : Mage
    3. {
    4.     Mage mage = gameObject.GetComponent<Mage>();
    5.     if (mage != null)
    6.         return mage as T;
    7.  
    8.     return null;
    9. }
    10.  
    Usage
    Code (csharp):
    1.  
    2. IceMage iceMage = someGameObject.GetMageComponent<IceMage>();
    3. if (iceMage != null)
    4. {
    5.     // I'm an Ice Mage!
    6. }
    7.  
     
  5. Glurth

    Glurth

    Joined:
    Dec 29, 2014
    Posts:
    109
    Here is a sample of what I mean:

    Code (CSharp):
    1.  
    2.  
    3. class Mage: MonoBehaviour
    4.     {
    5.         public virtual void CastSpell()  
    6.         {
    7.             UseMana ();
    8.         }
    9.     }
    10.     class FireMage : Mage
    11.     {
    12.         public override void CastSpell()
    13.         {
    14.             Fireball ();
    15.             base.CastSpell ();
    16.         }
    17.     }
    18.     class IceMage : Mage
    19.     {
    20.         public override void CastSpell()
    21.         {
    22.             IceRay ();
    23.             base.CastSpell ();
    24.         }
    25.     }
    26.     //example usage
    27.     class test:MonoBehaviour
    28.     {
    29.         Mage player1;
    30.         Mage player2;
    31.         void Start()
    32.         {
    33.             player1 = new FireMage ();
    34.             player2 = new IceMage ();
    35.         }
    36.         void Update()
    37.         {
    38.             player1.CastSpell ();//  this will call Fireball(), then UseMana()
    39.             player2.CastSpell ();//  this will call Iceray(), then UseMana()
    40.  
    41.         }
    42.     }
    43.  
    44.  
     
    Mycroft likes this.
  6. WizardGameDev

    WizardGameDev

    Joined:
    Jul 25, 2012
    Posts:
    62
    Many thanks for the help.

    This I liked because it is simple but for some reason I get back the super class when I try to 'upcast' (i think that is the technical term) the sub class. Here is my code:

    // Get the main game object in the scene
    GameObject oMage= GameObject.Find ("MageCharacter");

    // Let's add a fire mage component for now to test.
    oMage.gameObject.AddComponent<FireMage>();

    // Later somewhere else in the system I want to fire the spell using the generic mage.
    Mage obj = oMage.GetComponent<Mage> ();

    obj.CastSpell(); // This is calling the castspell in the Mage class, not the FireMage class I have added to the gameobject.

    Sadly, it looks like generics maybe the only way to go.


    UPDATE -- FIXED IT!

    My mistake here was not having the virtual keyword on the method of the superclass and the override method in the subclasses for the method. Now that I have that in place it looks like I can make it work as I had hoped.

    I'd still like to implement this with generics. I've read in a few design patterns that upcasting like this has draw backs and isn't exactly the best of practices. Still it helps a lot with encapsulation and decoupling in other parts of the system so it is a big step forward from where I was currently at. This well help me implement my mage minions as well.
     
    Last edited: Jan 6, 2015
  7. WizardGameDev

    WizardGameDev

    Joined:
    Jul 25, 2012
    Posts:
    62
    Many thanks to both you guys for helping me with this problem. The generics example by KelsoMRK is also very insightful and I hope to move to this design. Would there be advantages to generics over the upcasting?

    I'm assuming that from a design perspective you limit yourself to one superclass and that makes it less flexible and more tightly coupled than if you have a generic in which you could perhaps have a Fighter class could swap into the framework. This gets me where I want to be for now and I think that once the game is released I'll know how to go back and build it right! lol
     
  8. Kirk Clawson

    Kirk Clawson

    Joined:
    Nov 4, 2014
    Posts:
    65
    The only thing the Generics gets you is that it's doing the casting for you. Adding a fighter class will still require a new base class (unless your fighters will be casting spells)
     
  9. WizardGameDev

    WizardGameDev

    Joined:
    Jul 25, 2012
    Posts:
    62
    But wouldn't that mean that if I'm using a new base class that the override isn't going to work? The override and upcasting of the object require that I call use the superclass "Mage" when I hook to it do call the methods and look at properties. If it were changed over to generics then I could pass in an object that isn't from that same base class such as a "Fighter".

    For this specific example it is probably not applicable as a Fighter class should likely still derive from another base class with common functionality for it to interface in the first place.

    I'm just trying to wrap my head around the specifics of overriding vs generics for future design patterns. It would seem if you had a more generic function that would work with a great deal many classes you would for sure want to go generic because overriding the methods requires a common base class.

    Really appreciate the help.
     
  10. WizardGameDev

    WizardGameDev

    Joined:
    Jul 25, 2012
    Posts:
    62
    Many thanks for this. I'm now trying to figure out exactly how to go the "other way"... referencing the component generically while initializing it with a specific subclass.


    So I would want to add my component by using something like AddMageComponent that looks like this...

    Code (csharp):
    1.  
    2. public static T AddMageComponent<T>(this GameObject gameObject) where T : Mage
    3. {
    4.     Mage mage = gameObject.AddComponent<T>();
    5.     if (mage != null)
    6.         return mage as T;
    7.  
    8.     return null;
    9. }
    10.  
    But I'm confused on syntax give the T : Mage in the where. Can I just plug a generic object in using the AddComponent?
    Usage
    Code (csharp):
    1.  
    2.  
    3. // Code to create
    4. IceMage oIceMage= someGameObject.AddMageComponent<IceMage>();
    5.  
    6. // Code to lookup with standard Mage but really get the IceMage back.
    7. Mage oMage = someGameObject.GetMageComponent<Mage>();
    8.  
    9. oMage.CastSpell();   // Here I would want to call the cast spell for my IceMage.
    10.  
    11.  
    I like this generics design because it would be useful for other patterns.But then I get confused by the where T: Mage in the Add/Get methods that would access the instance.

    What if I had a whole bunch of different objects I'd like to be able to plug into that component slot and reference generically as long as it had an 'interface' with the methods CastSpell, Retreat, etc?

    I think that hardest part for me to grasp is taking the clean design patterns from gang of four and others and then integrating them within Unity's component and prefab inheritance models.
     
  11. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    Yeah that would work. It just prevents silliness like this
    Code (csharp):
    1.  
    2. Collider iceMage = someGameObject.AddMageComponent<SphereCollider>();
    3.  
    That'll fail at compile time because SphereCollider doesn't inherit from Mage (or implement it if you make Mage an interface). Certainly not necessary but I included it for flavor and because it mimics the pattern set out by AddComponent and GetComponent (which is constrained by where T : Component).

    It also allows you to make the base assumption that T is going to be a Mage.

    Note that if you make Mage an interface I would also constrain T to being a MonoBehaviour so you won't be able to bork your component calls.

    Edit: That being said - if you make Mage an abstract class inheriting from MonoBehaviour and all you're concerned about are a set of base methods then you could just do GetComponent<Mage> and call those methods straight away. Wrapping the Get like I did allows you some level of built-in type checking. IE - if iceMage is null then this mage is not an iceMage (or not a mage at all!)
     
    Last edited: Jan 6, 2015
  12. WizardGameDev

    WizardGameDev

    Joined:
    Jul 25, 2012
    Posts:
    62
    Many thanks for the clarification on this. For this specific problem I would want to have a the built in type checking and i know it has to have certain methods. At first I was building around interfaces and then realized that for what I was doing for the core components the more straight forward inheritance made sense.

    As I've continued to refine and refactor the game I always find something I can improve on. This was a big step as I add additional mage classes and a variety of minions and tower defense elements to the game. It's really a factory pattern of sorts and I'm slowly starting to get better at design patterns... but I get tripped up with the Unity integration at times. This thread has been huge at getting my head around what it takes to get the job done. I can worry later about refining the design further.