Overlapping Collider2D Triggers - unity3d

Ok lets get into it.
I've got 2 colliders whose purpose it is to determine the hit type of the ball. If it is GoodHit or NiceHit (Nice is like Perfect).
The GoodHit collider2d has a much larger area than NiceHit.
The NiceHit on the other hand overlaps to a portion of GoodHit in its center.
Using this code I can determine whether the ball enters the area of GoodHit and NiceHit
void OnTriggerEnter2D(Collider2D other) {
if (other.name == "HitNice") {
hitType = 2;
} else if(other.name == "HitGood") {
hitType = 1;
}
}
void OnTriggerStay2D(Collider2D other) {
if (other.name == "HitNice") {
hitType = 2;
} else if(other.name == "HitGood") {
hitType = 1;
}
}
void OnTriggerExit2D(Collider2D other) {
hitType = 0;
}
The problem is it doesn't triggers the NiceHit and the hitType is still valued 1 regardless if the ball is in NiceHit area.
I first created the Goodhit object, that in a sense gives it a priority than NiceHit. I could swap them by names and values that way I can achieve the thing that I wanted but that doesn't really solved it.
Any workaround or tweaks I can do? Thanks!

You have two overlapping triggers, one of which is in the middle of a larger trigger.
Here's something to remember: if an object is inside the inner trigger, it is also inside the larger trigger!
So in your code, you use OnTriggerStay to determine if the object is in one of those triggers. Let's take a look at that:
void OnTriggerStay2D(Collider2D other) {
if (other.name == "HitNice") {
hitType = 2;
} else if(other.name == "HitGood") {
hitType = 1;
}
}
This function is executed once per trigger that the object is inside of. So this function is executed twice if it is inside of the HitNice trigger. So let's say Unity calls this function for each trigger. What is probably happening is that it's calling the handlers in a bad order (pseudocode):
OnTriggerEnter(HitNice) //hitType is 2
OnTriggerEnter(HitGood) //hitType is now 1!
Unity is calling the collider handlers from inside out, causing the result from a HitGood trigger to always override the HitNice value. I don't know for sure if that's how it works, but looking at your code it's totally a possibility of why it's not working correctly.
Now, I can't say this for sure, but I think just using your OnTriggerEnter handler will suffice. It will only trigger the hitType detection when it enters a collider rather than when it stays. Since the object must enter the outer trigger before entering the inner trigger, this means that it should always evaluate in the correct order. In that case, your code is probably working as is, but I would say that your logic should be like this:
void OnTriggerEnter2D(Collider2D other) {
if(other.name == "HitNice") {
hitType = 2;
} else if(other.name == "HitGood" && hitType != 2) {
hitType = 1;
}
}
The only caveat of this approach is that you would need to reset the value of hitType manually. Not sure exactly how your game works, but this should give you an idea of how to approach this. Your HitNice should always override a HitGood. So when OnTriggerEnter is called for HitGood, check that you haven't yet hit HitNice yet. Or even better, record both hit types in separate variables, and then resolve which type of hit it was later on when doing scoring or whatever.

Related

SteamVR: Correct way to get the input device triggered by an action and then get it's corresponding Hand class?

I have an action that is mapped to both my left amd right hand triggers on my VR controllers. I would like to access these instances...
Player.instance.rightHand
Player.instance.leftHand
...depending on which trigger is used but I can't fathom the proper way to do it from the SteamVR API. So far the closest I have gotten is this...
public SteamVR_Action_Boolean CubeNavigation_Position;
private void Update()
{
if (CubeNavigation_Position[SteamVR_Input_Sources.Any].state) {
// this returns an enum which can be converted to string for LeftHand or RightHand
SteamVR_Input_Sources inputSource = CubeNavigation_Position[SteamVR_Input_Sources.Any].activeDevice;
}
}
...am I supposed to do multiple if statements for SteamVR_Input_Sources.LeftHand and SteamVR_Input_Sources.RightHand? That doesn't seem correct.
I just want to get the input device that triggered the action and then access it using Player.instance.
I was also looking for an answer to this. I've for now done what I think is what you mean with the if-statements. It works, but definitely not ideal. You want to directly refer to the hand which triggered the action, right?
With the 'inputHand' variable here I get the transform.position of the hand from which I will raycast and show a visible line. I could have put a separate instance of a raycastScript like this on each hand, of course, but I wanted to make a 'global' script, if that makes sense.
private SteamVR_Input_Sources inputSource = SteamVR_Input_Sources.Any; //which controller
public SteamVR_Action_Boolean raycastTrigger; // action-button
private Hand inputHand;
private void Update()
{
if (raycastTrigger.stateDown && !isRaycasting) // If holding down trigger
{
isRaycasting = true;
inputHand = inputChecker();
}
if (raycastTrigger.stateUp && isRaycasting)
{
isRaycasting = false;
}
}
private Hand inputChecker()
{
if (raycastTrigger.activeDevice == SteamVR_Input_Sources.RightHand)
{
inputHand = Player.instance.rightHand;
}
else if (raycastTrigger.activeDevice == SteamVR_Input_Sources.LeftHand)
{
inputHand = Player.instance.leftHand;
}
return inputHand;
}

unity physic.OverlapSphere as trigger

I wanna make sphere collision as trigger everytime player shoot by physics.overlapsphere. but it seems like it only create make collision which is not trigger. so how can i solve this?
Collider[] cs = Physics.OverlapSphere(transform.position, redi);
foreach (Collider c in cs)
{
Rigidbody r = c.gameObject.GetComponent<Rigidbody>();
if (r != null)
{
r.AddExplosionForce(power, transform.position, redi);
}
if (c.gameObject.tag == "enemy")
{
Destroy(c.gameObject);
}
if (c.gameObject.tag == "player")
{
c.gameObject.GetComponent<movee>().health -= 5;
}
}
Physics queries rely on Physics.queriesHitTriggers property which defines, if certain calls hit trigger colliders or not (see https://docs.unity3d.com/ScriptReference/QueryTriggerInteraction.html).
You can set QueryTriggerInteraction for your OverlapSphere call as a method parameter. If you do not do so, it uses the global setting, which might be set to ignore triggers in your case.
Proper method call:
Collider[] hitColliders = Physics.OverlapSphere(position, range, layerMaskSphere, QueryTriggerInteraction.Collide);

I have button that execute my method but ignoring the if statement

i have a problem. iam making a game that have button for buy item , and the button have no issue, it run normally, but the problem is the button immediately executes the method without seeing that the method contains an if statement, so what is wrong here Here's the code :
public void TakeMoney()
{
int minusCoin = PlayerInfo.coin -= 5;
PlayerPrefs.SetInt("MyCoinAmount", minusCoin);
}
public void DisableBuyButton()
{
disableBuyButtonRocket3 = 1;
PlayerPrefs.SetInt("DisableBuyButtonRocket3", disableBuyButtonRocket3);
}
public void Buy()
{
int getPlayerCoin = PlayerInfo.coin;
if (getPlayerCoin > 4)
{
if (MyRocket._rocket3 == 1)
{
GetComponent<AudioSource>().Play();
MyRocket._rocket3 = 1;
PlayerPrefs.SetInt("Rocket3Bought", MyRocket._rocket3);
}
}
}
so the button will perform all of the 3 functions above, but as can be seen that I make an if statement so the item can only be purchased if we already have enough money, but when I try I try to reset all values, so my money by default it is 0, but I can buy the item and my money becomes a minus.
Btw, PlayerInfo above is a script from another scene.
Sorry for my bad english.
Your problem is that you do everything in separated methods. It's basically not a problem - but, when you execute them concurrently, it is. Also, your methods are completely independent of each other - an if statement in one will not affect the others' execution. The solution is that you first remove the event listeners to TakeMoney and DisableBuyButton. Then, call them from Buy. If you think it through, it is more logical. Also, remove the temporary variables, as they slow down your code a bit and also make it less readable. Final code could be something like this:
public void TakeMoney()
{
PlayerInfo.coin -= 5;
PlayerPrefs.SetInt("MyCoinAmount", PlayerInfo.coin);
}
public void DisableBuyButton()
{
PlayerPrefs.SetInt("DisableBuyButtonRocket3", 1);
}
public void Buy()
{
if (PlayerInfo.coin > 4) {
TakeMoney();
DisableBuyButton();
if (MyRocket._rocket3 != 1 /* changed this, because it seemed that you want to check whether it is NOT one */)
{
GetComponent<AudioSource>().Play();
// I changed the if statement because this
MyRocket._rocket3 = 1;
PlayerPrefs.SetInt("Rocket3Bought", MyRocket._rocket3);
}
}
}
the playerinfo.coin value would be more than 4 because it will decrease coin at click so it will minus only 5 at a click you could check playerinfo.coin value

Colliding objects bouncing off when isTrigger is true instead of passing through

I have looped through some game objects to set the isTrigger property before a certain collision occurs but though the property is true collision with the object still occurs. Please see the relevant code below:
void OnCollisionEnter2D(Collision2D col) {
if (col.gameObject.tag == "object1") {
for (int i = 0; i < badGuys.Count; i++) {
badGuys[i].getBadGuyGameObject().GetComponent<Collider2D>().isTrigger = true;
}
}
else if (col.gameObject.tag == "object2") {
// collision with object1 always occurs before object2, though isTrigger is true the colliding object doesn't pass through the badGuys game objects, it bounces off on collision
}
else if (col.gameObject.tag == "object3") {
for (int i = 0; i < badGuys.Count; i++) {
badGuys[i].getBadGuyGameObject().GetComponent<Collider2D>().isTrigger = false;
}
}
else if (col.gameObject.tag == "badGuy") {
}
}
collision with object1 always occurs before object2, though isTrigger is true the colliding object doesn't pass through the badGuys game objects, it bounces off on collision. How can I solve this?
UPDATE 1
I just realized that the isTrigger value is true or false in the inspector depending on the time I stop the game. For example if I stop the game after Object1 collision the isTrigger is true and when I stop the game after collision with object3 it false. This inconsistency makes debugging very cumbersome. Is this a bug or something?
UPDATE 2
Based on recommendation from Joe Blow and Agustin0987 I used the enabled property of Collider2D instead of isTrigger. I also removed virtually all the code in OnCollisionEnter2D to get a simple test scenario. Please see code below:
void OnCollisionEnter2D(Collision2D col) {
if (col.gameObject.tag == "object1") {
if (badGuys[4].getBadGuyGameObject().GetComponent<Collider2D>().enabled = false) {
badGuys[4].getBadGuyGameObject().GetComponent<Collider2D>().enabled = true;
Debug.Log ("CHANGED TO TRUE");
Debug.Log ("bad guy collider enabled is " + badGuys[4].getBadGuyGameObject().GetComponent<Collider2D>().enabled);
}
else if (badGuys[4].getBadGuyGameObject().GetComponent<Collider2D>().enabled = true) {
badGuys[4].getBadGuyGameObject().GetComponent<Collider2D>().enabled = false;
Debug.Log ("CHANGED TO FALSE");
Debug.Log ("bad guy collider enabled is " + badGuys[4].getBadGuyGameObject().GetComponent<Collider2D>().enabled);
}
}
else if (col.gameObject.tag == "badGuy") {
Debug.Log("collided with bad guy"); // DOESN'T OCCUR
}
}
Instead of looping through this time, I decided to test for one. The else if is where enabled is true always satisfied though the Log prints false and the if where enabled is false is never satisfied. In the inspector, the Box Collider2D for bad guys is always unchecked. Collision with the badGuy never occurs. Based on my simple scenario code I thought it should be working.
Never, ever ever
I mean absolutely never
use "else if".
Simply, never, ever - ever - type "else if" for the rest of your life, until you die.
In the first instance, delete all your code and replace it with this:
void OnCollisionEnter2D(Collision2D col)
{
Debug.Log("We will deal with this ......... " +col.gameObject.name);
if (col.gameObject.tag == "object1")
{
Debug.Log("\t handling 'object' type issue");
HandleObjectIssue(col)
return; //NOTE HERE
}
if (col.gameObject.tag == "object1")
{
Debug.Log("\t handling 'bad guy' type issue");
HandleBadGuyIssue(col)
return; //NOTE HERE
}
}
private void HandleObjectIssue(Collision2D col)
{
Debug.Log("'\t\t object', I'm dealing with this .. " +col.gameObject.name);
}
private void HandleBadGuyIssue(Collision2D col)
{
Debug.Log("\t\t 'badguy', I'm dealing with this .. " +col.gameObject.name);
}
Run that extensively and "unit test" it. If you like, convert back (USING THAT CODE) to a "loop" method to look at all items. In any event, test that extensively.
--
Secondly, add this routine ..
private void ToggleColliderOnGameObject( GameObject gg )
{
Debug.Log("I'm supposed to toggle the collider on: " +gg.name);
Collider2D { or whatever } col = gg.GetComponent<Collider2D>();
bool currentState = GetComponent<Collider2D>().enabled;
Debug.Log("\t it is currently: " +currentState);
bool newState = ! currentState;
col.enabled = newState;
Debug.Log("\t\t but now it is: " +newState);
}
... and start unit testing with it. So you will have code like:
ToggleColliderOnGameObject( badGuys[4].getBadGuyGameObject() );
For example, try some things like this:
void Start()
{
InvokeRepeating("teste", 1f, 1f);
}
private void Teste()
{
ToggleColliderOnGameObject( badGuys[4].getBadGuyGameObject() );
}
After experimenting with that for some time and unit testing, you will be able to move on.
Note that apart from other problems, it is likely that (for example) your routine "getBadGuyGameObject" is returning the wrong thing - or some similar problem.
EDIT:
Remove badGuy = badGuyPrefab; from your public void createBadGuy(GameObject badGuyPrefab, BadGuyType badGuyType, Vector3 badGuyPosition) function. This was a mistake in Programmer's code from the link in your comment.
It seems like you are trying to enable/detect the collision between badGuys and object2 only if object1 collides with whatever object has this script attached (seems to be some kind of BadGuyManager). So I would recommend that instead of setting isTrigger to true or false, you should try to enable or disable the whole `Collider2D.
P.S: I agree with Joe Blow in that you should explain what are you trying to achieve.

Strange If/Else Behavior

Context
I am making a mobile game in which the player is required to touch objects in a specified order. The correct order is determined in a List called clickOrder. To determine the current object the player is supposed to click, currClickIndex is used.
Problem
When touching a correct object, the debug text will display "Correct" for a split second, and will then immediately change to "Wrong." What I am unsure about is why both the if and else blocks are executed when only touching a single object.
Code
void Update()
{
if (Input.touchCount == 1)
{
if (this.enabled)
{
Vector2 worldPoint = Camera.main.ScreenToWorldPoint(Input.mousePosition);
RaycastHit2D hit = Physics2D.Raycast(worldPoint, Vector2.zero);
if (hit != null && hit.collider != null)
{
// check if the touched object is the correct one
if (hit.collider.gameObject == clickOrder[MyData.currClickIndex])
{
debug.text = "Correct";
MyData.currClickIndex++;
}
else
{
debug.text = "Wrong";
}
}
}
}
}
As soon as the correct object is being touched, you do this:
MyData.currClickIndex++;
which moves you forward in the ordered sequence, and from then on, the previously correct object is not correct anymore. But you're still touching it.
If you want to avoid this, you need to move forward in the sequence after you've touched the correct object.
if (there are touches and the correct object is being touched)
{
set a flag;
}
else if (a flag has been set)
{
MyData.currClickIndex++;
reset the flag;
}