I have a baseclass that inherits from Monobehaviour. How do i cast my monobehaviour to the base class when finding it in the hierarchy?
GameManager : MonoBehaviour {
public MonoBaseClass MyThing;
void Awake() {
MyThing = GameObject.Find("Child") as MonoBaseClass;
}
}
MonoBaseClass : MonoBehaviour {
public void BaseClassMethod() {}
}
GameObject.Find returns a GameObject, a MonoBehaviour is a component of a GameObject. That's why you can't cast the GameObject to the MonoBaseClass.
Instead you have to get a reference of the GameObject and then get the Component:
GameObject childGameObject = GameObject.Find("Child");
MyThing = childGameObject.GetComponent<MonoBaseClass>();
You need to use FindObjectOfType<MonoBaseClass>(), i.e.:
void Awake() {
MyThing = FindObjectOfType<MonoBaseClass>();
}
Problem with both Find and FindObjectOfType is: They are quite slow and you will get the first hit from the entire scene.
If the Component you are looking for is on a Gameobject which is a child of the current GameObject (which seems the case) than you can just use:
MyThing = GetComponentInChildren<MonoBaseClass>();
https://docs.unity3d.com/ScriptReference/Component.GetComponentInChildren.html
Of course this will anyway still only get the first hit. For more use an array and GetComponentsInChildren<T>()
Related
Hiya - so i think i'm at a bit of a misunderstanding here with using abstract classes so if it isn't too much trouble i need someone to explain to me where i've gone wrong here;
So i have an abstract class structured like this:
[RequireComponent(typeof(Rigidbody2D))]
[RequireComponent(typeof(Collider2D))]
public abstract class Enemy : MonoBehaviour {
// Physics
protected Rigidbody2D rb;
protected Collider2D col;
protected virtual void Awake(){
// Physics
rb = this.GetComponent<Rigidbody2D>();
col = this.GetComponent<Collider2D>();
col.isTrigger = true;
}
}
Then a class inheriting it:
public class Whizzer : Enemy {
protected override void Awake(){
// Physics
rb = GetComponent<Rigidbody2D>();
rb.bodyType = RigidbodyType2D.Dynamic;
rb.gravityScale = 0f;
col = GetComponent<Collider2D>();
col.isTrigger = true;
}
}
However the problem i'm having is that when i reference rb or col inside the Whizzer class it's (i think) accessing the variables defined in the abstract Enemy class - this results in only one instance of a gameobject with this script working at a time since the last script to run assigns their rigidbody and collider as the variables in the abstract Enemy class
My question is how can i create seperate instances of the variables rb and col for every Whizzer class instance created whilst keeping the variables desired inside the abstract enemy class?
Or if this is even the right way to go about doing this?
Thankyou in advance!
They should be saparate. If you want a variable to be shared among all instances you should use "static" modifier. But you do not use that so each instance of Enemy (Whizzer is also an Enemy) should have it's own value for these fields. How do you know that they point to the same instances of collider and rigidbody?
About your implementation you are slightly missing the point:
public abstract class Enemy : MonoBehaviour {
protected Rigidbody2D rb;
protected Collider2D col;
protected virtual void Awake(){
rb = this.GetComponent<Rigidbody2D>();
col = this.GetComponent<Collider2D>();
col.isTrigger = true;
}
}
Then a class inheriting it:
public class Whizzer : Enemy {
protected override void Awake(){
base.Awake();
rb.bodyType = RigidbodyType2D.Dynamic;
rb.gravityScale = 0f;
}
}
That makes more sense imho, you should use base method in most cases instead of pasting the same chunk of code to override
In my game I have coins, in my coin script I have an OnDestroy() function but I get this error "NullReferenceException: Object reference not set to an instance of an object
coinscript.OnDestroy () (at Assets/Scrips/coinscript.cs:9)"
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class coinscript : MonoBehaviour
{ public gamemanager GameManager;
void OnDestroy()
{
GameManager.plusScore(10);// FindObjectOfType<gamemanager>().pluseScore(10); gets the same error
}
}
fixed it thanks to help from KiynL
using System.Collections.Generic;
using UnityEngine;
public class coinscript : MonoBehaviour
{ public gamemanager GameManager;
bool destroyed = false;
void OnDestroy()
{
if (destroyed = false)
{
GameManager.plusScore(10);
destroyed = true;
}
}
}
This is because the object may be destroyed multiple times in one frame. Fix it.
void OnDestroy()
{
if (gameObject) GameManager.plusScore(10);
}
You need to go into the properties of whatever object you attached that script to, and assign Game Manager an object that has a gamemanager script attached to it.
As ted said its probably because you haven't set GameManager from inspector. you need to drag a gameObject that has gamemanager component on it.
but I recommand making plusScore function static and calling it without an object, if you make it statis you also have to make varible that stores score static as well, some thing like this:
public class gamemanager : MonoBehaviour
{
static int Score = 0;
public static void plusScore(int score)
{
Score += score;
}
}
then calling it like this:
gamemanager.plusScore(10);
I am trying to use GameObject.Find within a prefab to reference an object that exists in the scene. The name of the object is exactly the same as the actual object.
LoseLives loseLives;
GameObject loseLivesing;
private void Awake()
{
loseLivesing = GameObject.Find("MasterManager");
}
private void Start()
{
loseLives = loseLivesing.GetComponent<LoseLives>();
target = Waypoints.waypoints[0];
}
loseLives.LoseLife();
Null Reference
I get a null reference when "loseLives.LoseLife();" runs.
Any help with solving the no reference problem would be greatly appreciated!
If you think there is a better way to create the connection between a script on a prefab and a script on a in-scene gameObject could you please explain it or point me to a tutorial, because in all my research I did not have any luck.
Thank you!
It seems like your LoseLives is a singular, constant object.
First Method
Simply making use of the inspector by exposing the fields.
public class YourScript : MonoBehaviour {
[SerializeField]
private LoseLives loseLives;
// ..
private void Start() {
loseLives.LoseLife();
}
// ..
}
It comes with the problem of needing to constantly drag your constant object into the inspector whenever you need it.
For a Prefab, it works if the target object is part of the Prefab, or the Prefab is generated in the same scene as the targeted object.
Second Method
Using singletons.
LoseLives.cs
public class LoseLives : MonoBehaviour {
public static LoseLives Instance {
get => Instance;
}
private static LoseLives instance;
private void Start() {
instance = this;
}
public void LoseLife() {
// ...
}
}
YourScript.cs
public class YourScript : MonoBehaviour {
// ..
private void Start() {
LoseLives.Instance.LoseLife();
}
// ..
}
This works if you have a global, singular instance of LoseLives object.
Even though it might not be the best approach, I found a workaround for my issue.
I am using a static variable that resides in the other script (CurrencyManager), calling it from the LoseLives script.
Instead of:
loseLives.LoseLife();
I do:
CurrencyManager.playerHealth -= 1;
Where CurrencyMananger is the other script and playerHealth is a static variable that is in the CurrencyManager script. This works because a static variable does not require the objects to be referenced in the inspector.
From now on, when I have a problem where I want to reference a gameObject within a script that is on a prefab, I will consider using static variables in my solution.
I am new and learning coding. How do I change a sprite from another game scene through a buttoned code.
using UnityEngine;
public class PlayerChoice : MonoBehaviour
{
public GameObject plo;
public Sprite boy;
public Sprite girl;
public void Boy()
{
plo.GetComponent<SpriteRenderer>().sprite = boy;
}
public void Girl()
{
plo.GetComponent<SpriteRenderer>().sprite = girl;
}
}
You actually can't do it the way you are trying.
This is a bit tricky for a beginner and you would have to read a bit to get behind it.
This is some starting point where you can dive in:
You would have to to make a gameobject that lives between scenes with
DontDestroyOnLoad(targetGameObject);
see: https://docs.unity3d.com/ScriptReference/Object.DontDestroyOnLoad.html
On this gameobject you could have a script which holds all variables that are needed in the next scene.
When scenes are switched access that object and get your values;
I was getting Missing Reference Exception within my Audio Source when I change my game scene and again move back to the same scene.
Before I change the main menu scene, it is working fine but after changing and moving back to the main menu scene, it is started showing this exception.
Here is the code I have written for AudioManager:
public class AudioManager : MonoBehaviour
{
static AudioManager instance;
//
[SerializeField] AudioClip buttonClickClip;
[SerializeField] AudioSource myAudioSource;
private void Awake()
{
instance = this;
}
public static AudioManager Instance
{
get
{
return instance;
}
}
public void PlayButtonClickSound()
{
if (GameManager.Instance.IsEnableSounds)
myAudioSource.PlayOneShot(buttonClickClip);
}
}
Here is the code that I wrote for DontDestroyOnLoad purpose:
public class DontDetroyOnLoad : MonoBehaviour
{
private static bool created = false;
void Awake()
{
if (!created)
{
DontDestroyOnLoad(this.gameObject);
created = true;
}
else
Destroy(this.gameObject);
}
}
Now please give me some suggestion to solve this problem.
You are referencing the AudioManager that you are destroying in the script that is trying to play the sound.
Think of it this way. You have
AudioManager A - DontDestroyOnLoad
AudioManager B - That gets destroyed cause A exists
In your scripts you are referencing AudioManager A when you first start up. Then when you leave the Scene and return you are now referencing AudioManager B, which got destroyed because A exists. All you need to do is always reference AudioManager A, not B.
Appreciate the answer from #jfish. That clearly describes the situation.
Here let me elaborate mine, on the example of using DontDestroyOnLoad
[SerializeField] AudioSource audioSource;
void Awake()
{
GameObject[] objs = GameObject.FindGameObjectsWithTag("music");
if (objs.Length > 1)
{
Destroy(this.gameObject);
//Here, get the previous audioSource from previous game object
audioSource = objs[0].GetComponent<AudioSource>();
}
DontDestroyOnLoad(this.gameObject);
}
On first load, it gets from Editor.
Subsequently, it gets from previous game object.