I am working on a method where the player can customize their sprite. I have created an array in my manager script like so:
public Sprite[] spriteImages = new Sprite[5];
In this array, I intend to add 5 png files, that will be used to update the spriteRenderer sprite. I have two problems, I am struggling accessing the array in the sprite's script (which I believe is the best way to change the image). I am also struggling to actually work out how to change the sprites image. Currently I am using this code:
this.gameObject.GetComponent<SpriteRenderer> ().sprite = GameObject.Find ("UIManager").GetComponent<UIManager> ().spriteImages [0];
In my sprite list, I have put 5 png files (2D sprite textures), but when I run the program, it says 'object reference not set to an instance of an object'. Not sure why it gives this error when the array has sprites in?
The gameobject I am referring to 'UIManager' is in a different scene to the one I am using the find from. Is this invalid?
There are a few ways I can think of resolving your issue.
1) Singleton classes. Declare a static instance of your class in which the sprite array is kept i.e. uIManager class. You can make your class singleton using the method shown in link or else you can simply declare like this:
public class UIManager : MonoBehaviour {
UIManager static instance;
void Start() {
instance = this;
}
}
The good part is your can directly refer to your class like UIManager.instance and get all the public variables inside the class. Downside is if you have multiple scripts in same scene it will only refer to one of the instance and you will not be able to access other instances of the script which are present in scene. So this method can only be used for one instance per scene.
2) Saving a reference of your desired script in Unity Editor & by assigning it to public variable in the script. Consider you have Class A which does some calculation and you have Class B which stores some data; let's assume its Sprite array similer to your script. Now in Class A you require that sprite array for assigning. So what you can do is declare a public variable of Class B ie public ClassB classBInstance;. And you will be able to see empty the instance of ClassB field in unity editor. You can directly assign the required reference and voila you will be able to access variables of your class B.
3) Use unity API to find gameobject in which your class is attached.
These are some of the API I use regularly aside from above two method:
You can find single game object with name or will return the first
object with the name. GameObject.Find(string name)
This will return the game objects with a particular tag. You might
want to look into setting a game objects with a tag if you don't
know how to set them.
GameObject.FindGameObjectsWithTag(string tag)
This will return first game object with tag.
GameObject FindWithTag(string tag)
This API returns first gameobject of the desired type.
GameObject.FindObjectOfType(Type type)
This API will return all gamebjects with the given type.Here in both
the cases the type can be of Class type. And this will return all the
game objcts which has your class type attached.
GameObject.FindObjectsOfType(Type type)
4) Setting up your array as static, But if you do that then you will not be able to access it in unity editor and therefore wont be able to assign the Sprites in editor.
For your question I believe first method should more than do the trick.
The spriteImages variable is not declared as static and this means that you need an instance of UIManager to access the spriteImages variable in it. You can mark it as static and you won't need to provide instance of it to access it.
public static Sprite[] spriteImages = new Sprite[5];
That's one way to fix that error but I don't think you should use static for this. UIManager is likely a script attached to a GameObject so all you've got to do is find the GameObject the UIManager is attached to then get the UIManager component from it. That component is the script instance required to access the spriteImages variable without making it static.
private UIManager uIManager;
void Start()
{
//Find the GameObject then the script instance
GameObject uManagerObj = GameObject.Find("UIManager GameObject");
uIManager = uManagerObj.GetComponent<UIManager>();
//Now you can do this with the script instance
this.gameObject.GetComponent<SpriteRenderer>().sprite = uIManager.spriteImages[0];
}
Make sure to replace "UIManager GameObject" from the GameObject.Find function with the name of GameObject the UIManager script is attached to.
Related
I'm not sure how else to word this question. so I will post screenshots of my problem.
How to I make
[SerializeField] private Sprite[] m_Sprites;
show up in the inspector here
I have tried public Sprite[] Sprites; and private Sprite[] m_Sprites as well with no effect, I've seen variables appear there but I cannot seem to figure it out now that I need it. After Further Attempts I have found that a public Sprite Sprite; appears however I require it to be an array of Sprites. I also tried creating a holder class and defining the array inside there and it still doesn't appear.
The only field types allowing default references are fields of a type derived from UnityEngine.Object
... a Sprite[] is an array and does not inhertit from UnityEngine.Object.
As mentioned before you could use a ScriptableObject wrapper class such as e.g.
// this attribute adds an entry to the Asset context menu Add->SpriteCotnainer
[CreateAssetMenu]
public class SpriteContainer : ScriptableObject
{
public Sprite[] sprites;
}
then in your class use a
[SerializeField] private SpriteContainer spriteContainer;
which now is of type ScriptableObject which inherits from UnityEngine.Object so it should show up as default reference field.
So you would need to create this asset via right click on Assets → Add → SpriteContainer. Here you can reference all the sprites you need. Then drag this into the spriteContainer field of your script.
You have two options, either create a gameobject/prefab, add the script to it - and then it will appear.
Or, if it's just a data object you need to access, you can make it a ScriptableObject and create an instance of it.
Either way, you will have to create an instance; you can't assign variables to scripts, only objects with the scripts attached.
Odd question here - I have a gameobject with a class object attached. I instantiate the gameobject, and assign the class object with data from an existing class.
I then instantiate a second gameobject with the exact same class object attached and assign it the same data as the first object. If I modify the class attached to the first gameobject, it is different to the class attached to the second object, even though they both reference (or should) reference the same original class.
Can anyone think why this would happen?
if you make the variables you want to persist in the class your adding static, it will use the same instance for both cases, modifying both when you mod one.
check out this enemy class from unity3d's own dev pages
https://unity3d.com/learn/tutorials/topics/scripting/statics
using UnityEngine;
using System.Collections;
public class Enemy
{
//Static variables are shared across all instances
//of a class.
public static int enemyCount = 0;
public Enemy()
{
//Increment the static variable to know how many
//objects of this class have been created.
enemyCount++;
}
}
They are different instances of the same class, they don't reference the same class.
You should write a duplicate method to duplicate the values
I'm using InvokeRepeating() to call a method in a game. I call InvokeRepeating() in the Start() method of one of the GameObject classes. To set the repeatRate parameter for InvokeRepeating(), I am passing it a public field called secondsBetweenBombDrops.
Unity ignores the value I specify for secondsBetweenBombDrops in the code and instead uses some default value (i.e. 1) when secondsBetweenBombDrops is declared without a static modifier:
public float secondsBetweenBombDrops = 10f;
void Start() {
InvokeRepeating("DropBomb", 1f, secondsBetweenBombDrops);
}
However, once I add the static modifier to secondsBetweenBombDrops, the code behaves as expected and the correct value of 10 is used:
public static float secondsBetweenBombDrops = 10f;
void Start() {
InvokeRepeating("DropBomb", 1f, secondsBetweenBombDrops);
}
Why does this field require the static modifier to use the appropriate value?
In the Unity inspector, the script component shows that secondsBetweenBombDrops is 1. This default value of 1 is present regardless of whether I instantiate the prefab on game start or create prefab instances while the game is running.
The double-edged sword of serialization
Unity wants to make things easier for everyone, including people with limited coding knowledge (beginners, designers).
To help them out, Unity displays data in the inspector. This allows the coder to code and the designer to design by tweaking the values without opening MonoDevelop/an IDE.
There are two ways to have values display in the inspector:
public int myVar = 10;
[SerializeField] private int myOtherVar = 0; // Can also be protected
The second one is better since it complies with encapsulation principle (variables are private/protected and modified via methods or properties).
When you display a variable in the Editor, the value given in the script is only used when dragging the script. Unity then serializes those values and does not care about any script modification anymore. This can lead to confusion if, for instance, myVar is set to 20 inside the script after the fact, it will not be used. The serialization is written in the scene file.
The two lines in the example work exactly in the same way.
Possible solutions
It is possible to get Unity to consider new values in a script by pressing Reset on the settings wheel of the script component. That will also reset all the other variables of the component, so only do this if that is intended.
Making the variable private and omitting the attribute [SerializeField] will disable the serialization process, so Unity will no longer look in the scene file for a value to display - instead, the value will be created at runtime by the script.
When adding a component to Unity, a new object of the type of the component is created. The values that are displayed are the serialized values from that object. For this reason, only member values can be displayed and static variables are not, as they are not serializable. (This is a .NET specification, not strictly specific to Unity.) Because Unity does not serialize static fields, this is why adding the static modifier seemed to solve the problem.
Explaining the OP
In the OP case, based on the comments, your public field was showing a value of 1 in the editor. You thought this value was a default one, when it was actually the value you most likely gave to the field when originally declaring it. After you added the script as a component, you made the value 10 and thought it was buggy as it was still using the value of 1. You should now understand that it was working just fine, as designed.
What does Unity serialize?
By default, Unity will serialize and display value types (int, float, enum and so on) as well as string, array, List and MonoBehaviour. (It is possible to modify their appearance with Editor scripts, but this is off-topic.)
The following:
public class NonMonoBehaviourClass{
public int myVar;
}
is not serialized by default. Here again, this is .NET specification. Unity serializes MonoBehaviour by default as part of the engine requirement (this will save the content to the scene file). If you wish to display a "classic" class in the editor, just say so:
[System.Serializable]
public class NonMonoBehaviourClass{
public int myVar = 10;
}
Obviously, you cannot add it to a game object so you need to use within a MonoBehaviour:
public class MyScript:MonoBehaviour{
public NonMonoBehaviourClass obj = new NonMonoBehaviourClass();
}
this will display the object in the inspector and allow modifications to the myVar variable in the instance of NonMonoBehaviourClass. And again, any changes to myVar within the script will not be considered after the value is serialized and stored to the scene.
Extra tips on displaying things in the inspector
To finish, interfaces are not displayed in the inspector either since they don't contain any variables - just methods and properties. In debug mode, properties are not displayed by default. You can change this mode using the button with three lines in the top right corner of the Inspector. The first two settings are Normal/Debug. The first one is the default one, the second will also display private variable. This is useful to watch their values but cannot be altered from editor.
So if you need an interface to be displayed, you would have to consider an abstract class as it offers a similar functionality (except for multi inheritance) but can be a MonoBehaviour.
References:
http://docs.unity3d.com/ScriptReference/SerializeField.html
http://docs.unity3d.com/Manual/script-Serialization.html
https://www.youtube.com/watch?v=9gscwiS3xsU
https://www.youtube.com/watch?v=MmUT0ljrHNc
Title explains it all, I want to know how to access variables from another script. I've searched online, but found nothing that's worked.
You could make a class, instantiate an object of the class and access propterties.
Or you could use static variables.
Or even beter, lets say you have a GameManager.cs script attached to an empty object called GameManager. And you want to access its variables form the LevelManager.cs script. You do this inside the LevelManager.cs
public GameManager gameManager;
Then you can drag and drop your GameManager empty object to this public field, and everytime you want to access a variable you type gamemanager.yourVariableHere
Or, if you dont want to drag and drop:
in the start method...
void Start()
{
gameManager = GameObject.Find("GameManager");
//this way it finds your object automatically
}
Hope it helped, good luck.
First you need to make your variables public. Then get the GameObject, get the Componend and access your variable.
var variable = GameObject.Find("name").GetComponent<ScriptClass>().yourVariable;
To add to the previous answer, if your class is a pure c# class and doesnt inherit from monbehaviour then must create an instance of class. If you want global access to certain variables you should look into static variables or implement a singleton pattern
I have a game object. In the "Inspector" of the game object there are some scripts. Now I want to change a public variable that is attached to the script. How can I do this?
GameObject.Find("Wanderer").GetComponent<SteerForTeether>().TetherPosition = "whatever";
That should do it I suppose
A script attached to an object is a Component of the object. To access to a component, you need to call GetComponent<TYPE>() method from the game object. The type of the component would be same as the name of the script you are trying to reach.
Here's an example:
GameObject.Find("Player").GetComponent<PlayerController>().KillPlayer();
The name of the script, in this case, is PlayerController.cs. KillPlayer() is a public method defined in this script.