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

[SOLVED?]Help with interfaces

Discussion in 'Scripting' started by Collin_Patrick, Sep 24, 2016.

  1. Collin_Patrick

    Collin_Patrick

    Joined:
    Sep 24, 2016
    Posts:
    83
    Why doesn't this work and how can I fix it?
    Code (CSharp):
    1. public interface IBaseCharacterSubClass
    2. {
    3.     string SubClassName { get; }
    4.     string SubClassDescription { get; }
    5. }
    6. public class AdventurerSubClass : IBaseCharacterSubClass {
    7.  
    8.     public string SubClassName { get { return "Adventurer"; } }
    9.     public string SubClassDescription { get { return "Adventurer Description"; } }
    10.  
    11.     public ISkill MySubClassSkills = new Skill1();
    12. }
    13.  
    14.  
    15.  
    16. public interface ISkill
    17. {
    18.     string skillName { get; }
    19. }
    20.  
    21. public class Skill1 : ISkill
    22. {
    23.     public string skillName { get { return "THIS IS MY SKILL NAME"; } }
    24. }
     
  2. takatok

    takatok

    Joined:
    Aug 18, 2016
    Posts:
    1,496
    That code works perfectly fine. I created a c# script and copied it into it.
    Then I created an Emtpy GameObject and attached this script to it:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class GenericTest : MonoBehaviour {
    5.  
    6.     AdventurerSubClass subClass = new AdventurerSubClass();
    7.  
    8.     void Start()
    9.     {
    10.         Debug.Log(subClass.SubClassDescription);
    11.         Debug.Log(subClass.SubClassName);
    12.         Debug.Log(subClass.MySubClassSkills.skillName);
    13.     }
    14. }
    And my Console printed out :
    "Adventurer Description"
    "Adventurer"
    "THIS IS MY SKILL NAME"

    What exactly have you tried to do, and what errors or problems are you actually having?
     
  3. Collin_Patrick

    Collin_Patrick

    Joined:
    Sep 24, 2016
    Posts:
    83
    I have found the solution.

    I added this to the sub class interface:
    Code (CSharp):
    1. ISkill MySubClassSkills { get; }
    I changed this in the AdventurerSubClass script:
    Code (CSharp):
    1. public ISkill MySubClassSkills = new Skill1();
    To this:
    Code (CSharp):
    1. public ISkill MySubClassSkills { get {return new Skill1(); }}
    And Finally I was able to successfully debug this:
    Code (CSharp):
    1. Debug.Log(currentSubClass.MySubClassSkills.skillName);
     
  4. takatok

    takatok

    Joined:
    Aug 18, 2016
    Posts:
    1,496
    You have still yet to post any actual errors. Its hard to tell you how to fix a problem that you never state :) You need to be more explicit than "This is broke".

    Since I can't see all the code this might be a problem:
    Code (CSharp):
    1.  if (loadCharacterData == true){
    2.             CharacterManager.Load(this.gameObject);
    3.             CurrentCharacterClass parsedClassEnum = (CurrentCharacterClass)System.Enum.Parse(typeof(CurrentCharacterClass), currentClass.GetType().Name);
    4.             currentCharacterClass = parsedClassEnum;
    5.         }
    6.         else{
    7.             currentClass = CreateInstance<IBaseCharacterClass>(currentCharacterClass.ToString());
    8.             currentSubClass = CreateInstance<IBaseCharacterSubClass>(currentCharacterSubClass.ToString());
    Is currentClass and currentSubClass being set if loadCharacterData isn't true? if so where and to what?
    Also I assume currentSubClass is a IBaseCharacterClass variable?

    I modifiied my Code to this to test out your CreateInstance function:
    Code (CSharp):
    1. using UnityEngine;
    2. using System;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5.  
    6. public class GenericTest : MonoBehaviour {
    7.  
    8.     AdventurerSubClass subClass = new AdventurerSubClass();
    9.     enum CurrentCharacterSubClass
    10.     {
    11.         AdventurerSubClass,
    12.         MageClass
    13.     }
    14.     void Start()
    15.     {
    16.      
    17.  
    18.         CurrentCharacterSubClass currentCharacterSubClass = CurrentCharacterSubClass.AdventurerSubClass;
    19.         IBaseCharacterSubClass currentSubClass = CreateInstance<IBaseCharacterSubClass>(currentCharacterSubClass.ToString());
    20.         Debug.Log(currentSubClass.SubClassDescription);
    21.         Debug.Log(currentSubClass.SubClassName);
    22.  
    23.         // This will not work since declaring someting as the IBaseCharacterClass
    24.         // Won't let us actually see the stuff we Do in AdventurerSubClass (or MageSubClass, etc)
    25.         //Debug.Log(currentSubClass.MySubClassSkills.skillName);
    26.  
    27.  
    28.     }
    29.  
    30.     public static T CreateInstance<T>(string name)
    31.     {
    32.         Type type = Type.GetType(name);
    33.         return (T)Activator.CreateInstance(type);
    34.     }
    35. }
    36.  
    And it again worked fine. However, as I noted in the comments you can't get at anything you actually implement extra in the individual Classes since you have it defined as type IBaseCharacterSubClass
     
  5. Collin_Patrick

    Collin_Patrick

    Joined:
    Sep 24, 2016
    Posts:
    83
    Thanks for the help but I already found the solution and stated it above. I am not quite sure what the problem was myself but whatever I did, fixed it.
     
  6. takatok

    takatok

    Joined:
    Aug 18, 2016
    Posts:
    1,496
    This is going to return a new instance of Skill1 every single time someone access MySubClassSkills. So if you are trying to store dynamic data and access it later this won't be very usable in the long run.

    That is if you do this:
    Code (CSharp):
    1. MyClassObject.MySubClassSkills.SomeSkill = 20;
    2.  
    3. // then later on you try to get that variable
    4. chance = MyClassObject.MySubClassSkills.SomeSkill;
    5.  
    6. //chance will likely = 0 or whatever its default value is.
     
  7. Collin_Patrick

    Collin_Patrick

    Joined:
    Sep 24, 2016
    Posts:
    83
    The skills will never change in the subclass. I see that is would be pointless to create a new instance of the skill. Do you have any ideas as to how to get around this. The way I have this structured is that I would have a bunch of skills in the ISkill interface and assign them to a subclass.
     
  8. takatok

    takatok

    Joined:
    Aug 18, 2016
    Posts:
    1,496
    Sorry for the late reply. Dinner and had to really think about how to explain this and code something.

    First a quick reason for interfaces. You implement an interface on your class as a contract saying that this class is guaranteed to have all the properties and methods defined in the Interface declaration. Additionally anyone can get a reference to your object by the interface and utilize those properties and methods *Without caring anything about other interfaces, properties or methods on that object*

    So ideally when your instantiating and object by its interface it should be by a class / method that only cares about that interface. If you need to know about everything in the object then you need to instantiate it and reference it as that object. I wrote some code that hopefully illustrates this. Just copy both sets of code in to 2 script files. Attach the 2nd script file to a GameObject and play. It will Debug all the appropriate values stored in the classes. You should be able to choose between Adventurer and Mage in the Editor settings.

    Generic Interface and Class Code:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. public enum CurrentCharacterSubClass
    6. {
    7.     AdventurerSubClass,
    8.     MageSubClass
    9. }
    10. public interface IBaseCharacterSubClass
    11. {
    12.     string SubClassName { get; }
    13.     string SubClassDescription { get; }
    14. }
    15. public interface ISkill
    16. {
    17.     string skillName { get; }
    18. }
    19. public class AdventurerSubClass : IBaseCharacterSubClass, ISkill
    20. {
    21.     public string SubClassName { get { return "Adventurer"; } }
    22.     public string SubClassDescription { get { return "Adventurer Description"; } }
    23.  
    24.     public string skillName { get { return "Shield Bash"; }   }
    25.  
    26.     public void AdventurerOnlyThing()
    27.     {
    28.         Debug.Log("I'm doing my Adventure Thing");
    29.     }
    30. }
    31.  
    32. public class MageSubClass : IBaseCharacterSubClass, ISkill
    33. {
    34.     public string SubClassName { get { return "Mage"; } }
    35.     public string SubClassDescription { get { return "Mage Description"; } }
    36.  
    37.     public string skillName { get { return "FireBall"; } }
    38.  
    39.     public void MageOnlyThing()
    40.     {
    41.         Debug.Log("I'm doing my Mage only thing");
    42.     }
    43. }
    44.  
    GameObject Code that does actual implementation:
    Code (CSharp):
    1. using UnityEngine;
    2. using System;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5.  
    6. public class GenericTest : MonoBehaviour {
    7.  
    8.  
    9.     // Assume we read the SubClass from a file instead of just assigning it
    10.     public CurrentCharacterSubClass currentCharacterSubClass = CurrentCharacterSubClass.AdventurerSubClass;
    11.     void Start()
    12.     {
    13.        
    14.      
    15.         Type type = Type.GetType(currentCharacterSubClass.ToString());
    16.  
    17.         AccessObjectsByInterface(type);
    18.         AccessTotalObject(type);
    19.        
    20.     }
    21.  
    22.     void AccessObjectsByInterface(Type type)
    23.     {
    24.         var currentSubClass = (IBaseCharacterSubClass)Activator.CreateInstance(type);
    25.         var currentSubClassSkill = currentSubClass as ISkill;
    26.         Debug.Log(currentSubClass.SubClassName);
    27.         Debug.Log(currentSubClass.SubClassDescription);
    28.         Debug.Log(currentSubClassSkill.skillName);
    29.     }
    30.  
    31.     void AccessTotalObject(Type type)
    32.     {
    33.         switch (type.ToString())
    34.         {
    35.             case "AdventurerSubClass":
    36.                 var adventureSubClass = (AdventurerSubClass)Activator.CreateInstance(type);
    37.                 adventureSubClass.AdventurerOnlyThing();          
    38.                 break;
    39.             case "MageSubClass":
    40.                 var mageSubClass = (MageSubClass)Activator.CreateInstance(type);
    41.                 mageSubClass.MageOnlyThing();
    42.                 break;
    43.         }
    44.     }
    45. }
    46.  
    Note: I removed your extra class for skills. If you implement ISkill as an interface just have your Specific subclasses Implement it as well. That is the point of Interfaces, to implement as many as are appropriate. I combined accessing both Interfaces into one function, but they just as easily could have been 2 different methods on 2 different Objects. The point is when your accessing the Interface of an Object you don't really care what it is, or what other methods its has. You just know its guaranteed to have your Interface stuff and you access and manipulate that.