Unity 2D Change sprite from a list of sprites - c#

I have a gameObject as a sprite in my 2D game, and then in a different script I have an array of 2d textures, in a sprite array. I simple want to change the sprite's sprite property but get the error 'object reference not set to an instance of a object'.
Here is the line throwing the error, which is in one script.:
this.gameObject.GetComponent<SpriteRenderer> ().sprite = GameObject.Find ("UIM").GetComponent<Manager> ().spriteImages [1];
While the array, is in a different script, attached to the object 'UIM' in a different scene to the one I am trying to access it from (not sure if this causes the problem), is defined with:
public Sprite[] spriteImages = new Sprite[5];
Why am I getting this error? I have filled the array with the textures so cannot see the problem.

You are trying to access a script & variable from another scene. In unity whenever you load a new scene your old scene values will be destroyed and that will lead to 'object reference not set to an instance of a object' error. You can bring the tell the unity not to destroy the object which you will need in future scenes by calling DontDestroyOnLoad. This will allow the gameobject to persist across the scene.
One more thing you have to keep in mind is that if you are returning to the old scene where you marked the Object UIManager as dontdestroyonload then make sure the next instances are deleted and managed from the scene which is reloaded else whenever you keep on reloading the old scene with UImanager it will keep on piling up the instances of it across the scenes. The example in the unity with dontdestroyonload should be more than enough to solve your problems. Here is a snippet shown in Unity API which you can refer:
void Awake()
{
GameObject[] objs = GameObject.FindGameObjectsWithTag("music");
if (objs.Length > 1)
{
Destroy(this.gameObject);
}
DontDestroyOnLoad(this.gameObject);
}

You can try:
public GameObject[] objs =
SceneManager.GetSceneByName(sceneToLoad).GetRootGameObjects();
foreach(GameObject go in objs)
{
if(go.name == "UIM")
{
this.gameObject.GetComponent<SpriteRenderer> ().sprite =
go.GetComponent<Manager> ().spriteImages [1];
break;
}
}

Related

Issue with storing Instantiated prefabs

(I'm new to Unity and probably jumping the gun a bit here) -
I'm getting myself a bit confused regarding instantiating prefabs. I have a GameObject (gameManager) that instantiates a prefab, and then sets the reference of the instantiated object to my LevelManager (stores a static gameObject)
I call on the reference to the instantiated object using the LevelManager (LevelManager.enemyPrefabInstance) in other scripts (for reasons such as grabbing the animator component). Because it is stored as static, I think every instantiation has shared components. If I instantiate two enemies and kill one at runtime (once dead the gameObject destroys itself), the 2nd looses its components because they've also been destroyed. I know I could change the prefab reference from static to non-static but I am trying to teach myself about static objects and how they work, so that defeats my purpose.
Is there a way to store all instantiated objects as a single static gameobject, so they all keep have their own individual components?
Many thanks in advance
EDIT - I'm being told instances do not share components regardless if they are stored as static or not. The exact error I get when I try and destory a 2nd instantiated clone is:
MissingReferenceException: The object of type "Animator" has been destroyed but you are still trying to access it. Your script should either check if it is null or not destroy the object.
GameManager Script:
Void Awake()
{
GameObject enemyPrefabInstantiated = Instantiate(enemyPrefab, position, rotation);
//instantiate gameObject
LevelManager.enemyPrefabInstantiated = enemyPrefabInstantiated;
//Set reference for LevelManager
}
LevelManager:
public static GameObject enemyPrefabInstantiated; //Stores static GameObject
Animator Script:
public Animator enemyAnim;
void Start()
{
enemyAnim = LevelManager.enemyPrefabInstantiated.GetComponent<Animator>();
//Gets animator component off instantiated prefab
}
void OnDestroy()
{
Destroy(enemyAnim.gameObject);
//destroy gameobject
}
Your reference is:
GameObject prefab; //your prefab you want to instantiate
GameObject obj1 =Instantiate(prefab);
//create your first gameobject
GameObject obj2 =Instantiate(prefab);
//and the second
The obj2 variable now only references the instantiated copy of the prefab.

why do we need to Instantiate the loaded asset after LoadAsset in Unity AssetBundle?

In the manual of AssetBundle usage, https://docs.unity3d.com/Manual/AssetBundles-Native.html
I can see that when use LoadAsset loaded an GameObject, why we need to do the extra step Instantiate it ? this will create another copy of the prefab, I'm confuse why we do not use the loaded prefab GameObject directly?
public class LoadFromFileExample extends MonoBehaviour {
function Start() {
var myLoadedAssetBundle = AssetBundle.LoadFromFile(Path.Combine(Application.streamingAssetsPath, "myassetBundle"));
if (myLoadedAssetBundle == null) {
Debug.Log("Failed to load AssetBundle!");
return;
}
var prefab = myLoadedAssetBundle.LoadAsset.<GameObject>("MyObject");
Instantiate(prefab); // why do not use prefab directly ?
}
}
Instating prefabs really has nothing to do with AssetBundle. It's also the-same when using the Resources.Load API. You need to understand what prefab is before you can answer this question.
Prefabs are just GameObjects and Components put together and made to be re-usable. It is used so that if you want the-same type of Object, you won't have to manually create them over and over again every-time. You just instantiate the already created and saved prefab. This is really important when you want to share resources between multiple scenes.
When you load a prefab, it is only stored in the memory waiting to be used or instantiated. It's not visible in the scene or Hierarchy tab. There is no reason the loading function should also instantiate the prefab because you may not need it right when it is loaded. Usually prefabs are loaded when game is loading and then appropriate prefabs are instantiated during run-time when needed.
Finally, instantiating a prefab, crates a copy of it and put's in the scene. Any operation should be done on the instantiated object the loaded prefab.

How to find a sibling GameObject by name; Unity2d c#

I am trying to develop a small 2D Unity platformer to learn to work with Unity's interface. I have been having an issue trying to make a gameobject track clones of an enemy and follow it. The function is to make it so when you hover on the enemy clone, it shows the enemy's health. But when I hover on the enemy, it tracks the position of the original enemy, not the clone. Both gameobjects have the same name. What I want is the GameObject HoverDataDisplay (as shown in the screenshot) to track its sibling, Enemy.
The current code that I have for the tracking script is as shown:
private GameObject Enemy;
void Start() {
Enemy = GameObject.Find ("Enemy");
}
void Update(){
transform.position = new Vector3 (Enemy.transform.position.x - 0.57f, Enemy.transform.position.y + 1.5f, Enemy.transform.position.z);
}
But the GameObject (HoverDataDisplay) Only follows the original enemy.
Thanks for your help!
In Unity, you can use the forward slash "/" to find a Child of an Object. Since the name of the parents are different, you can easily find them like this:
GameObject.Find("EnemyObject/Enemy");
And the second HoverDataDisplay:
GameObject.Find("EnemyObject(Clone)/Enemy");
It's not really a good idea to let your GameObject use the default "Clone" name. You should rename it after instantiating it so that it will be easy to find. Only do this if you need to find the Object after instantiating it.
This script is attached to the HoverDataDisplay GameObject
You can actually get it's parent Object which is either EnemyObject or EnemyObject(Clone) with transform.parent then use the FindChild to find the enemy Object.
//First, Find the Parent Object which is either EnemyObject or EnemyObject(Clone)
Transform parent = transform.parent;
//Now, Find it's Enemy Object
GameObject enemy = parent.FindChild("Enemy").gameObject;
I recommend you use this method instead of the first one I mentioned. The first one is mentioned so that you will know it can be done.
EDIT:
Transform.FindChild is now deprecated. You can use Transform.Find to do that same exact thing.
You can get a reference to the parent then search through the parents children
// get a reference to the rb of the parent
parentRigidbody = gameObject.GetComponentInParent<Rigidbody>();
// a reference to the camera in a sibling object
playerCam = rigRigidbody.gameObject.GetComponentInChildren<Camera>();

Unity 2D: Destroy + Instantiate new GameObject vs Changing state and sprite

I am creating prefabs for farmland in my 2D game. Since I want all ground tiles to turn into farmable-land when hit with a hoe, I am worried about performance (Since there will be hundreds of these GameObjects in a scene).
Would the best thing be to Destroy the ground tile and Instantiate a farm tile in it's position, or would it be better to create a more generic Script that is attached to every Ground tile(?), which has states like:
GROUND, FARMABLE, PLANTED
, then depending on the state I change behaviour and set a sprite like: tile.GetComponent<Image>().sprite = Resources.Load<Sprite>(pathToSprite);
Maybe I'm missing a better option but these are the ones I can think of.
Destroyin and instantiating hundreds of game objects during runtime is a recipe for disaster, memory fragmentation and GC going wild which will kill performance.
The second solution is way better. Use an enum for all the possible states of a tile in your game logic, and then change the Sprite field of the Sprite Renderer component accordingly.
PSA: Don't use GetComponent and Resources.Load every time you need to change a sprite, get a reference to the Sprite Renderer component and to a Sprite[] array which contains all possible state images to use in Awake, and then use those references to change the sprite image when needed.
Edit: Answering your question in the comment.
Be sure that the Sprites are all in the Resources folder of your
project, they can be in a subfolder, i.e.: Resources/Sprites.
Check that the path string is correct, i.e. if the sprite asset is called Circle, and it's in the Resources/Sprites folder,
path must be "Sprites/Circle".
Code example:
public class MyClass : MonoBehaviour {
Sprite[] spritesArray = new Sprite[10];
void Awake() {
spritesArray[0] = Resources.Load<Sprite>("Sprites/Circle");
}
}
Try This:{ tile.GetComponent(/).sprite = Resources.Load(pathtosprite); }

"Object reference" needed in script

I am currently working on a foxes & rabbits simulation, and I am completely stuck on "breeding".
The way I have built the simulation, three scripts are used; “TheGame”, “FoxScript” and “RabbitScript. Since the foxes and rabbits are essentially the same, we can reduce these three to two scripts; “RabbitScript” and “TheGame”. The RabbitScript is attached to the respective prefab; the “rabbitPrefab”, whereas TheGame is attached to an empty GameObject.
TheGame instantiates a number of RabbitPrefabs, which then move, age and breed. Since the build is supposed to collect and present data at a later stage, the rabbits are included in a list as well as being counted. This list is found in the main script, and when the rabbits breed, the offspring needs to be included in this list as well as adding to the counter.
I have tried instantiating a primitive with this method, and it works.
The Breed function in the script attached to the rabbits:
void Breed(){
float p = Random.Range (0.0f, 1.0f);
if (p < probability2breed) {
position = gameObject.transform.position;
TheGame.BreedRabbit(position);
}
}
And the BreedRabbit method in TheGame script:
public static void BreedRabbit(Vector3 position) {
GameObject rabbit = Instantiate(RabbitPrefab) as GameObject;
rabbit.transform.position = new Vector3(position);
Rigidbody gameObjectsRigidBody = rabbit.AddComponent<Rigidbody>();
rabbit.GetComponent<Rigidbody>().useGravity = false;
rabbit.name = "Rabbit#:" + rabbitCount;
rabbit.tag = "rabbittag";
rabbits.Add(rabbit);
rabbitCount++;
}
NOTES: (I figure a lot of this code seems pointless, so to answer any questions about that beforehand: I use collider to handle interactions between the agents involved, and to my understanding this calls for a rigidbody. With rigidbody they started falling, even without mass, so I had to turn of gravity. The tags are to my understanding needed for collision handlig as well.I could probably skip the count and just count the list, but this shouldn't matter now)
It keeps asking for an object reference , and I just can't figure out how this can be solved?
THe error message: "an object reference is required for the nonstatic field method or property"
I'd assume the object reference error occurs on this line?:
GameObject rabbit = Instantiate(RabbitPrefab) as GameObject;
If this is the case, it may be because the prefab hasn't been set, i.e, the script doesn't know what RabbitPrefab is.
You could set a variable in the script, and then drag your prefab onto the corresponding slot into the inspector:
public GameObject theRabbitPrefab;
GameObject rabbit = Instantiate(theRabbitPrefab) as GameObject;
If this is not the case, can you edit your question to where you are getting the error? Surely the error states which line of code the error is being generated from? :)
Edit: From Diego, if this is the case, you can add the rigidbody and configure it in your prefab, and you won't need to do it in the code for every new rabbit!

Categories