I am trying to get the scene to switch when a method in the script TrippleBall returns 0. I know it returns 0 at the appropriate time because I have tested it. Here is my code to switch scenes:
void OnTriggerEnter(Collider col)
{
if (col.gameObject.tag == "ball")
{
col.gameObject.GetComponent<Ball>().setIsOffScreen(true);
/*error*/ if (GameObject.Find("TrippleBalls").GetComponent<TripleBall>().getBallCount() == 0) {
Debug.Log("Loading next screen...");
SceneManager.LoadScene("GameOverScene");
}
}
}
Here is an image to show where TrippleBalls is
The script TrippleBall is in the component TrippleBalls
Here is an image to show where the code above is located.
The code above is in a class called SceneTrigger which has been put in LBackBoard and RBackBoard
When I test the code, and the getBallCount returns 0 (to satisfy the condition above) I get the following error:
Object reference not set to an instance of an object
This error line sends me to where i marked error in the code above.
If anyone can help me figure this out, that would be awesome. Thank you!
You GameObject is named TrippleBall in the Scene but you are looking for TrippleBalls in the Scene. Simply change GameObject.Find("TrippleBalls") to GameObject.Find("TrippleBall");.
Also Don't use GameObject.Find in the OnTrigger function. It will slow down your game.Cach TripleBall in a local variable then you can re-use it.
TripleBall myTripleBall = null;
void Strt(){
//Cache TripleBall
myTripleBall = GameObject.Find("TrippleBalls").GetComponent<TripleBall>()
}
Now you can re-use it any time.
void OnTriggerEnter(Collider col)
{
if (col.gameObject.tag == "ball")
{
Debug.Log("COUNT IS: "+myTripleBall.getBallCount());
col.gameObject.GetComponent<Ball>().setIsOffScreen(true);
if (myTripleBall.getBallCount() == 0) {
Debug.Log("Loading next screen...");
SceneManager.LoadScene("GameOverScene");
}
}
}
Make sure to Add the GameOverScene scene in your Build Setting.
Advice:
Advice for you is that when Looking for another GameObject inside another GameObject, Use '/' like you would in a folder path. For example, TrippleBall is under Specialties GameObject. So use GameObject.Find("Specialties/TrippleBall"); instead of GameObject.Find("TrippleBall");
Related
This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 9 months ago.
This is my code that is receiving the error on the playerScore increment line. The code is in a prefab that is generated many times, and if it hits the player, it is destroyed and the player's score is incremented. Currently the code will destroy on collision with anything that does not have the name "Player" due to the error once the if statement is executed.
private void OnCollisionEnter2D(Collision2D collisionInfo)
{
Debug.Log($#"Collision name = {collisionInfo.collider.name}");
if (collisionInfo.collider.name == "Player")
{
GetComponent<GameMechanics>().playerScore++;
}
GameObject e = Instantiate(explosion) as GameObject;
e.transform.position = transform.position;
Destroy(gameObject);
}
This is my code for the referenced variable, I just cannot tell what I am doing wrong here, I want the object destroyed in all cases, but want the score incremented when the player hits it.Is the object being destroyed preventing the changes to be made to the player score?
public class GameMechanics : MonoBehaviour
{
public int playerScore;
// Start is called before the first frame update
void Start()
{
playerScore = 0;
}
// Update is called once per frame
void Update()
{
Debug.Log($#"Player Score = {playerScore} ");
}
}
Thanks in advance!
This is because you are using GetComponent in a class that does not have it and should find GameMechanics in it, but the result is null.
GetComponent<GameMechanics>().playerScore++; // GameMechanics dosen't found..
One way is to give GameMechanic to the other object's is through the inspector:
public GameMechanics Mechanics;
private void OnCollisionEnter2D(Collision2D collisionInfo)
{
if (collisionInfo.collider.name == "Player") Mechanics.playerScore++;
}
However There are other ways to reference GameMechanic and Manager classes in other objects, the best of which is Singleton. here is a simple way to solve your problem easy and that is to use FindObjectOfType. Change the code below and it will probably be fixed.
if (collisionInfo.collider.name == "Player")
{
FindObjectOfType<GameMechanics>().playerScore++; // replace it with this way
}
NullReferenceExceptions occur when the code attempts to access a variable which has not been initialized properly. In your case, this could be because of several reasons.
Ensure the gameobject is set to active in the scene
Ensure the script object is enabled on the gameobject
If these do not help, you can try making your variable static, and setting it to an ititial value of 0.
Static variables are loaded before everything else, and accessible by most other classes, so this may be able to fix your issue.
void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.layer == layer)
{
StopAllCoroutines();
Destroy(gameObject, 1f);
}
}
//Coroutine is called from another script
public IEnumerator MoveRb(Vector3 destination)
{
yield return new WaitUntil(() => rb.velocity == Vector3.zero);
destination.y = rb.position.y;
rb.velocity = transform.right;
//this is where i get an error
yield return new WaitUntil(() => Vector3.Distance(rb.position, destination) < 0.1f);
rb.velocity = Vector3.zero;
}
Basically, getting "MissingReferenceException" when trying to destroy an object while running coroutine. Delay of 1 second, nor replacing "WaitUntil" with while loop and "yield return null" doesn't fix this issue. The only place where object gets destroyed is inside of "OnCollisionEnter" inside of the same gameObject script. What am I missing here?
Full exception message:
MissingReferenceException: The object of type 'Rigidbody' has been destroyed but you are still trying to access it.
Your script should either check if it is null or you should not destroy the object.
As you posted on the other answer you actually run this routine from another script
public class ConveyourBelt : MonoBehaviour
{
void OnCollisionEnter(Collision other)
{
if (other.gameObject.TryGetComponent(out MovingPart movingPart))
{
// runs this Coroutine on THIS component
StartCoroutine(movingPart.MoveRb(transform.position + transform.right));
}
}
}
The issue here is that this ConveyourBelt component is running the Coroutine, not the MovingPart component attached to the other object
=> The call to
StopAllCoroutines();
in the MovingPart component has no effect at all since it is never running that routine!
So when you destroy the object after 1 second the routine could still be running on the ConveyourBelt component.
As a solution you should rather make the routine running on the other component like
public class ConveyourBelt : MonoBehaviour
{
void OnCollisionEnter(Collision other)
{
if (other.gameObject.TryGetComponent<MovingPart>(out var movingPart))
{
// rather runs the Coroutine on the movingPart component
movingPart.StartCoroutine(movingPart.MoveRb(transform.position + transform.right));
}
}
}
The code should work in theory. I think the problem is you are trying to access the coroutine again during this 1 second destroy delay. This results in the coroutine running while the gameobject is destroyed.
If you want this 1s delay then either make sure that the coroutine can't get called again after you have collided with something or the easier way is to make the coroutine error proof. Means you check if the rigidbody is null before you use it.
This is my script and I tried to make "" in case it's not one of the cases:
private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.name == "Platform")
Debug.Log("Touching Platform");
else Debug.Log("");
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.name == "OnTop Detector")
{
Debug.Log("On Top of Platform");
GameObject findGo = GameObject.Find("ThirdPersonController");
GameObject findGo1 = GameObject.Find("Elevator");
findGo.transform.parent = findGo1.transform;
}
else Debug.Log("");
}
But this "" not working. It's not deleting the text from the console while the game is running. And also when i stop the game and running it again it keep showing the last text of the Debug.Log.
I want to clear it if none of the If's happen in my script.
I saw this answer:
Answer
But i'm not sure if this is what I need and how to use it. Make a new script ?
And also what about errors. If I clear the Log in the console it will delete also erorrs if there will be some ?
In this screenshot it's in state after I touched the platform and then moved away from it but the text still exist in the console log:
The Debug.ClearDeveloperConsole() function is used when you clear logs from an application that was built while Debug Build is enabled in your project. There is no official API for clearing the Editor log.
Most Editor functionality can be replicated with Reflection just like hiding Gizmos and toggling the Stats Panel. I was going to write one but found this one.
This should clear every log on the Console tab.
using System.Reflection;
public void ClearLog()
{
var assembly = Assembly.GetAssembly(typeof(UnityEditor.ActiveEditorTracker));
var type = assembly.GetType("UnityEditorInternal.LogEntries");
var method = type.GetMethod("Clear");
method.Invoke(new object(), null);
}
Now, you can call ClearLog(); in your else statements.
EDIT:
This has changed recently in about ~Unity 2017. Since it is done with reflection, I consider it to change again anytime if any class, variable or function used in this code is renamed by Unity. Below is the new way to do this:
public void ClearLog()
{
var assembly = Assembly.GetAssembly(typeof(UnityEditor.Editor));
var type = assembly.GetType("UnityEditor.LogEntries");
var method = type.GetMethod("Clear");
method.Invoke(new object(), null);
}
Note that the Debug Log is longer than just one line (you can see it by clicking on said line).
You can try to use Debug.ClearDeveloperConsole() and see what you get.
I've been trying for a little bit here to set a muteSound boolean in my SoundManager and then to switch to a new scene and keep the value previously stored in muteSound but I'm unsuccessful.
I tried the DontDestroyOnLoad(this); in hopes that it'd bring it to the new scene but for some reason it isn't.
Would any of you know what my problem could be? Am I using the correct function?
Thanks,
Some would say use static. That would work but avoid doing that as you will run into other problems. What you need is the PlayerPrefs. Save the value on exit. Read the value when game starts. You can do that in your SoundManager script.
bool muteSound = false;
//Load the value when game starts (default is false)
void Start()
{
muteSound = intToBool(PlayerPrefs.GetInt("muteSound", 0));
}
int boolToInt(bool val)
{
if (val)
return 1;
else
return 0;
}
bool intToBool(int val)
{
if (val != 0)
return true;
else
return false;
}
//Save on Exit
void OnDisable()
{
PlayerPrefs.SetInt("muteSound", boolToInt(muteSound));
}
You can actually pass a game object from scene to scene and all the values for classes assigned to that game object are maintained between scenes.
You basically just create an empty game object that stores your manager scripts and then pass the game object from scene to scene when you load them. This should preserve the values for the scripts attached to the empty game object.
EDIT: Fixed some spelling/grammatical errors.
Instead of typing
DontDestroyOnLoad(this); type
void Awake() {
DontDestroyOnLoad(gameObject);
}
If this doesn't work I suggest you check out the PlayerPrefs.
When the level is finished usePlayerPrefs.SetFloat("pref name", variable); or .SetInt(); or .SetBool();
And when the next level loads usevalue = PlayerPrefs.GetFloat("pref name"); or .GetInt(); or .GetBool();
This player prefs can also be used as saves because they are stored inside the computers registry.
If you use this for loading things such as money and you are loading it for the first time, do this:
if(PlayerPrefs.HasKey("money")) {
money = PlayerPrefs.GetFloat("money");
} else {
PlayerPrefs.SetFloat("money", 0);
money = 0;
}
If you don't do this the values can get messy a lot. I had big problems when I didn't use PlayerPrefs.HasKey();
Here was my solution:
public class SoundManager : MonoBehaviour {
public static SoundManager instance = null;
protected virtual void Awake()
{
if (instance == null)
instance = this;
else if (instance != this)
Destroy(gameObject);
DontDestroyOnLoad(gameObject);
}
}
While I thought my problem was because of my changing the class to a singleton the actual problem was that there was another class that needed to be a singleton.
Since the rest of the code wouldn't be mine to share, here is a tutorial which helped me understand what I was doing much better if anyone else ever runs into this type of problem.
I am trying to get a ball to pass trough an empty object but also send back a message to debug window. But I don't have a clue how to do this or where to start, so any help on this would be greatly appreciated. I am sorry I have no code to show as an example, however I have been able to get a collision detection or to allow the object pass through the empty object one at a time but never both. I have used OnTriggerEnter and OnCollionEnter.
Put a Collider (e.g. a SphereCollider) on your empty object, and set its Is Trigger to true. Now you can use OnTriggerEnter in your script (attached to the empty object) as you expected.
public class MyBehaviour : MonoBehaviour
{
private void OnTriggerEnter(Collider other)
{
var collider = other.gameObject;
// Do something...
Debug.Log(collider);
}
}
You want to make your GameObject's collider a Trigger on the Editor. Go to the editor and add a collider to your GameObject, then make it a Trigger.
C# Code
void OnTriggerEnter(Collider other)
{
Debug.Log("I hit something: " + other.gameObject);
}
Javascript
function OnTriggerEnter (other : Collider)
{
Debug.Log("Hey I hit you: " + other.gameObject);
}