Search Unity

Getting error trying to access variable in a dynamically created class

Discussion in 'Scripting' started by Dreoh, Apr 12, 2018.

  1. Dreoh

    Dreoh

    Joined:
    Aug 15, 2012
    Posts:
    15
    I'm trying to create a dynamic spell system, and to do so I need to be able to call different classes depending on player choices to change how spells work. I got it mostly working, but I ran into a snag with an unexpected error where it seems everything should work fine.

    Here is where I'm having an issue

    Code (CSharp):
    1.  
    2. public GameObject origin;
    3. public Vector2 velocity = Vector2.right;
    4. public GameObject prefab;
    5. public List<string> attachedClasses = new List<string>();
    6.  
    7. void OnFire() {
    8.         var projectile = Instantiate(prefab) as GameObject;
    9.         projectile.transform.position = origin.transform.position;
    10.         projectile.transform.rotation = origin.transform.rotation;
    11.         projectile.GetComponent<Rigidbody2D>().velocity = velocity;
    12.  
    13.         foreach(string className in attachedClasses) {
    14.             var newClass = projectile.AddComponent(Type.GetType(className));
    15.             print(newClass);
    16.             if (newClass != null) {
    17.                 newClass.caster = transform.root;
    18.             }          
    19.         }
    20.     }
    21.  
    The line
    newClass.caster = transform.root;
    I'm getting the error
    error CS1061: Type `UnityEngine.Component' does not contain a definition for `caster' and no extension method `caster' of type `UnityEngine.Component' could be found. Are you missing an assembly reference?


    I have confirmed using the print statement that the classes are being created correctly and are attached to the correct gameobjects.

    Here is an example of one of the classes i'm adding to projectiles dynamically.

    Code (CSharp):
    1. public class Projectile : MonoBehaviour {
    2.  
    3.     public Transform caster;
    4. }
    5.  
    Code (CSharp):
    1. public class Temporary : Projectile {
    2.  
    3.     public float duration = 5;
    4.  
    5.     void Start() {
    6.         Destroy(gameObject, duration);
    7.     }
    8.  
    9. }
    As you can see, the Temporary class inherits caster from Projectile, but for some reason it's not recognizing that when I try to access it
     
  2. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    I don't know the extent of your needs, nor if this is the best way, but you could try this:
    Code (csharp):
    1. var g = projectile.AddComponent(System.Type.GetType(className));
    2. Temporary t = g as Temporary;
    3. if(t != null)
    4. {
    5.   // whatever...
    6. }
     
    Last edited: Apr 12, 2018
  3. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,187
    A simple cast might work? If @methos5k method doesn't work.
     
  4. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    That's a good point. I think I confused myself when I dismissed that idea lol sigh.
     
  5. Dreoh

    Dreoh

    Joined:
    Aug 15, 2012
    Posts:
    15
    The problem with methos5k's method is that my code doesn't know the class type beforehand, so doing
    Code (CSharp):
    1. Temporary t = newClass as Temporary;
    Won't work because you can't do
    Code (CSharp):
    1. Type.GetType(className) t = newClass as Type.GetType(className);
    2. if(t != null) {
    3.     print(t.caster);
    4. }
    as far as I'm aware. When trying to do this I just get the error "Unexpected symbol 't'" in line 1

    To further explain what I'm attempting, I'm trying to let the player choose what classes the spell has. In the case I showed before, the player would 'equip' the "Temporary" class to the projectile, so that it despawns after 5 seconds.
    This is why I'm creating the classes in a foreach loop from the 'attachedClasses' list

    As far as simple casting like Brathnann said, I've come from Dota2 Lua modding where casting wasn't a thing since it would autocast for you and I'm not sure how to format casting or how not to either. Monobehaviors and it's game object-oriented scripting system is really frustrating to deal with.
     
    Last edited: Apr 13, 2018
  6. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Okay, I thought that though they could be any class, they'd all have the same base. Perhaps I jumped to a conclusion from your example.
    If they won't be sharing a base, then you could use an interface, if they share a common property.
    I think either of our examples would work in that scenario.
     
  7. Dreoh

    Dreoh

    Joined:
    Aug 15, 2012
    Posts:
    15
    Well these WOULD all inherit from the 'Projectile' class, meaning they should all inherit Projectile.caster, meaning the 'Temporary' class should have Temporary.caster

    Sharing properties isn't the problem, it's that I can't access Temporary.caster when I create it

    It's a fairly complicated process, mostly because I have to do this in a complicated way to get around the Beginner-friendly Monobehavior-GameObject system Unity has
    To clarify further this is what's happening in the code

    I have 3 classes, Projectile, Temporary and DamageImmediate as such each in their own correct file:

    Code (CSharp):
    1. public class Projectile : MonoBehaviour {
    2.  
    3.     public Transform caster;
    4. }
    5.  
    6. public class DamageImmediate : Projectile {
    7.  
    8.     void OnTriggerEnter2D(Collider2D collision) {
    9.       //damage calculation using the caster parameter to reference who this projectile belongs to
    10.     }
    11.  
    12. }
    13.  
    14. public class Temporary : Projectile {
    15.  
    16.     public float duration = 5;
    17.  
    18.     void Start() {
    19.         Destroy(gameObject, duration);
    20.     }
    21.  
    22. }
    Both Temporary and DamageImmediate inherit from Projectile, so they should both have the 'caster' parameter.

    Now, a player in the game can choose, "I want my projectile spell to travel for 3 seconds and damage someone if it hits something". So they check the box for 'Temporary' and set the duration to 3, and 'Damage Immediately' and set the damage to 5 for example. These selections would be saved in a list

    Now, in my 'SpawnWithVelocity' class I actually create the projectile gameobject and attach all the classes the player chose.

    Code (CSharp):
    1. public class SpawnWithVelocity : MonoBehaviour {
    2.  
    3.     public GameObject prefab;
    4.     public GameObject origin;
    5.     public Vector2 velocity = Vector2.right; //A temporary direction for testing
    6.     public float force = 500f;
    7.  
    8.     public List<string> attachedClasses = new List<string>(); //This is where the players projectile "choices" are stored as strings, such as "Temporary" and "DamageImmediate"
    9.  
    10.     void OnFire() {  //Called outside this class from button press
    11.         var projectile = Instantiate(prefab) as GameObject;
    12.         projectile.transform.position = origin.transform.position;
    13.         projectile.transform.rotation = origin.transform.rotation;
    14.         projectile.GetComponent<Rigidbody2D>().velocity = velocity;
    15.  
    16.         foreach(string className in attachedClasses) { //Iterate through the players choices of projectile modifications and add each class script to the projectile game object
    17.             var newClass = projectile.AddComponent(Type.GetType(className));
    18.             print(newClass);
    19.             if (newClass != null) {
    20.                 newClass.caster = transform.root; //This would set the caster property of each of the 'projectile modifiers' like Temporary and DamageImmediate to the GameObject that casted the spell, so that I can reference it later in those modifiers
    21.             }
    22.         }
    23.     }
    24.  
    25. }
    So based on this right here, I should be able to have both Temporary and DamageImmediate created and attached to the new projectile game object, and then set the 'caster' value.

    But the problem I'm getting is that it doesn't register that '.caster' exists on any of the new classes and thus won't let me set it

    I know it's a bit confusing, but if I can figure out why it doesn't recognize .caster then I'll be home free
     
  8. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    I stopped reading part way down.. The thing is I must have misread your code earlier, as "Temporary" being the base.
    If you used casting for Projectile you should be able to do what you want.
     
  9. Dreoh

    Dreoh

    Joined:
    Aug 15, 2012
    Posts:
    15
    I'm sorry if it feels like i'm talking in circles, I'm very grateful for any help you provide

    I'm still not sure what you mean by casting since the concept of casting is new to me even though i'm experienced in other areas of programming

    Do you mean adding a Projectile class and then downcasting it to Temporary or DamageImmediate? I just googled downcasting and it seems that's what you mean, but then I run into the problem I stated before where I can't because I don't know the type beforehand and run into this problem


    Code (CSharp):
    1. //I get an error because it needs to know the type beforehand
    2. Type.GetType(className) t = newClass as Type.GetType(className);
    3. if(t != null) {
    4.     print(t.caster);
    5. }
     
  10. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    No, no.
    One of these, just try both:
    Code (csharp):
    1. Projectile proj = (Projectile) projectile.AddComponent(System.Type.GetType(className));
    2. proj.caster = transform.root;
    3. // or
    4. var proj = projectile.AddComponent(System.Type.GetType(className));
    5. Projectile p = proj as Projectile;
    6. if(p != null) { // shouldn't ever be 'null'.
    7.    p.caster = transform.root;
    8.   }
    Edit: Edited fast, but originally missed (Projectile) in the first example*.
     
  11. Dreoh

    Dreoh

    Joined:
    Aug 15, 2012
    Posts:
    15
    That worked!

    Thank you, I understand what you meant after seeing what you wrote.
    I should probably read up a lot more on casting, I've been spoiled by Lua
     
  12. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Just out of curiosity, did both examples work? :)

    And you're welcome - glad it's working for ya.
     
  13. Dreoh

    Dreoh

    Joined:
    Aug 15, 2012
    Posts:
    15
    Yep, both of them worked!
     
  14. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Cool. :) Take care... Enjoy your game!
     
  15. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,187
    Yeah, glad @methos5k was able to help out with the casting. I had mentioned it just because I noticed the error was reading it as component.