Reference image
I have a certain object in my game and I'm trying to see whether the object triggers multiple triggers. I tried with the code bellow but for some reason it doesn't work.
void OnTriggerEnter2D(Collider2D col)
{
if (col.tag == "speed")
{
//do something
}
else if (col.tag == "speed" && col.tag == "point")
{
//do something
}
}
How can I recognize if the object only hit "Col1" or "Col1" and "Col2"
OnTriggerEnter is only called when your object is colliding with one specific trigger. Thus, the tag of the collider (col) can't be speed and point at the same time.
You have to track if the object is colliding with your triggers using a boolean variable for example :
private bool collidingWithSpeed;
private bool collidingWithPoint;
void OnTriggerEnter2D(Collider2D col)
{
if (col.CompareTag("speed"))
{
collidingWithSpeed = true ;
//do something
}
else if (col.CompareTag("point"))
{
collidingWithPoint = true ;
//do something
}
if( collidingWithSpeed && collidingWithPoint )
{
// Do something when your object collided with both triggers
}
}
// Don't forget to set the variables to false when your object exits the triggers!
void OnTriggerExit2D(Collider2D col)
{
if (col.CompareTag("speed"))
{
collidingWithSpeed = false;
}
else if (col.CompareTag("point"))
{
collidingWithPoint = false;
}
}
While #Hellium's answer would work perfectly, I personally prefer using a list to store all my colliding objects (or at least some of them). Like so
private readonly List<string> collidingTags = new List<string>();
void OnTriggerEnter2D(Collider2D collider)
{
//Add some kind of filter or safety check if needed
collidingTags.Add(collider.tag);
}
void OnTriggerExit2D(Collider2D collider)
{
//Add some kind of safety check if needed
collidingTags.Remove(collider.tag);
}
In theory, this is far less efficient in terms of performances (compared to storing a bool) but stills, it adds a nice layer of flexibility.
In practice, the difference in performances is incredibly small so you decide!
Related
I have two exactly the same GameObjects colliding.
Both have script atached, in which I have OnCollisionEnter().
Context: two Armies of ships(single game object with script: ShipBehaviour) is fighting and one that have more ships inside lives, the other is destroys;
The problem is that OnCollisionEnter() being called twice (once on each GameObject),
therefore final calculating number(ShipsInside) is double than it should be.
OnCollisionEnter(Collision collision) method:
if(collision.gameObject.tag == "Ship")
{
var shipBehaviour = collision.gameObject.GetComponent<ShipBehaviour>();
if ( shipBehaviour != null )
{
if(shipBehaviour.ShipsInside > ShipsInside)
{
shipBehaviour.ShipsInside -= ShipsInside;
Destroy(gameObject);
}
else if(shipBehaviour.ShipsInside < ShipsInside)
{
ShipsInside -= shipBehaviour.ShipsInside;
Destroy(shipBehaviour.gameObject);
}
else if (shipBehaviour.ShipsInside == ShipsInside)
{
Destroy(gameObject);
Destroy(shipBehaviour.gameObject);
}
}
return;
}
So I want to execute OnCollisionEnter() logic in one GameObject and ignore it in other.
I have a solution: simly add in calculation division by 2. Than final number would be accurate. But obviosly it's not a right way to go about it.
Is there more afficient way to do so?
You don't actually need a flag for this.
Your battle outcomes are exactly symmetrical, meaning either one party is the winner.
So the simplest fix in my eyes would be to only let the winning (or alternately loosing) party handle the outcome of the battle and do e.g.
// Note that your tag check is actually redundant
// this would already be enough and you don't need to remember assigning tags
if(collision.gameObject.TryGetComponent<ShipBehaviour>(out var shipBehaviour))
{
if(shipBehaviour.ShipsInside < ShipsInside)
{
ShipsInside -= shipBehaviour.ShipsInside;
Destroy(shipBehaviour.gameObject);
}
else if (shipBehaviour.ShipsInside == ShipsInside)
{
Destroy(shipBehaviour.gameObject);
}
}
This should be enough then since the other collision party already handles the other cases on their side anyway.
You could of course as well turn it the other way round and handle your own loosing case. The same way you might prefer rather only destroying yourself in the == case.
If you don't care about the one redundant subtraction (since the object is destroyed anyway) you could even as well shorten it down to
if(collision.gameObject.TryGetComponent<ShipBehaviour>(out var shipBehaviour))
{
if(shipBehaviour.ShipsInside <= ShipsInside)
{
ShipsInside -= shipBehaviour.ShipsInside;
Destroy(shipBehaviour.gameObject);
}
}
You can simply add a flag to solve this.
private bool _collisionHandled = false;
private void OnCollisionEnter(Collision collision)
{
if (_collisionHandled || collision.gameObject.tag != "Ship")
{
return;
}
var shipBehaviour = collision.gameObject.GetComponent<ShipBehaviour>();
if (shipBehaviour != null)
{
if (shipBehaviour.ShipsInside > ShipsInside)
{
shipBehaviour._collisionHandled = true;
shipBehaviour.ShipsInside -= ShipsInside;
Destroy(gameObject);
}
else if (shipBehaviour.ShipsInside < ShipsInside)
{
_collisionHandled = true;
ShipsInside -= shipBehaviour.ShipsInside;
Destroy(shipBehaviour.gameObject);
}
else if (shipBehaviour.ShipsInside == ShipsInside)
{
_collisionHandled = true;
shipBehaviour._collisionHandled = true;
Destroy(gameObject);
Destroy(shipBehaviour.gameObject);
}
}
}
Some part of the code works (hp1 -= damage1;), but the second part doesn't work.
Where did I make a mistake?
Here are parts of two scripts:
Player1.cs
private Bullet1 b1;
void Start()
{
b1 = FindObjectOfType<Bullet1>();
}
void OnCollisionEnter2D(Collision2D col)
{
if (col.gameObject.tag == "Bullet1")
{
hp1 -= damage1; // it works, my player loses hp
Destroy1(); // doesn't work
}
}
void Destroy1()
{
b1.hit1 = true; // hit for bullet1.cs
}
Bullet1.cs
public bool hit1;
void Update()
{
if (hit1)
{
hit1 = false;
Destroy(gameObject);
}
}
If I switch bool hit = true in real time in Unity, destroying works. It means that Bullet1.cs can't recieve hit = true;
If I swap lines hp1 -= damage1; and Destroy1();, my player can't get damage. So, Destroy1(); stops my code and then can't activate other lines. Also if I change Destroy1(); to b1.hit1 = true; nothing new happens.
Just destroy the bullet through the collision instead of using the boolean. It's creating unnecessary resource usage. Of the given code, this is all you need to do to destroy the bullet using the player script. The parts of the bullet script shown are unnecessary. If you want additional logic handled inside the bullet when destroyed then use a OnDestroy function to handle it.
void OnCollisionEnter2D(Collision2D col)
{
if (col.gameObject.tag == "Bullet1")
{
hp1 -= damage1;
Destroy(col.gameobject);
}
}
I need a way to check all scripts if boolean is true and then a way in this script to see in the if statement of the current light the character is standing next to has the boolean true to activate and only then should the Instantiate function be triggered.
private bool Once = true;
public Transform Spawnpoint1;
public Transform Spawnpoint2;
public GameObject Prefab1;
public GameObject Prefab2;
//Something like this, but I don't know where to go after that
GameObject[] Lights = GameObject.FindGameObjectsWithTag("lightSwitch");
//foreach(GameObject Light in Lights)
void OnTriggerEnter2D(Collider2D collision)
{
if (Once == true)
{
Debug.Log("It's true");
if (LightOnOff.isON == true) // It needs to check this constantly
{
Debug.Log(" It's Lit");
Instantiate(Prefab1, Spawnpoint1.position, Spawnpoint1.rotation);
Instantiate(Prefab2, Spawnpoint2.position, Spawnpoint2.rotation);
Once = false;
}
}
}
here is the Light script as well
public static bool isON;
public void lightOn()
{
this.GetComponent<Light>().enabled = true;
isON = true;
}
First of all you shouldn't use static
public static bool isON;
for an individual value! static makes the value a "non instanced" value, meaning it belongs to the entire class instead of instances => To say it in simple words this variable is shared between all your components (see this post for more information). Instead use
public bool isON;
Than access the insteance variable of the component like
Update: From the comments I now know that actually the components are not on the collider object but rather on a child of the object this script is attached to
void OnTriggerEnter2D(Collider2D collision)
{
// Update. TODO check if the correct Object collided
//if(!collision.gameObject == <your player>)
if (!Once) return;
// Update this now searches in its own children instead of children of collision
var lightOnOff = GetComponentInChildren<LightOnOff>(true);
if(!lightOnOff)
{
Debug.Log("No LightOnOff found on collision" + collision.name, this);
return;
}
Debug.Log("It's true");
if (!LightOnOff.isON) return;
Debug.Log(" It's Lit");
Instantiate(Prefab1, Spawnpoint1.position, Spawnpoint1.rotation);
Instantiate(Prefab2, Spawnpoint2.position, Spawnpoint2.rotation);
Once = false;
}
But instead of using your LightOnOff component you could also simply acces the Light component and do something like
Update: From the comments I now know that actually the components are not on the collider object but rather on a child of the object this script is attached to
void OnTriggerEnter2D(Collider2D collision)
{
if (!Once) return;
// directly access the Light
// Update this now searches in its own children instead of children of collision
var light = GetComponentInChildren<Light>(true);
if(!light)
{
Debug.Log("No Light found on collision" + collision.name, this);
return;
}
Debug.Log("It's true");
// Directly check if the Light component is enabled
if (!light.enabled) return;
Debug.Log(" It's Lit");
Instantiate(Prefab1, Spawnpoint1.position, Spawnpoint1.rotation);
Instantiate(Prefab2, Spawnpoint2.position, Spawnpoint2.rotation);
Once = false;
}
You don't need to keep track of all the lights just for that.
Something you do need to change is that you should make isON not static. This is because an actual light might be on or not, not that the concept of lights are on or not.
public bool isON;
Check the Collider2D for the associated object you're colliding with, and look for a light there. The following code assumes any light will be on the same GameObject as a Trigger or one of its children.
void OnCollisionEnter2D(Collider2D col) {
// only activate once
if (once) {
// Get light if exists
GameObject collidedObject = col.gameObject;
Light light = collidedObject.GetComponentInChildren<Light>();
if (light != null) {
// We have a light, check if it's on. We only care about the collided light
if (light.isON) {
Debug.Log("It's Lit fam");
Instantiate(Prefab1, Spawnpoint1.position, Spawnpoint1.rotation);
Instantiate(Prefab2, Spawnpoint2.position, Spawnpoint2.rotation);
// Note that if we run into another lit light nothing will happen,
// even if its the first time running into that particular light
Once = false;
}
}
}
}
Also, you can just use if(something) instead of if(something == true)
I'm coding a 2D platformer in Unity and need to call the OnTriggerEnter function twice, I've created one Public Collider2D, named headPos,
the first time I use the OnTriggerEnter2D is here
void OnTriggerEnter2D(Collider2D headPos)
{
//Run My Code
{
And the second time I use it is here
void OnTriggerEnter2D(Collider2D other)
{
//Run More Code
{
I get the following error
Type 'Player' already defines a member called 'OnTriggerEnter2D' with the same parameter types
How do I check for two seperate OnTriggerEnter2D's?
When you say different triggers, it looks like you are trying to detect different gameobjects with trigger. If this this is true, then you can use if statement to check which gameobject was triggered.
You can tag each body part with collider/trigger and detect with code like below:
void OnTriggerEnter(Collider col)
{
if(col.CompareTag("head")){
//Run My Code
Debug.Log("Head Triggered!");
}
else if (col.CompareTag("hand"))
{
//Run My Code
Debug.Log("Hand Triggered!");
}
else if (col.CompareTag("leg"))
{
//Run My Code
Debug.Log("Leg Triggered!");
}
}
You can also compare them by name
void OnTriggerEnter(Collider col)
{
if (col.name == "head")
{
//Run My Code
Debug.Log("Head Triggered!");
}
else if (col.name == "hand")
{
//Run My Code
Debug.Log("Hand Triggered!");
}
else if (col.name == "leg")
{
//Run My Code
Debug.Log("Leg Triggered!");
}
}
What is the use case? You should never use the same function twice, especially when it comes to optimization.
I guess what you want to achieve is detecting two separate collisions happening in the same time. In this case your function OnTriggerEnter2D(Collider2D headPos) will be called twice with different colliding object passed in every time.
I have craete 2 ball sprite & one hole sprite, when this 2 ball sprite enters into the hole it need to load new scene.
im trying to find multi collision with AND operator but it's not working,if i try with one condition it workes fine, i don't know why.
void OnTriggerEnter2D(Collider2D col)
{
if ((col.gameObject.tag == "ball2") && (col.gameObject.tag == "ball")) {
Application.LoadLevel("Main");
}
}
Logistically, the code you've presented can never equate to true, because you're comparing the same string against two different values. If it returns false from the first test, it returns false. If it returns true from the first test, then it will always return false from the second, because the first evaluated it as being "ball2" - and thus it will return false.
If you're looking for it to return true if either (not both) side is true, use the or operator (condition1 || condition2)
void OnTriggerEnter2D(Collider2D col)
{
if ((col.gameObject.tag == "ball2")
|| (col.gameObject.tag == "ball"))
{
Application.LoadLevel("Main");
}
}
ps, yes, I like the Visual Studio code formatting, particularly for cases where conditional blocks are multiple lines
EDIT: Going by your comments, you're waiting to get two objects into the collision. In such a case, you cannot perform this AND test, because you're testing the same object since you only received the col parameter once. Because you get each collision as a discreet, separate event, you should instead set a flag per occurrence, and then advance when both flags have been set.
private bool ball = false;
private bool ball2 = false;
void OnTriggerEnter2D(Collider2D col)
{
// Set each flag individually to allow for separate events triggering each
if(col.gameObject.tag == "ball") ball = true;
if(col.gameObject.tag == "ball2") ball2 = true;
// Perform operation once both flags have been set
if(ball && ball2) Application.LoadLevel("Main");
}
Add two different collider with two different Gameobject and add two different collider script. Access one variable from other script if that's true then go on...
void OnTriggerEnter2D(Collider2D col) // Collider1 Script
{
if ((col.gameObject.tag == "ball2"))
{
firstcollider = true;
}
}
void OnTriggerEnter2D(Collider2D col) // Collider2 Script
{
if (Collider1.firstcollider == true ) && (col.gameObject.tag == "ball"))
{
Application.LoadLevel("Main");
}
}
Refer Google to access a variable from one script to another script if you dont know. I don't know this is the best way for code but just for crack....