Unity, Class on instantiated prefab is different between objects - c#

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

Related

CS0120 Attempting to invoke a method on a different script, but this error is produced

I'm very new to coding, so apologies if any terminology is wrong. The code is trying to change a bool from false to true (it will do something more significant later, but I'm having issues doing this which is much simpler). I'm doing this in unity if that changes anything.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AnimalPositionalUpdates : MonoBehaviour
{
public PositionArrays ScripBeingAcessed;
// Use this for initialization
void Start()
{
//Find where the position is going to be input
int ArrayPosition = ScripBeingAcessed.FoodPosition.Length + 1;
PositionArrays.PositionBeingUpdated = transform.position;
}
// Update is called once per frame
void Update ()
{
}
}
Second Script (the one that I'm trying to influence)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PositionArrays : MonoBehaviour {
public bool PositionInProcess = false;
public Vector3 PositionBeingUpdated;
public Vector3[] FoodPosition = new Vector3[0];
// Use this for initialization
void Start () {
}
public void ArrayPosUpdate()
{
PositionInProcess = true;
}
// Update is called once per frame
void Update ()
{
}
}
Any help would be appreciated. As far as I know, there is some issue with me trying to use a static method like an instance??? Idk where it starts being static, and putting static in the method declaration for both only causes the same error to appear again referencing different components.
The error Is on line 16 of the first script.
Full error message
"CS0120 An object reference is required for the non-static field, method, or property 'PositionArrays.PositionBeingUpdated' 16 Active"
Obviously you are lacking fundamentals of OOP and delved into Unity development right away. You can do this, but I guess that programming in Unity will Taint your C# if you did not learn it properly. Having said that, you could certainly do it, but I'd suggest learning proper C# first and then use that sometimes uncommon Unity style.
There is some confusion around how the fields of your class are used, so let me explain: Usually, a class is a blueprint of sorts of which you can create instances. All of these instances adhere to that blueprint, which tells you exactly how you can use those instances (this is sometimes called implicit interface) and which data they contain (this is where Unity is somewhat sloppy). An instance bundles up the data with the methods you use to access and manipulate the data and you "always" need an instance to access these members, usually through a variable.
In your case, this variable is ScripBeingAcessed. This is the variable that holds your instance (at least if you did not forget to assign it) and therefor, you'd need the variable to access the field PositionBeingUpdated
void Start()
{
//Find where the position is going to be input
int ArrayPosition = ScripBeingAcessed.FoodPosition.Length + 1;
ScripBeingAcessed.PositionBeingUpdated = transform.position;
}
On static
Besides the members of a class that form the actual blueprint to create instances from, there are members that are accessed through the class itself. These are called static members. Anyway, since they are not bound to an instance, static members that contain any data will be the same regardless of where you access them from. This may be fine, if the static members are private (only instances of that class can access it) but I'd avoid it otherwise, since this creates a global state, which is not safe of being manipulated from other classes and can thus lead to hard to debug errors.

Unity2D changing sprite image from array c#

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.

Unity C# : custom Object is null BUT I can access its properties

I found a way to get around this issue in the code, but I would like to understand why this is happening because I want to learn C# well. I´ve searched a lot and could simplify the problem, here's the simplified code:
/*MyCustomComponent.cs, this is attached to a game object in the scene*/
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class MyCustomComponent : MonoBehaviour {
void Start () {
Test testInstance = new Test();
Debug.Log (testInstance);
Debug.Log (testInstance == null);
Debug.Log (testInstance.Equals(null)); //Also tried the same with Equals()
Debug.Log (testInstance.value);
}
}
/*Test.cs*/
using UnityEngine;
using System.Collections;
public class Test : Object {
public int value = 7;
public Test(){
}
}
Console Output:
null
true
7
Why is this happening? How can be possible an Object is null but returns a property value?
Because Unity.Object works in a very peculiar way, basically it contains data regarding the managed object while pointing to the native object (crudely speaking).
Let's make an example: you create a new object inheriting from MonoBehaviour. Now you have two objects, one on the native side and one on the managed side. You can access the instanceID propriety of the managed object thanks to the fact that MonoBehaviour inherits from Unity.Object. Basically, the latter is used to show all the relative data of the object in the Inspector.
Now, you want to Destroy this game object by calling the same named method. The native object is actually destroyed, but the managed object is not, since all managed objects can only be destroyed by the garbage collector. It's at this point that Unity.Object becomes null: it's a trick of the Unity engine uses in order to avoid that you try to access a native object which doesn't exist anymore. Eventually, when the GC is called, this Unity.Object will be destroyed too.
And this is why you should never create an Unity object which inherits directly from Unity.Object, but only from MonoBehaviour or ScriptableObject.

How do I use variables in a separate script in Unity3D?

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

Getting classes in XNA to 'talk' to eachother

I have two classes in XNA.
maingame.cs
and
player.cs
In my maingame.cs I have a rectangle that is drawn wherever the mouse is, I use this for clicking.
My player.cs has a rectangle for the players sprite.
I Have No idea how to make these two classes 'talk' to eachother so that I can have something like
if (ButtonState.Pressed == mouseState.LeftButton && mouseRectangle.Intersects(playerRectangle))
{
//draw something
}
The problem is that playerRectangle is in the Player.CS and the mouseRectangle is in the maingame.CS
How do I get these two to talk to eachother? I've been Googling for hours now with no luck.
Player.CS looks like
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
namespace PlanetDefence
{
class Battleship
{
//Textures
Texture2D playerBattleshipTexture;
//Rectangles
Rectangle playerBattleshipRectangle;
//Integers
public Battleship(Texture2D newPlayerBattleshipTexture, Rectangle newPlayerBattleshipRectangle)
{
playerBattleshipTexture = newPlayerBattleshipTexture;
playerBattleshipRectangle = newPlayerBattleshipRectangle;
newPlayerBattleshipRectangle = new Rectangle(100, 100, 100, 100);
}
public void Update()
{
}
public void Draw(SpriteBatch spriteBatch)
{
//Draw the player Battleship
spriteBatch.Draw(playerBattleshipTexture, playerBattleshipRectangle, Color.White);
}
}
}
I'm trying to make it so that my mouse rectangle in MainGame.CS is able to click it by doing
if (ButtonState.Pressed == mouseState.LeftButton && mouseRectangle.Intersects(playerBattleshipRectangle))
{
playerBattleship.Draw(spriteBatch);
}
I'm not sure if I'm stating the obvious here, but you need a public accessor for your Rectangle in your Battleship class. If you don't understand what that means then you need to do some reading on the basics of object-oriented programming. For now, though, if you modify the code of your Battleship class to say
public Rectangle playerBattleshipRectangle;
... you can then access it from your maingame.cs' reference to the player object using
player.playerBattleshipRectangle
(assuming your player is a Battleship class. If it's not then you need to be more clear in your question and post the class source code for whatever class your player is. You say "player.cs" but post the class Battleship-- which is it? If the filename is player.cs but the class name is actually battleship you should change one of them so that they are the same. Practice good, easy to understand class and variable names; be as descriptive as possible without being too wordy).
By default, if you include no scope modifier (public, private, protected, internal...) before your member fields in a class, then they are set to private and not accessible by other classes.
You can also use Properties to have more control over access to a class' member fields. For instance,
private int health;
public int Health
{
get { return health; }
set { health = MathHelper.Clamp(health, 0, 100); }
}
this code contains a private (only this class can access it) definition of the health of an object. It also provides a public way for other classes to 'see' the health of that object and change this health. As you can see in the 'set' section, when another class sets the health of this object it is automatically clamped between 0 and 100. You could also omit 'set' entirely and no other class could change the health, they could only see it.
This is all the basics of object-oriented programming in C#. I strongly encourage you to start from the basics if you don't know them. You cannot successfully create a game without understanding scope, properties, inheritance, object instances and references, just to name a few.
A quick summary of some relevant definitions from this post:
field - aka a variable; a basic trait stored in a class as a 'member'. i.e., 'health' or 'boundingBox'.
property - an accessor for a field which is useful for defining how outside classes can see and modify it.
instance - a 'real' object which is stored in memory. a class only defines the behavior of an object; it must be made into an instance for the object to actually exist. A class like Battleship can be used to make unlimited instances of battleships in memory. When you say Battleship player = new Battleship(), you are creating an instance of Battleship called 'player'.
private - placing 'private' before a field means only this class can see it.
public - placing 'public' before a field means all classes can see it.
protected - placing 'protected' before a field means only this class and any classes inherited off of this class can see it.
internal - placing 'internal' before a field means only classes in this class' namespace can see it.
I apologize if I underestimate your knowledge of the subject.
One of the classes needs to hold a reference to the other.
With such a reference, it can call methods on it, set properties etc.
You can do this by passing in an instance in the constructor or as a property.
public Maingame(Player player)
{
// use the Player instance
}
To access it outside of the constructor, you should use a field and assign it in the constructor (or property setter, whichever you decide to use):
Player myPlayer;
public Maingame(Player player)
{
myPlayer = player;
// use the myPlayer field in the class
}
To avoid tight coupling, you may want to investigate using events.

Categories

Resources