Getting variable from another script by its string

Hello,
Here is my problem :
I’ve a Player script with a list of public string variables (quests).

I created also an empty gameobject that allows the player to change level when he gets close from it AND when the quest named “quest_tantive” has a value of 1 in the Player script.
Here is the script I attached to this gameobject. I can’t get why it’s not working, I need help!

public class load_level : MonoBehaviour 
{
public string level_to_load;
public string quest_condition="quest_tantive";
void Start () 
{
 }
void Update ()
    {
        if (Mathf.Abs (transform.position.x - GameObject.Find ("Player").transform.position.x) <= 1.5 &&
            Mathf.Abs (transform.position.y - GameObject.Find ("Player").transform.position.y) <= 1.5&&
            GameObject.Find ("Player").GetComponent<Player>().GetType().GetField(quest_condition).GetHashcode()==1)
            Application.LoadLevel (level_to_load);
    }

The problem comes from the last condition
GameObject.Find (“Player”).GetComponent().GetType().GetField(quest_condition).GetHashcode()
which is not accepted by unity.
If I write “GameObject.Find (“Player”).GetComponentquest_tantive==1” it works but I need a general function since there’ll be many different quests/levels. Actually I need in pseudo code this :

GameObject.Find ("Player").GetComponent<Player>"variable having same name as the string contained in quest_condition"==1"

To start with, your current problem - GetHashCode doesn’t mean anything close to what you seem to think it means. The hash code is not going to be the same as the value of your member variable. There are several things wrong with the technique there, and truth is, I can’t really name them all offhand because reflection is complicated. I originally wrote a solution here, but then realized it was still wrong… take that as a sign of how complicated it is (and how rarely you should use it in gaming).

In general, using reflection (e.g. anything involving GetField et al) as a standard technique is a Really Bad Idea™. Reflection is complicated, slow, it’s unreliable, if there are any errors they’ll only show up in runtime (instead of at compile time, where they’re most useful), and worst of all, it doesn’t work on every platform (as WebGL grows in usage and in platforms, this will become more and more prevalent). Using it in the editor is fine; using it in the game is bad. So, maybe you could explain a little more about what you’re trying to accomplish and we can help you find a different way?

Finally, there are a lot of beginner mistakes in that code. Most notably, frequent usage of GameObject.Find, overusage of GetComponent, a strange (slow, error-prone, inaccurate) way of comparing positions, etc.

I would like to point out more in depth that your usage of getcomponent and gameobject.find are not just “beginner” mistakes … but really really bad beginner mistakes. Not to come across as mean or anything. What I mean by bad is that it will completely obliterate your performance. Like seriously destroy without mercy. It will annihilate all of your frame rate. It will implode upon your user’s happiness. It will … Is that really all the creativity I can muster? I was totally going to keep going but then I ran out of ideas.

You get the point. Use Awake() to grab your runtime references that don’t make sense to assign in the inspector (which I have become less and less a fan of lately, TBO).

1 Like

I suppose I could use to avoid frequent usage of GameObject.Find something like
Gameobject ThePlayer = GameObject.Find(“Player”); then use “ThePlayer” as the object instead of looking for it at each test?
Beside that, my problem is how to access a variable located in another script by its string name.
The interest of using a string name is because I wanted to use this “loading level object trigger” as a generic object to load any level. I would have just to edit the public string from the visual editor of unity instead of creating 10 differents scripts doing almost the same thing, beside test condition to load a level… It is possible, but not elegant and boring.

Note that my game is in 2d (it’s a RPG style in the world of starwars) and works fast with more than 20 characters fighting each other in real time with blasters :).

I don’t get the problem with GetComponent? I need to access a public variable located in another script. Without this “GetComponent”, I thought it was impossible. What should I do?

Sorry, still off topic. I apologize if I’m off here, but you seem like youre maybe potentially new to this whole thing raised inflection ?

If so I would REALLY recommend both a) Don’t use Star Wars unless you can get permission… and b) make a significantly smaller project. Like super duper duper smaller.

So aside from all that, use an enum. Your value checking is crazy there with all the get type get hash etc etc. If you’re looking for an “If state == 1 | if state == 2” logic, then you absolutely just want to use an enum.

public enum QuestState {
    InProgress,
    Complete,
    Null,
}

Once you’ve defined that (note that each value is essentially and abstracted int starting from 0.

Then just do

psuedo code

if (distance <= 1.5f) {
    if (player.QuestCondition == QuestState.InProgress) {
        Application.LoadLevel(level_to_load);
    }
}

It’s not so much that you shouldn’t use it, it’s that you shouldn’t use it multiple times every frame. Like GameObject.Find (though to a lesser degree), it’s unnecessarily slow.

You can cache the result, like this:

Player playerObject;
void Start() {
playerObject = GameObject.Find("Player").GetComponent<Player>();
}
void Update() {
Debug.Log("Player is at "+playerObject.transform.position);
playerObject.somePlayerVariable = 5;
}

Even better, you could (in a case like this) use a singleton to find the Player object without a single GetComponent or GameObject.Find at all!

Technically you’re still using it… just in another script. :stuck_out_tongue:

Not if you set the global “instance” reference in the Player’s Awake.

All the above advice still applying, it sounds like you want a long list of similarly typed variables, that can be accessed by name. My recommendation would be to use a Dictionary. A dictionary takes two types as parameters, can access them by name (efficiently), and you know you’ll always get the right data type in return, and it’ll prevent you from accidentally modifying other, unrelated variables by accidentally typing the wrong name somewhere.

It might look something like this (untested code here):

// in Player.cs
using System.Collections.Generic;

....

public static Player main;

Dictionary<string, QuestState> allQuestStates;

public void SetState(string stateName, QuestState state) {
if (allQuestStates == null) allQuestStates = new Dictionary<string, QuestState>();
if (allQuestStates.ContainsKey(stateName) ) {
allQuestStates[stateName] = state;
}
else {
allQuestStates.Add(stateName, state);
}
}

public QuestState GetState(string stateName) {
if (allQuestStates == null) allQuestStates = new Dictionary<string, QuestState>();
if (allQuestStates.ContainsKey(stateName) ) {
return allQuestStates[stateName];
}
else {
return QuestState.Null;
}
}

void Awake() {
main=this;
}

//in other scripts

Player.main.SetState("quest_tantive", QuestState.Complete);
1 Like

Thanks for the reply. I think the dictionnary seems the best option in my case.

Regarding copyright issue of Starwars, since the game will be a freeware, I hope it will be consider as fan art! Even if not, I’ll leave the project on the internet as a gift coming from nowhere. People will do whatever they want with it. For me, it’s just fun to code and play a game I would have liked as a player.
You also say my project is too big. Actually, it is when you start from scratch with no asset and with a non specific language system (Visual C#, or worse Delphi, which was from where I come)/
I’ve already many assets available, all of those were adapted to the game (sprite, locations), visual effects (lighting, wind sand effect, scrollings), combat and simple waypoint navigation scripts for npcs, a GUI inventory system, a general script for tree-base dialogs with npcs. The game is very smooth (meaning 60fps at least), no slowdown. Of course, it’s a 2d game but I had to work on optimisation several times.

I used to code in Delphi (for several years, I even made simple games with it, like a kind of shuffle puck coffee and Pac Man made from scratch) then 6 months ago I shifted to C#. I finally discovered Unity and I think it’s the best option to create a game “easily”. Of course “easily” is relative, but when you try to find out with Delphi how to do a scrolling with sprites moving around smoothly by using double buffer and complicated code (at least from my viewpoint), Unity seems much more easy.

Starmanta : thanks for the optimisation of code, I’ll use this playerObject = GameObject.Find("Player").GetComponent<Player>();
It’s indeed faster to user a pointer instead of scanning a specific object among hundreds of gameObjects at each frame since it’s in the update() function.

Mmm, by the way

not working because as you look for the script component, playerObject is not accepted by unity as an object.
I just used :
joueur = GameObject.Find(“Player”);
So it’s “half” optimised, with

if (Mathf.Abs (transform.position.x - joueur.transform.position.x) <= 1.5 &&
            Mathf.Abs (transform.position.y - joueur.transform.position.y) <= 1.5&&
            joueur.GetComponent<Player>().quest_capsule_tantive==1)

Well, perhabs better than I thought at first sight, the last test with variable located in script (…Player()>.quest_capsulte_tantive==1)is not called at each update() as long as the player is more than 1.5 distance from this object…

Now I think I get how works dictionary with C#, I need to test it, I didn’t know such datas array exist… Since my childhood, it was always about string list or fixed array of int/string…

System.Collection.Generic has several cool data collections. List<> is another really useful one - a resizable array that can be easily converted to a builtin one.