- Home /
Reference a static variable from an instance
Hello, fairly new to Unity and C#, I have a Character script I'm using to make instances of characters, and a PlayerStats script storing static variables, trying to give each instance it's own variable they can modify in PlayerStats by either referencing it in Unity or possibly by using an ID number (e.g. char1 - var1, char2 - var2, etc.), thought about just storing this variable in the Character script, but trying to use these variables as a currency might become troublesome if too many characters are added, anyway of doing something like this? Or am I going about this completely wrong? Couldn't really find anything like this scenario online, so help would be much appreciated.
Thanks for reading :)
Answer by farmvillehd · Jun 23, 2020 at 11:16 AM
Spent sometime searching how to use lists and made a super simple system that does what I want that I'm very happy with
public List<float> matValueList = new List<float>();
public float Material0;
public float Material1;
public float Material2;
void Start()
{
matValueList.Add(Material0);
matValueList.Add(Material1);
matValueList.Add(Material2);
Make a list in your playerStats
public PlayerStats playerStats;
public int Id = 0;
public void Increase();
{
playerStats.matValueList[Id] += 1
}
Then all you gotta do is get some kind of reference to it, and give an iD, now the Character with ID 0 increases Material0. This is probably pretty trivial to more experienced programmers. But this newbie is proud to of figured it out, hopefully this helps anyone with the same issue I was in :)
While this does probably what you want, I don't quite see the reason for your $$anonymous$$aterial0, $$anonymous$$aterial1 and $$anonymous$$aterial2 fields. The list itself is also serialized in the inspetor and the user (you) can change / add / remove values there. This is also quite dangerous in your case. Since you create the list in the field initializer which is then overwritten by the deserializer with the list shown in the inspector, you could have already values in that list when Start is called. In that case your $$anonymous$$aterial0 / 1 / 2 is just appended at the end of that list.
Also keep in $$anonymous$$d that float variables can not be "referenced". So all your "$$anonymous$$aterial" fields do is providing initial values. When you do
playerStats.matValueList[0] += 1;
You do change the first element in the List, but you do NOT change the value of $$anonymous$$aterial0. That field has no relation to the values in the list.
I would recommend that you either:
Get rid of those "$$anonymous$$aterial" fields and just setup your initial values in the inspector directly by setting the size of the List to 3 and then input your 3 initial values there.
If you want to keep those "$$anonymous$$aterial" initializer variables, you should put a
[System.NonSerialized]
attribute before your List so it does not get serialized in the inspector. It is still public, but not serialized anymore so it won't show up in the inspector.
If this is about creating a list of different materials, I would strongly recommend to create a seperate "$$anonymous$$aterial" class (NOTE: do not call it $$anonymous$$aterial ^^ find a more suitable name to avoid issues with Unity's $$anonymous$$aterial class) which contains all the information needed, create an instance of that class for every material you want have and put those in a list. This is commonly done with ScriptableObject assets. So you can simply create as many of your materials in Unity as standalone assets. You can give them a name, maybe an initial value, a $$anonymous$$ and max value and whatever you might need.
I actually am using ScriptableObjects to store names, sprites, and max values (and they're a life saver). the reason I'd prefer the $$anonymous$$aterial list over a class for them is just for easier access, and that seems to me currently the best thing for what I'm going for, from what I understand, if I wanted to use a value from a "$$anonymous$$aterial Class" I'd have to get the component for that particular class, and that might get complicated after a bit for someone of my skill level lol. I appreciate the feedback a lot on my previous code and currently applying changes to it. Thanks for the help :)
Answer by ShadyProductions · Jun 16, 2020 at 05:17 AM
If the PlayerStats script is also on the same object as the character script you can do:
public class Character : MonoBehaviour
{
public PlayerStats PlayerStats { get; private set; }
private void Start
{
PlayerStats = GetComponent<PlayerStats>();
}
}
public class PlayerStats : MonoBehaviour
{
public int Health;
public int Shield;
}
This is close to what I need, anyway to reference a particular variable when getting a component?Something like "If character ID = 0, Var = Health, or If character ID = 1, Var = Shield"? The end result is doing something like "Var++" and having Character 1 increase health, and Character 2 increase shield
Will update if I find out on my own, thanks a bunch for the info :)
EDIT: I know I could use a switch to do something like this, but I'm pretty sure each character would have to run it every frame in order for it to work, and that'd get pretty messy with each new character being added, I'd much rather have a direct reference if it's possible
Sure imagine:
public class Character : $$anonymous$$onoBehaviour
{
public int Id;
public PlayerStats PlayerStats { get; private set; }
private void Start
{
PlayerStats = GetComponent<PlayerStats>();
}
}
Define this method somewhere:
private Character[] _characters;
public Character GetCharacterById(int id)
{
if (_characters == null)
{
// Find all objects of type Character in scene
_characters = FindObjectsOfType<Character>();
}
return _characters.FirstOrDefault(a => a.Id == id);
}
Now you can do:
var character = GetCharacterById(id);
character.PlayerStats.Health++;
Been having issues getting this to work, trying working it out myself for a couple hours but couldn't find it out.
Set up a test to try and work it out
Character:
using UnityEngine;
public class Character : $$anonymous$$onoBehaviour
{
public int Id;
public PlayerStats PlayerStats { get; private set; }
private void Start()
{
PlayerStats = GetComponent<PlayerStats>();
}
}
PlayerStats:
using UnityEngine;
public class PlayerStats : $$anonymous$$onoBehaviour
{
public int material1;
public int material2;
}
Game$$anonymous$$aster:
using UnityEngine;
using System.Linq;
public class $$anonymous$$aster : $$anonymous$$onoBehaviour
{
private Character[] _characters;
public Character GetCharacterById(int id)
{
if (_characters == null)
{
// Find all Characters in scene
_characters = FindObjectsOfType<Character>();
}
return _characters.FirstOrDefault(a => a.Id == id);
}
//Test if Char is found
public void IncreaseVar()
{
var character = GetCharacterById(0);
character.PlayerStats.material1++;
}
}
Running the code "character.PlayerStats.material1++;" is giving this error "NullReferenceException: Object reference not set to an instance of an object $$anonymous$$aster.IncreaseVar () (at Assets/$$anonymous$$aster.cs:23)"
It seems like it can't find the instance to reference, but I'm not really sure what to do about this, did I misplace something? Let me know if you get the chance Thanks for helping this helpless novice lol
I would recommend to use Awake to setup connections between components since Start is usually used to do the first interactions. When you setup all necessary connections in Awake it's guaranteed to have them initialized for other scripts inside Start.