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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice
  4. Dismiss Notice

[SOLVED] Passing values between scripts (non-static class)

Discussion in 'Scripting' started by ArchVile, Apr 21, 2010.

  1. ArchVile

    ArchVile

    Joined:
    Apr 21, 2010
    Posts:
    4
    Okay guys, this is my first time posting, and I'm a fairly new user to Unity. I'm coding a shooter game in C# and so far I've been very successful a figuring out my own problems and have been moving along fairly well.

    I finally hit the wall and I'm sure the answer is simple, but the more I try to figure it out, the farther I seem to get from the solution.

    Here's the issue:

    I have two scripts on a game object, The first one is just a trigger script runs like an event system. It triggers another game object when conditions are met. In this case, it generates and instance of a ship prefab that has it own script attached to it. In the same event gameObject (along with the trigger script) there is a control script that I want to use to modify the behavior or the instantiated object. The problem is, I can't figure out how to pass values from one script to the other.

    In case that was confusing above, basically I want the instanced scripted prefab to accept the values from the control script in the event object. I have no idea why I can't get it to work though. I'm sure there's something simple I'm missing.

    Here's when I set in the control script:
    Code (csharp):
    1.  
    2.     public float ShipPosition = 4;
    3.     public float ShipSpeed = 3;
    4.  
    In the actual ship script, I want to accept those values and pass them to the ship script:
    Code (csharp):
    1.  
    2.     void Start(){
    3.             WarheadEvent WarheadStats = (WarheadEvent)GetComponent("WarheadEvent");
    4.             currentSpeed = WarheadStats.ShipSpeed;
    5.             position = WarheadStats.ShipPosition;
    6.         }
    7.  
    Unity doesn't like it and tells me
    It would be nice and easy to just do "Scriptname.Variable" but I can't go with a static class, because I need to be able to change the value in the Unity interface.

    I'm sure this is a simple problem, but I just can't seem to wrap my head around it.

    Thanks in advance. I really appreciate any help you guys can give me.
     
  2. ESB

    ESB

    Joined:
    Mar 2, 2010
    Posts:
    71
    One simple way to make sure your script knows which object to interact with, if you only have one of those objects, is to set it via the editor.

    So in the script accessing the warheadevent, add
    Code (csharp):
    1. public WarheadEvent warheadStats;
    and then in the editor set that to be the object that is relevant. (Drag that object onto this bit in the inspector.) Then you can skip the GetComponent bit altogether.

    If that solution makes no sense, I probably misunderstood your question.
     
    Zarocross likes this.
  3. dart

    dart

    Joined:
    Jan 9, 2010
    Posts:
    211
    I don´t know if I understood your problem, but to access variables in a C# class from another, try:


    Code (csharp):
    1.  
    2. public void Start()
    3. {
    4.     gameObject.Find("NameOfTheGameObjectTarget").GetComponent<NameOfTheScrit>().NameOfTheProperty = ...;
    5. }
    6.  
     
  4. ArchVile

    ArchVile

    Joined:
    Apr 21, 2010
    Posts:
    4
    Looking back on my original post, I can understand confusion. My explanation was a little long winded and made my situation hard to follow. I tend to over-describe things.

    @ESB - I considered that route too. The problem was that each setup (gameObjects I just call "Events" are all different and are instatiating objects with different values) has to use multiple gameObjects that share scripts and activate and deactivate in succession, making giving a specific name impossible, at least in the way I'm going about it. However, I'll keep your solution in mind, just in case I have to rethink my approach, because there may be an easier and better way of dealing with this and your way may just make the most sense in the end.

    @dart - Thanks for the suggestion. I'm going to give that a try right now. .Find is something I didn't try yet, but after looking it up, that may work. I tried to understand the process better by watching the tutorial videos at 3DBuzz, but in that case they were passing information that was assigned to game objects, but it seems C# is fussy about that distinction. I'm gonna fiddle around with your approach right now and see if that gets me on track, and I'll let you know.

    Sorry I took so long to respond. I've been away from home since this morning.
     
    arielfel likes this.
  5. ArchVile

    ArchVile

    Joined:
    Apr 21, 2010
    Posts:
    4
    Okay guys. Thanks for all the help. After stopping, mapping out my logic and rethinking my approach, I realized that what I was trying to accomplish was just not possible. From a logical standpoint, I can't just pull info from a non-active script arbitrarily. In the case that multiple instances of that script were active at once, the engine would have no idea what I intended and would probably just crash, if C# allowed it to be accessed without having a direct connection to the object in question.

    The big reason I got confused by the process was I saw them do something similar in the 3DBuzz video tutorials. In that case though, they access the information from another object after making collision with it (just essentially handshaking each other) and that's why they can pass the info. What I was attempting to do just wasn't possible after really thinking about it. I just need to rethink my approach.

    Thanks for all the help guys. Your help has not been in vein. It actually helped me with a different issue that I needed to address anyway, so the help was definitely useful anyway. Again, thanks for the responses.
     
    arielfel likes this.
  6. ArchVile

    ArchVile

    Joined:
    Apr 21, 2010
    Posts:
    4
    Just an update, but after I had given up and decided to move on and find a different way about going about things, I got an idea that I tried and eventually found a way to do exactly what I wanted to do from the beginning. So I'm updating this with the solution so that if anyone else stumbles on this thread trying to accomplish something similar, it may help them out.

    After I realized that I couldn't arbitrarily pass values between non-active scripts, I got the idea that as long as they were all active and connected somehow, it should work. Then I realized that parenting was the answer. Here's what I did:

    Event #1 (as a gameObject) will trigger a sequence of instantiated game objects (a number of Warhead ships that I can set in the event script). But I also needed the event script to be able to pass values off onto the Warhead ship instance as well (things like ship speed, start position, etc). Kind tough to do, yes, but not impossible.

    First I needed the Event trigger to generate the instantiated objects as a "child" of the event. Here's the code that did that:
    Code (csharp):
    1.  
    2. GameObject instance = Instantiate(ObjectTrigger, new Vector3(3, 0, 0), Quaternion.identity) as GameObject;
    3. instance.transform.parent = transform;
    4.  
    Cool. Now that I had the gameObjects instantiating as children to my event, now I just needed to find a way to have those children (with their own scripts) pull values from the "parent" event script. This wasn't as easy as I expected. There's a feature called ".GetComponentsInChildren" which can help when the parent needs info from the children, but no ".GetComponentsInParent" if you need to go to the parent in the hierarchy like I did.

    There are two ways to do it. One is the clean way and one is the potentially bad way that will cause conflicts. The first way I did it was to attempt to just use ".FindGameObjectsWithTag" and give my parent event a special tag. This seems like it works beautifully, but the catch is that it will only work right if you have one instance of that event going on at one time. What if I wanted to have two or three events that were generating instantiated Warhead ships? Well, the problem with .FindGameObjectsWithTag is that it will override every event with the same tag, regardless of hierarchy. Not good.

    Now here's the alternate solution that worked exactly as I needed it to and won't go outside of the hierarchy. Here's what I did in the script of the script of the instantiated prefab object.
    Code (csharp):
    1.  
    2. WarheadEvent warhead = (WarheadEvent)GameObject.Find([b]transform.parent.name[/b]).GetComponent("WarheadEvent");
    3. Position = warhead.ShipPosition;
    4. currentSpeed = warhead.ShipSpeed;
    5.  
    transform.parent.name is the kicker here. That ensures that it is grabbing the values from whatever the parent is. The reason I can't just go by name, is that every event will be named differently, so therefore, searching by name is unreliable. This way, it will get values from the parent, regardless of its name.

    Here's a sample pic of what I accomplished and why this is useful:



    Click here for full size image

    As you can see above, I have two events, running concurrently, each with their own children being generated, but each event is behaving differently. I am now able to change the ship spacing, ship speed, position, amount, or anything else I want, and pass those values onto the instanced objects without any problems. I can also have multiple instances of this event running, without either affecting or conflicting with each other.

    Here's a more complete version of the code I used to accomplish this:

    Here's the code for the EVENT script (only the important part)
    Code (csharp):
    1.  
    2.     public float GenerationAmount = 8;
    3.     public float ShipSpacing = 0.4f;
    4.     public float ShipPosition = 0;
    5.     public float ShipSpeed = 4;
    6.  
    7.  
    8.     IEnumerator ShipSpread()
    9.     {
    10.         for (int i = 1; i <= GenerationAmount; i++)
    11.         {
    12.             //These next two lines are calling from another script that handles triggers based on a set game              //object.
    13.  
    14.             EventHandler ReadObjectTrigger = (EventHandler)gameObject.GetComponent("EventHandler");
    15.             GameObject ObjectTrigger = ReadObjectTrigger.TriggerObject as GameObject;
    16.             yield return new WaitForSeconds(ShipSpacing);
    17.            
    18.             //These next two lines are the important ones.  They will help instantiate the set prefab as a  
    19.             //child GameObject.
    20.  
    21.             GameObject instance = Instantiate(ObjectTrigger, new Vector3(3, 0, 0), Quaternion.identity) as                  GameObject;
    22.             instance.transform.parent = transform;
    23.         }
    24.     }
    25.  
    Here's the code for the INSTANTIATED object script (only the important part)
    Code (csharp):
    1.  
    2.     private float currentSpeed;
    3.     private float Position;
    4.     private float ShipSpread;
    5.     private float ShipCount;
    6.    
    7.     void Start()
    8.     {
    9.         //This next line opens the possibility to nab values from the parent (the event object)
    10.         WarheadEvent warhead =            (WarheadEvent)GameObject.Find(transform.parent.name).GetComponent("WarheadEvent");
    11.  
    12.         //These next two lines are me assigning the values I am taking so that I can transfer them over to            //the actual instanced ship script.
    13.  
    14.         Position = warhead.ShipPosition;
    15.         currentSpeed = warhead.ShipSpeed;
    16.     }
    17.  

    There ya go. I hope this wasn't too confusing, but I really hope that this can help someone else out down the road, because it was kind of a pain to figure out. Now that I have it set, setting variations on events is a breeze, and helps to me time out events and ship behaviors in nothing flat. It's really useful.
     
  7. krasimirdanielof

    krasimirdanielof

    Joined:
    Oct 26, 2019
    Posts:
    1
    ArchVile you are my hero, you saved me 9 years later i was looking for something like this for so long and this is the only comment like this that i could find !!!!!
     
    Bengle15 and wanati like this.
  8. Bengle15

    Bengle15

    Joined:
    Apr 20, 2020
    Posts:
    1
    Thank you ArchVile! Literally the day before the 10 year anniversary of this thread, it comes in clutch for Ludum Dare 46. Thank you!
     
  9. TheStrugglingDudeGames

    TheStrugglingDudeGames

    Joined:
    Apr 19, 2020
    Posts:
    3
    Also might have been able to use PlayerPrefs.setInt("Positition") & PlayerPrefs.SetInt("Speed") and then just done PlayerPrefs.GetInt("Position") & PlayerPrefs.GetInt("Position"), I'm not sure how you coded your game but if and Integer value is not suitable you should be able to do GetFloat instead
     
  10. rmezulanik

    rmezulanik

    Joined:
    May 3, 2021
    Posts:
    1
    I'm new in Unity, but in 2022 I solved it by instantiating the class:

    Code (CSharp):
    1. public class ScoreCount : MonoBehaviour
    2. {
    3. public static ScoreCount instance;
    4. public float time = 10;
    5. }
    6.  
    7. public class TryAgain : MonoBehaviour
    8. {
    9. void Update()
    10. {
    11. if (ScoreCount.instance && ScoreCount.instance.time <= 0)
    12. print(ScoreCount.instance.time);
    13. }
    14. }