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.
Related
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:)
I am making a multiplayer game using Unet. Currently, the players can move independently (i.e., if user one presses d, then character one will move right if user two presses d then character two will move right) - however, both players shoot 'through' the host.
My movement code is inside the PlayerUnit script. It has one !isLocalPlayer check at the top, in the Update() method. This check is performed before the script detects for user inputs (i.e., w, s, mouse1, etc.).
This all works fine when the correct key is pressed; the proper action is performed.
Eg:
if (Input.GetKeyDown(up))
Jump();
Shooting works in a slightly different way as it involves a different script.
When mouse1 is pressed, the Shoot() method in PlayerUnit is called. This, in turn, uses a Command inside the PlayerUnit script to call a ClientRpc in a separate WeaponMaster script, which calls the Fire() coroutine.
In PlayerUnit:
private void Shoot ()
{
//WeaponMaster.wM.Shoot();
CmdShoot();
}
[Command]
void CmdShoot ()
{
WeaponMaster.wM.RpcShoot();
}
In WeaponMaster:
[ClientRpc]
public void RpcShoot ()
{
if (ammo != 0)
{
if (isAutomatic == true)
{
keepFiring = true;
StartCoroutine(Fire());
}
if (isAutomatic == false && noQuickFire == true)
StartCoroutine(Fire());
}
}
WeaponMaster has one !isLocalPlayer check as well, once again in the Update() method.
When debugging, I know that playerTwo's (client) Shoot() method is being called, but playerOne's RpcShoot is being executed. Everything works fine when playerOne shoots - hence the problem of both players shooting 'through' the host.
All the scripts are on the same object with one NetworkIdentity component.
I'm convinced the problem is just a missing authority check - but I've tried adding !isLocalPlayer tests in the various methods to no avail.
As said my guess is that a Singleton pattern in WeaponMaster.wM is not a good approach here since you get the wrong reference and since everything goes through the server might simply always get the same reference no matter who called CmdShoot.
You should rather store each players own WeaponMaster reference and go through it like in PlayerUnit
// Already reference this via the inspector (drag&drop)
[SerializeField] private WeaponMaster weaponMaster;
// or as fallback get it on runtime
private void Awake()
{
if(!weaponMaster) weaponMaster = GetComponent<WeaponMaster>();
}
public void Shoot ()
{
if(!isLocalPlayer) return;
CmdShoot();
}
[Command]
private void CmdShoot ()
{
RpcShoot();
}
// Personally in general I would leave any RPC
// as close to the method calling it
// as possible for better maintainability
[ClientRpc]
private void RpcShoot ()
{
weaponMaster.Shoot();
}
and accordingly in WeaponMaster
public void Shoot()
{
if (ammo == 0) return;
if (isAutomatic)
{
keepFiring = true;
StartCoroutine(Fire());
}
else if (!isAutomatic && noQuickFire)
StartCoroutine(Fire());
}
I have an NPC that my player can talk to when the players collider is colliding with the NPC, I do that using this piece of code:
private void OnTriggerStay2D(Collider2D other)
{
if (other.gameObject.tag == "InteractiveArea")
{
if (Input.GetKeyDown(KeyCode.E))
{
Debug.Log("PRESSED NPC");
CreateAndShowDialog();
}
}
}
However, this gets called really randomly, sometimes the first time I press "E", sometimes the second or third time, etc.
My rigidbodies:
The colliders I use are standard BoxCollider2D, my players collider is a trigger, the NPCs is not.
Why are some key press not detected in the OnTriggerStay function?
OnTriggerStay2D is called randomly. This is why you should never check for Input inside of it.
Set to a flag to true and false in the OnTriggerEnter2D and OnTriggerExit2D functions then check for that flag and input in the Update function which is called every frame. Also, always use CompareTag instead of other.gameObject.tag to compare tags.
private void Update()
{
if (Input.GetKeyDown(KeyCode.E) && triggerStay)
{
//
}
}
bool triggerStay = false;
void OnTriggerEnter2D(Collider2D collision)
{
Debug.Log("Entered");
if (collision.gameObject.CompareTag("InteractiveArea"))
{
triggerStay = true;
}
}
void OnTriggerExit2D(Collider2D collision)
{
Debug.Log("Exited");
if (collision.gameObject.CompareTag("InteractiveArea"))
{
triggerStay = false;
}
}
My guess would be because the value returned by Input.GetKeyDown is only true for the single (Update()) frame that the key was pressed, whereas physics (including OnTriggerStay) are called during physics frames, i.e. FixedUpdate()
Doing a bit of research I pull up this question which suggests:
I've been struggling with this issue all afternoon!
My OnTriggerStay and Update/FixedUpdate methods were not in sync resulting is split-second undesired effects.
I finally found the solution when I read in the OnTriggerStay docs that this function can be a co-routine. I simply added a WaitForFixedUpdate in the correct location in my OnTriggerStay and it worked. They now both run in sync with each other. This even corrects the issue in Update.
Another question has this as a solution (Programmer's answer):
OnTriggerStay will not call on every frame. One way to get around this is to have OnTriggerEnter, and OnTriggerExit set a bool. Then execute your code in the FixedUpdate().
While the documentation no longer says what this post says,
OnTriggerStay gets called every FixedUpdate.
"Note: OnTriggerStay function is on the physics timer so it wont necessary run every frame. "
That confirms my guess and that the documentation was changed to no longer include this note, for some unexplained reason.
You can do this :
bool eIsOnclick;
float wait = 0.03f;
float nextFire = 0.0f;
void Update() {
if(Input.GetKeyDown(keyKode.E))
eIsOnclick = true;
else if(nextFire < Time.time)
nextFire = Time.time + wait;
ebas = false;
}
void OnTriggerStay2D(Collider2D collision) {
if(eIsOnclick == true) {
// your cods
}
// not work in alwaays aquestions
I'm working on my first Unity game and I'm having a problem with this script.
void FixedUpdate ()
{
Debug.Log ("dead is " + dead);
dead = Physics.OverlapSphere (frontCheck.position, radius, whatIsWall);
if (dead == true)
{
Debug.Log ("Player died!");
Invoke ("Reset", 1);
}
}
void Reset()
{
SceneManager.LoadScene ("Game");
}
I'm trying to make the bool dead true when the player runs into a wall, using a transform in front of the player. I was using Physics2D.OverLapPoint and it worked fine, but I had to change player's physics to 3D. I'm trying to use OverLapSphere now, but Im getting an error message "Cannot implicitly convert type UnityEngine.Collider[] to bool. What should I do to make this work? Im very much a beginner to Unity and coding in general, so it's probably a simple fix. Maybe I just need to try something else? thanks.
Better Approach
I believe a better approach to detect collisions is using OnColissionEnter.
https://docs.unity3d.com/ScriptReference/Collider.OnCollisionEnter.html
That way you can have a simple check such as:
void OnCollisionEnter(Collision col) {
if (col.gameObject.tag == "Wall"){
dead = true;
}
}
Here is a short tutorial on that: https://unity3d.com/learn/tutorials/topics/physics/detecting-collisions-oncollisionenter
Using OverlapSphere
If for some reason you prefer OverlapSphere, then you need to know that it doesn't return a bool as you are expecting. Instead, it returns all colliders that overlap with the sphere.
https://docs.unity3d.com/ScriptReference/Physics.OverlapSphere.html
I believe what you are looking for is:
void FixedUpdate ()
{
Debug.Log ("dead is " + dead);
Collider[] hitColliders = = Physics.OverlapSphere (frontCheck.position, radius, whatIsWall);
if (hitColliders.length != 0) {
Debug.Log ("Player died!");
Invoke ("Reset", 1);
}
}
What should I do to make this work?
I personally would use different approach then overlap. One of the easiest solutions is to use colliders and object tags.
To answer why is your code not working. It is mainly because variable "dead" is not bool and 'UnityEngine.Collider[]' cant be value "true".
This is example of Unity prefab 1st person controller which have assigned following script. After that all object which have any collider and tag set ro "red" will so to speak react to scrip.In this case it will write "I have collided with trigger"+something.
using UnityEngine;
public class collisionTest : MonoBehaviour {
void OnTriggerEnter(Collider trigg)
{
if (trigg.gameObject.tag == "Red")
{
Debug.Log("I have collided with trigger" + trigg.gameObject.name);
//do your stuff
}
}
}
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();
}
}