OnTriggerEnter called multiple times - c#

This counter would randomly increment. It's driving me crazy.
When the player picks up all the game objects on the level (10), the level should restarts again.
That works, however, randomly with picking up objects it can either add +1 to the score, which is expected, or +2.
using UnityEngine;
using System.Collections;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class Pickup : MonoBehaviour {
//Required Variables
public GameObject pointsObject;
public Text scoreText;
private int score;
private int scoreCount;
void Start()
{
score = 0;
SetScoreText();
scoreCount = GameObject.FindGameObjectsWithTag("Pickups").Length;
}
void Update()
{
if(score >= scoreCount)
{
Scene scene = SceneManager.GetActiveScene();
SceneManager.LoadScene(scene.name);
}
SetScoreText();
Debug.Log("Found " + scoreCount + " Pickup Objects!");
}
void OnTriggerEnter(Collider col)
{
if(col.gameObject.CompareTag("Pickups"))
{
Destroy(col.gameObject);
score++;
}
}
void SetScoreText()
{
scoreText.text = "Score: " + score.ToString();
}
}
Can anyone see why I'm having this issue, as I can't see the reason why at all. Thanks in advance.

Not to revive a dead thread, but I ran into this issue recently. Bullets would count twice when triggered. Found out it was because both object had colliders with OnTrigger enabled. I placed the enemyHitbox collider on a separate layer (to not collide with anything else) and set it to OnTrigger false, while the playerBullet was set to OnTrigger true. This resolved the issue for me.
I'm assuming that both objects somehow caused a trigger event even though it was set for one hitting another with a set tag.
Hope this helps someone with a similar issue.
Using Unity2020 at this point btw.

This would have been a case of attaching the Pickup script to multiple GameObjects but I don't think so because the Pickup script would then have multiple instances and the score wont be incremented as mentioned in your question.
Since that is eliminated, it is very likely that you have more than 1 colliders (whether trigger or not) attached the GameObjects. This causes the OnTriggerEnter function to be called multiple times.
You have 2 solutions:
1.Find the duplicated colliders and remove them from the GameObjects. Check this on the Pickups GameObjects and other GameObjects they collider with.
2.Modify your code and make it understand there is a duplicated collider.
You can do this by making a list to store each of those 10 Pickups each time the OnTriggerEnter function is called. Check if collided GameObject exist in the List. If it exist in the List, don't increment. If it does not exist in the List, increment then add it to the List. This is very simple and easy logic to implement. Not tested but should work.
The code below is what it should look like if you go with solution #2.
public class Pickup : MonoBehaviour
{
//Required Variables
public GameObject pointsObject;
public Text scoreText;
private int score;
private int scoreCount;
List<GameObject> pickups = new List<GameObject>();
void Start()
{
score = 0;
SetScoreText();
scoreCount = GameObject.FindGameObjectsWithTag("Pickups").Length;
}
void Update()
{
if (score >= scoreCount)
{
Scene scene = SceneManager.GetActiveScene();
SceneManager.LoadScene(scene.name);
}
SetScoreText();
Debug.Log("Found " + scoreCount + " Pickup Objects!");
}
void OnTriggerEnter(Collider col)
{
if (col.gameObject.CompareTag("Pickups"))
{
//Increment if the gameObject is [not] already in the List
if (!pickups.Contains(col.gameObject))
{
Destroy(col.gameObject);
score++;
//Now Add it to the List
pickups.Add(col.gameObject);
}
}
}
//Call this after GameOver to clear List
void resetPickupsList()
{
pickups.Clear();
}
void SetScoreText()
{
scoreText.text = "Score: " + score.ToString();
}
}

Related

How to do if something happened make number - 1

I am trying to do when i destroy all boxes something happen.
My code is;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class destroy : MonoBehaviour
{
private string BALL_TAG = "ball";
public AudioClip coin;
public AudioSource src;
public float numBox = 120f;
public bool isDestroyed;
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag(BALL_TAG))
{
src.clip = coin;
src.Play();
Destroy(gameObject);
isDestroyed = true;
}
}
private void Update()
{
boxes();
}
public void boxes()
{
if(isDestroyed == true)
numBox -= 1f;
if(numBox == 119)
SceneManager.LoadScene("mainManu");
}
private IEnumerator Two()
{
yield return new WaitForSeconds(1f);
Destroy(gameObject);
}
}
But it doesn't work.
It is suppose to do when I broke 1 box it sends me to menu.
I think its problem in "numBox -= 1f;" because I don't know hot to make this.
I don't understand your code completely. So, I need to make some assumptions.
I think the Script is attached to the box and every box has this Script. I also think, that your player Shoots Ball. Those Balls have a collider with an ball tag.
There are multiple problems with your code.
The first one is, that your count variable, numBox, is saved in your destroy Script, which is placed on each box.
this means, that every Box is counting for itself.
You have to centralize this. There are multiple ways for doing this.
One way is to declare this variable as static(https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/static)
This is not best practice, but works.
A Better way is to have a Script on Your Player, which holds this number and every Box searches for this Script and change this number if it is destroyed.
The second big Problem is, that your are doing some really weird thing in your Update and the collision handling
First of all, you are setting isDestroyed to true. Then in your boxes method, which is called in every Frame, you are decrementing your numBox variable by one, if this is Destroyed is true.
So if your Box gets hit, you are decrementing every frame.
After that you are checking every frame if your numBox is 119
If so, you change the Scene.
This is the reason, why you are getting to your MainMenu after only one boy
This behaviour is very weird, because it is totally unnecessary. You can reduce your variable directly in in your OnCollisionEnter2D Method.
There are some little things, which can be improved.
When you are trying to play a Sound, you don't have to specify the AudioClip in code. You can assign this directly in Unity on the AudioSource Component via drag and drop. This makes your code simpler.
You are not calling the Two Coroutine. You've specified this Coroutine but don't call it.
//Script on Player
public class PlayerBoxDestroyManager:MonoBehaviour
{
public int StartBoxes = 120;
private int Boxes;
private void Start()
{
Boxes = StartBoxes;
}
public void DestroyBox()
{
//Reduce our Boxes count
//This is equal to Boxes -= 1
// Boxes = Boxes -1
Boxes--;
// If we have less or zero Boxes left, we End call our EndGame methode
if(Boxes <= 0)
{
EndGame();
}
}
private void EndGame()
{
// We change the Scene to the mainMenu
SceneManager.LoadScene("mainManu");
}
}
```
//Script on all Boxes
public class Box : MonoBehaviour
{
public string Balltag = "ball";
//Audio Source the Audio Clip has to be assigned in the Unity editor
public AudioSource Coin;
private void OnCollisionEnter2D(Collision2D collision)
{
//Check it colliding Object has the right Tag
if(collision.transform.tag == Balltag)
{
//Get the reference to the Player Script
PlayerBoxDestroyManager PBDM = FindObjectOfType<PlayerBoxDestroyManager>();
//We can now access the Destroy Box Methode
PBDM.DestroyBox();
//Play the sound
Coin.Play();
//If we destroy our Object now, the Sound would also be deletet.
//We want to hear the sound, so we have to wait, till the sound is finished.
StartCoroutine(WaitTillAudioIsFinished());
}
}
IEnumerator WaitTillAudioIsFinished()
{
//we wait till the sound is finished
while (Coin.isPlaying)
{
yield return null;
}
//if finished, we destroy the Gameobject
Destroy(gameObject);
}
}
I hope I helped you. If you have questions, feel free to ask.
And sorry for my English:)

2D Runner Highscore Issue

Hey Guys I'm having some Problems with my Highscore in a Unity game. Its a 2d runner in which I save the Player Distance in a Variable and want to check when the Player is dead if distance is higher than Highscore. But I have a Error and can't figure out what the problem is. Can somebody help me, I'm pretty new to c#.
Heres my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class UIController : MonoBehaviour
{
Player player;
Text distanceText;
GameObject results;
Text finalDistanceText;
private void Awake()
{
player = GameObject.Find("Player").GetComponent<Player>();
distanceText = GameObject.Find("DistanceText").GetComponent<Text>();
results = GameObject.Find("Results");
finalDistanceText = GameObject.Find("FinalDistanceText").GetComponent<Text>();
HighscoreNumber = GameObject.Find("HighscoreNumber").GetComponent<Text>();
results.SetActive(false);
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
int distance = Mathf.FloorToInt(player.distance);
distanceText.text = distance + " m";
if (player.isDead)
{
results.SetActive(true);
finalDistanceText.text = distance + " m";
if (distance > Highscore)
{
int Highscore = distance
HighscoreNumber.text = Highscore + " m";
}
}
}
public void Quit()
{
SceneManager.LoadScene("Menu");
}
public void Retry()
{
SceneManager.LoadScene("SampleScene");
}
}
In your second if statment inside update method you use HighScore in comparison then declare variable with the same name on the next line.
Couple issues that I can see. You'll be getting an error for HighscoreNumber being assigned to before being declared.
class UIController : MonoBehaviour {
Text highScore
//...
void Awake() {
highScore = GameObject.Find...
}
}
The next issue is that you're declaring a Highscore locally after you try to use it in the if statement:
// Highscore doesn't exist yet so you can't compare distance against it.
if (distance > Highscore)
{
// The int here means you're making a new variable that only lives inside the braces and setting it to distance.
int Highscore = distance
HighscoreNumber.text = Highscore + " m";
}
If this UIComponent stays alive between scene loads, you could just move the declaration to the class level.
public class UIController : MonoBehavior {
int highScore = 0 //I get set to 0 when the UIController component is created.
//...
void Update() {
//...
// Since highScore is declared at the scope of the class, the if statement and assignment inside both have access.
if(distance > highScore) {
highScore = distance;
}
}
}
The other issue is that it looks like the UIComponent may be getting disposed and re-created when you restart the scene so will likely be reset to 0 between restarts. You'll want to look at something like the Singleton method described here:
https://www.sitepoint.com/saving-data-between-scenes-in-unity/
This is just what came up when I Googled, there's probably a bunch of examples but that should help you know what to search.
However, this will only survive as long as the game is running. If you want to retain the high score between game launches, you'll want to look at something like this:
https://blog.unity.com/technology/persistent-data-how-to-save-your-game-states-and-settings

Score increasing by 2 when it should be increased by 1

I have a bullet and when it hits a target it should increment the score by 1. but the score is increasing by 2. the bullet is a capsule with a collider and rigibody and the target is a cylinder with a collider and rigibody
the code on the bullet
public class Bullet : MonoBehaviour {
float lifespan = 2;
void Start()
{
// destroy the bullet
Destroy(gameObject, lifespan);
}
void OnTriggerEnter(Collider other) //collider event
{
if (other.gameObject.CompareTag("Score"))
{
Score.score = Score.score + 1;
}
}
}
the score code
public class Score : MonoBehaviour {
public static int score; // The player's score.
Text text; // Reference to the Text component.
void Start()
{
// Set up the reference.
text = GetComponent<Text>();
// Reset the score.
score = 0;
}
void Update()
{
// Set the displayed text to the score value.
text.text = "Score: " + score;
}
}
I've solved this exact problem before but I searched for it to mark this as a duplicate but couldn't find it. It's probably deleted by the OP.
There are 2 possible reasons why your score could update multiple times.
1.Your script (Bullet) is attached to your GameObject multiple times. This is very likely the problem. It is very likely that it is attached to random empty GameObject.
Fix:
A.Check that gameObject.AddComponent<Bullet>(); is not anywhere in any script in your project. The AddComponent will add new Bullet to your GameObject.
B.Search for duplicated script on GameObjects through the Editor.
Select your Bullet script, go to Assets --> Find References in Scene. It will show you every GameObject that has this script attached to it. Remove it from all of them except for your bullet GameObject.
2.You have more than one collider on the GameObject. Maybe a collider as a child. You must find a way to handle that. If this is the case, you can ignore the child colliders by putting them in a separate tag and checking it.
You are already checking the tags which is fine. Just change the tag of the child colliders to something else that is not "Score" so that other.gameObject.CompareTag("Score") will not be true.

OnTriggerEnter and OnTriggerExit called twice despite checking it

I have read a lot of questions about this problem but i couldn't solve it yet. I have one ball with a Sphere Collider and an invisible wall to restart the ball when it passes through it (on the onTriggerExit method). The problem is that i've not been able to solve it, even with a boolean to avoid entering the method.
public class ballRestart : MonoBehaviour
{
shootController instance;
bool isColliding;
// Use this for initialization
void Start()
{
instance = Camera.main.GetComponent<shootController>();
isColliding = false;
}
public void OnTriggerEnter(Collider col)
{
Debug.Log("TeEnter: " + isColliding);
if (!isColliding)
{
isColliding = true;
Debug.Log("TRIGGERED: " + isColliding);
}
}
void OnTriggerExit(Collider hit)
{
Debug.Log("TeExit: " + isColliding);
if (isColliding)
{
instance.initializeBall();
isColliding = false;
Debug.Log("exit");
}
}
}
OUTPUT:
Here is the output of the Logs
As you see, it enters twice each time the ball enters the collider and twice each time the ball exits the same collider. I don't know what happens here.
I'm pretty sure you have 2 colliders on your object, each of them causes event triggering. Check your components and child objects.
P.s.: I know it is old question, but my answer can be helpful for others.

Collision with enemy to inflict damage

I'm having some problems with Unity. I'm trying to make it so that if an enemy collides with the player, the player loses a health point. My C# code is below.
before you look at the code, I wanted to say that the enemies are rigid bodies so that the object bullets can affect them. I made an extra capsule to be a part of the player body that can be a rigid body so that the code can detect the collision. Do you think that would work? I'm unsure if it's easier for a rigid body to detect another rigid-body collision or if it doesn't care.
public class playerhealth : MonoBehaviour {
private int curHealth;
private int playerLives;
public GUIText winText;
public GUIText healthText;
public GUIText livesText;
void Start() {
curHealth = 3;
playerLives = 3;
SetHealthText();
SetLivesText();
winText.text = "";
}
void FixedUpdate()
{
// where physics codes go
}
// HERE'S WHERE THE COLLISIONS STUFF IS
void OnCollisionEnter(Collider rigidbody) {
if (rigidbody.gameObject.tag == "Enemy") {
curHealth = curHealth - 1;
SetHealthText();
}
if (rigidbody.gameObject.tag == "reloader") {
playerLives = playerLives - 1;
SetLivesText();
}
}
// setting GUI TEXT and reloading level
void SetHealthText() {
healthText.text = "Health Points: " + curHealth.ToString();
if (curHealth <= 0) {
Application.LoadLevel("shootingworld");
playerLives = playerLives - 1;
}
if(curHealth >= 10) {
playerLives+= 1;
}
}
void SetLivesText() {
livesText.text = "Lives: " + playerLives.ToString();
if (playerLives <= 0) {
winText.text = "GAME OVER";
}
}
}
You're making a number of assumptions here, some of which are wrong. I'll try to point them out.
Adding a RigidBody to a gameobject is the right idea, but it's the Collider component that determines the shape and size of the object's collision. Consider adding a BoxCollider, SphereCollider or CapsuleCollider to both.
I assume you're having trouble getting the objects to actually collide, this may be the solution.
Also,
void OnCollisionEnter(Collider rigidbody){
The parameter you've named 'rigidbody' is not guaranteed to be a RigidBody component. According to documentation
The Collision class contains information about contact points, impact velocity etc.
The proper syntax for OnCollisionEnter has a Collision parameter, not a Collider.
To access the rigidbody on the Collider, you'd have to use getcomponent on the object found by the Collider and check if the RigidBody component exists. I'm not sure this is what you're after, but the misleading parameter name should be checked.
Anyway you've got the right idea regarding comparing a Collider's gameobject by tag. All you need to do is enforce the tag on the object, either in the editor or through code.
You are using this:
void OnCollisionEnter(Collider collision) {
}
Collider is used for Ontrigger...
Try this:
void OnCollisionEnter(Collision collision) {
}
Hope this help ! :)
You try to this may help full also
void OnCollisionEnter(Collision collision)
{
}
Documentation is :
http://docs.unity3d.com/Documentation/ScriptReference/Collider.OnCollisionEnter.html

Categories

Resources