- Home /
Finding A Variable With A String Variable
Hey,
I am trying to find a variable in a script, using another variable (String). I will explain with an example:
var a : AssetList;
var code : String; // I enter in the specific ID here in the inspector (eg. A1, A2, D1)
var fullCode : String;
var mod : GameObject;
function Start ()
{
a = gameObject.Find("Assets").GetComponent("AssetList");
fullCode = "mod_" + code; // Combines with another string to form correct variable name
mod = a.fullCode; // Here's the problem, I cannot use a String to find the variable in the AssetList
var instanceMod : GameObject = Instantiate(mod, gameObject.transform.position, gameObject.transform.rotation); // Then it should spawn the desired mod
Destroy(gameObject);
}
Surely there is a way to acheive this, like GetVariable("String"); Obviously that's not a thing, but something similar just so I can use this String to find a variable.
I have heard of using Reflection. But apparently that is very slow, especially as it will be called several times when AI spawns.
Thanks for your time, Magnus
You cannot use a variable this way. Typical solutions to this kind of problem use dictionaries, arrays, or even 'switch' statements. I don't know which one to suggest since I don't know the nature of the problem you are trying to solve.
So there is no simple way to do this?
Because I have just done:
...
if (code == "A1")
mod = a.mod_A1;
if (code == "A2")
mod = a.mod_A2;
...
And that work fine, but laborious as i have a whole load of mods to write if statements for :(
There's no reason to ever write code like that; as robertbu said, use dictionaries or arrays ins$$anonymous$$d.
Answer by billykater · Oct 06, 2013 at 09:27 PM
You are right, Reflection the "only" way to access variables by name. The better question to ask here is why you need to access a variable by a string.
I suppose what you want to do is implement some kind of lookup that you can set from the inspector and have them differentiated by a name you attach to them.
1. Everytime you think about "lookup" the best way to implement this is through the Dictionary class.
import System.Collections.Generic;
var dict : Dictionary.<String, GameObject> = new Dictionary.<String, GameObject>();
function Start ()
{
dict.Add("a", gameObject);
}
function OnGUI()
{
GUILayout.Label(dict["a"].ToString());
}
Unfortunately dictionaries aren't editable from the inspector and won't be saved through unity's serialization system. For this you need a list of serializable data classes. Just use a list of something like
var entries : List.<Entry> = new List.<Entry>();
@Serializable
public class Entry
{
public var key : String;
public var asset : GameObject;
}
you can then loop over these entries in your awake / other initialization method and push all entries into the dictionary. To make this easier to see in the inspector you can either add a custom inspector or a custom property drawer. If you want a more contained solution (Not having to implement this everytime you want such a lookup) there are serializable dictionary implementations somewhere in the unify community wiki.
2. Expression Trees: I put "only" into quotes because there is another way than using reflection which is the Expression class located in System.Linq.Expressions. You can use Expression.Lambda, Expression.Field and Expression.Parameter to compile a function that will return the value of a variable given a string name. Theoretially this should be faster than using reflection (If you use it multiple times to access the same variable). This option is most likely not available on iOS (I haven't tried android either but it should be possible due to JIT compiler being available on this plattform). Overall this is an advanced construct and should only be used for cases where you can't really use the dictionary method above or are fully understanding what it is doing under the hood.
Thanks for the sweet response, helped a lot :)
Also to make it editable in the inspector I just added a variable in place of the "a", it works a treat:
import System.Collections.Generic;
var dict : Dictionary.<String, GameObject> = new Dictionary.<String, GameObject>();
var a : AssetList;
var code : String; // change this
function Start ()
{
a = gameObject.Find("Assets").GetComponent("AssetList");
dict.Add("A1", a.mod_A1);
dict.Add("A2", a.mod_A2);
dict.Add("A3", a.mod_A3);
Instantiate(dict[code], gameObject.transform.position, gameObject.transform.rotation);
Destroy(gameObject);
}
How ever I do not understand what you mean by 'won't be saved through unity's serialization system', is that an issue?
Unity's serialization system If you have something like an int or a List. and you modify the value of it in the inspector(or code for that matter) unity automatically remembers its value (e.g. having var speed : float and setting it to 5 will be remembered even if you close unity and restart it). If you add any value to a dictionary and restart unity the content will be gone. Thats why you need to reinitialize the dictionary like you do in your Start method.
One advice for the future, move the whole dictionary to your AssetList component this way you won't need to modify two components if you every decide to add another variable.
Another thing I would do (advanced topic) is to not have hardcoded variables. This way you can extend how many entries there are without ever touching the code again. This is what will need all the additional stuff I was talking about (Serializable Dictionary and Custom Inspectors)
Ah very good idea indeed, I will do that.
Awesome thanks, yeah I might look into custom inspectors. Look like they could be very useful for other things too.
Thanks again :)