- Home /
How to refrerence a changing variable without using GetComponent
I am attempting to modify FileBasedPlayerPrefs to where it can save at multiple locations.
Lets say I have a string called myString, And I have a method that changes the string when called upon, And in another script, it references that variable and prints whatever it is, So when I call my method, it changes the string, and my other script prints that.
How would I go about doing this? Thanks in advance!
EDIT
If I run this code, I get this error: error CS0120: An object reference is required for the non-static field, method, or property 'Config.myString'
//Config.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Config : MonoBehaviour
{
public string myString = "myValue1";
public void ChangeString(string changeStr)
{
myString = "myValue2";
}
}
//MyScript.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MyScript : MonoBehaviour
{
public string myStringReferece;
void Update() {
myStringReference = Config.myString;
}
}
Answer by lipisis · Mar 15, 2021 at 09:04 PM
You can use UnityEvent or standard C# Events. So when function X updates a string for example, it then also triggers an event and then all other scripts can subscribe to that event and act accordingly
That's a better method, but it's a little more complicated.
Answer by Ermiq · Mar 17, 2021 at 08:31 AM
Since there should be only one Config
in the game usually, you actually could make it static or use a singleton pattern.
A static class cannot derive from MonoBehaviour
, so you won't be able to attach it to a game object, and it want automatically execute MonoBehaviour
methods like Update()
, Awake()
and such. There're some other important things you'll need to learn eventually about static classes, like how and when do the static class constructor, fields and properties are initialized, and other things. It might be quite complicated for a novice.
Another option, a singleton is basically a usual MonoBehaviour
script that you attach to a GameObject, but you declare a static reference to the instance of it so you could access it from anywhere. The main difference between a usual reference and a static reference is that static one affects all the objects of the same type. That's why it should only be used when you have only one object of that type. Otherwise you'll get to a point where all of your enemies lose their health when you hit only one of them, for example.
So, here's a singleton approach:
public class Config : MonoBehaviour
{
// This is the static reference that could be used to access the Config script as a static object from anywhere
public static Config singleton;
public string myString = "myValue1";
void Awake()
{
// Here you assign the `singleton` reference to this object instance
singleton = this;
}
public void ChangeString(string changeStr)
{
myString = "myValue2";
}
}
public class MyScript : MonoBehaviour
{
public string myStringReferece;
void Update()
{
// To get the string in Config you use the static `singleton` reference of the `Config` class that is accessible without 'GetComponent'.
myStringReference = Config.singleton.myString;
}
}
If you need some other object to react somehow whenever the myString
is getting changed, you can use C# event system, as one of the options.
I prefer the C# own event Action
system because to me it seems the most easy to read and pretty convenient to use.
Events are useful when you want certain objects to react to some specific events in another class. It works as a subscription. The notifier (the class that broadcasts the message "Hey, I did something. Is anyone interested?") should declare an event that it will trigger when the thing happened. The listener (the class that want to know if something has happened) should subscribe to the event by using the reference to the notifier class instance.
In the case of singleton, the listener won't need the reference to the exact instance of the notifier though. Just like with accessing myScript
via Config.singleton.myString
, the subscription is also could be made with Config.singleton.myEvent
.
Here is how you could use events:
using System; //To use C# events system you need the System namespace
public class Config : MonoBehaviour
{
// Declare the event that other classes will subscribe to if they want. The `Action<string>` means that the StringHasChanged event will send a string as an argument to anyone who has subscribed to this event.
public event Action<string> StringHasChanged;
public string myString = "myValue1";
public void ChangeString(string changeStr)
{
myString = "myValue2";
// The string has changed. Let's trigger the event:
StringHasChanged?.Invoke(myString);
}
}
The ?
in the StringHasChanged?.Invoke(myString)
is actually a prettier version of null check. Basically, this code also could be written like this:
if (StringHasChanged != null)
StringHasChanged.Invoke(myString);
It means that we check if the event action has any subscribers actually. If it's null, then nobody is interested in this event, and it won't be broadcasted to not waste the CPU resources.
So, how to react to the event? In the singleton case, you do it like this:
public class MyScript : MonoBehaviour
{
public string myStringReferece;
void Start()
{
// On start we do subscribe to the event
Config.singleton.StringHasChanged += OnConfigStringHasChanged;
// Here the `OnConfigStringHasChanged` is the method that will be executed if the `Config` sends the signal that the string has changed.
void OnConfigStringHasChanged(string newString)
{
Debug.Log(newString);
}
}
Your singleton does not verify if it indeed is a singleton, meaning there could be multiple instances of it being attached to gameobjects.
while using Action as event delegate is technically valid, it would probably be useful to add that Action is just a generic delegate with a single parameter. Usually when communicating via events, you want to pass not only the information, but also the sender, especially in UI communication, to allow for data binding. Action is more geared towards method callbacks.
public delegate void StringEvent(object sender, string text); public event StringEvent StringHasChanged;
Yeah, I agree with both arguments.
But for the sake of simplicity I just used a 'bare bones' example with no if (singleton != null)
check and such. One could say it's better to bring the proper full fledged code to people so they would learn to do things properly right away, but I prefer easily readable examples when the goal is to give the main idea of how things work, and other stuff will come later anyway.
As for the actions being delegates actually, probably it would be useful, but in this case, I believe, the example with Action
is just easier to understand.
However, thanks for the additional explanations, it certainly would be useful to some people who will read this.
Answer by CoreDLL · Mar 15, 2021 at 09:02 PM
Declare your string variable like this :
public string StringName
{
get {return StringName;}
set { StringName = value; Debug.Log(StringName); }
}
You can call a function instead of Debug.Log if you want like here :
public string StringName
{
get {return StringName;}
set { StringName = value; StringChanged(); }
}
public void StringChanged()
{
Debug.Log(StringName);
}
Im not sure if this would work, so let me write this in pseudocode
//MyScript1
string myString = "TEST";
function ChangeString(string changeStr)
myString = changeStr
//MyScript2
string myStringReference
CODE TO CHANGE myStringReference TO myString
Update()
Debug.Log(myStringReference)
Answer by logicandchaos · Mar 17, 2021 at 12:33 PM
[CreateAssetMenu(fileName = "New ScriptableString", menuName = "ScriptableObjects/String")]
ScriptableString : ScriptableObject
{
string value;
}
Then you make an instance of this object in your project, you can call it ConfigString or something. Then you just make a reference to it where ever you need to access it.
Scriptable Object Architecture.