Call OnTriggerEnter Twice - c#

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.

Related

Stop onDestroy on Scene Load.Unity 2d

So I have a script that adds money when enemy dies (onDestroy). But when the scene changes that also destroys the enemies and adds money. How I can make it only add money when enemy is destroyed by health=0 and not on scene change?
Code:
void onDestroy()
{
AddCoins(Coins);
}
I can see two main ways to do this :
If it's possible, call the AddCoins Method in the code that calls Destroy() when health =0 instead of in OnDestroy(). Something like :
if(health == 0){
AddCoins(coins);
Destroy(gameObject);
}
and no OnDestroy method.
Otherwise, you could set a variable to true just before loading a new scene, and in OnDestroy() check if this variable is false, and only then adding coins.
If it's compatible with your game, I would recommend for the first solution, much cleaner in my opinion.
You can use scene.isLoaded to only check the times when the scene is loaded and is not changing.
void OnDestroy()
{
if(gameObject.scene.isLoaded)
{
AddCoins(Coins);
}
}
I would make it so that the AddCoin() method gets called not on destroy but instead have a check either in the update loop or ideally in your enemy damage function so it gets called only when health changes.
void Update()
{
if(health == 0)
{
AddCoins(Coins)
Destroy(gameobject)
}
}
if you want to use your existing code structure and still have it be called onDestory you could add another check
void onDestroy()
{
if(health == 0)
{
AddCoins(Coins);
}
}

can someone explain to me why my code dosen't work

i was making code for a project (i'm new in c#) and i'm so confused as to why my code dosent work
the code should make it so when a collider with the tag "floor" collides with my player it should change a variable,
no errors are coming up but the variable dose not change , here's the code where the problem is
void OnCollisionEnter2D()
{
if (GetComponent<Collider2D>().tag == "FLOOR")
{
jump = false;
Debug.Log("ITS A FLOOR");
}
}
Try this:
void OnCollisionEnter2D(Collision2D col)
{
if (col.gameObject.tag == "FLOOR")
{
jump = false;
Debug.Log("ITS A FLOOR");
}
}
My experience is mostly in 3D environments so I'm really not sure if I'm missing something, but your attempt to get a component doesn't make sense to me for two of reasons:
You didn't mention for which game object you would like the collider
The tag is found on the game object and not on the collider
Considering you want the variable jump to change. Debug with a breakpoint at the line
jump=false
Make sure the program reaches that line and make sure the scope of the variable is what you expect.
Going off the difference between "floor" in your description and then in your code, the string you are searching for may have different casing: "floor" != "FLOOR".
Try the following, if you need a case invariant string comparison:
if (string.Equals(GetComponent<Collider2D>().tag,"FLOOR", StringComparison.InvariantCultureIgnoreCase))
There is no void OnCollisionEnter2D() in Unity, only void OnCollisionEnter2D(Collision2D collision) where Collision2D collision is the collision that your object (player) entered.
void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "FLOOR")
{
jump = false;
Debug.Log("ITS A FLOOR");
}
}
Aight, answers above already told the answer to the problem
void OnCollisionEnter2D()
{
if (GetComponent<Collider2D>().tag == "FLOOR")
{
jump = false;
Debug.Log("ITS A FLOOR");
}
}
Is this code attached to the player? Or to the floor? I assume it's attached to the player, in this case GetComponent() will try to get the Collider2D from the player's entity or whatever, but not from the collision
And you didn't declare the Collision2D parameter. Intellisense usually knows Unity's methods, might help you a lot
So, the right way to access tag from the collision would be
void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "FLOOR")
{
//your code
}
}
Hope you'll get around

How can I call IEnumerator when a boolean is true?

I'm using the IEnumerator function and have had some issues with my if statement working:
IEnumerator Spawning()
{
Debug.Log("Spawning being called...");
if (GameObject.Find("FPSController").GetComponent<BoxCollide>().hitTrigger == true)
{
Debug.Log("CUSTOMER SHOULD SPAWN!");
while (countStop == false) {
yield return new WaitForSeconds(2);
Debug.Log("Fisher Spawned!");
counter++;
spawnNewCharacter.SpawnCharacter();
if (counter >= 3){
countStop = true;
}
}
}
}
After some debugging, it turns out that my if statement actually works. This issue is actually the IEnumerator function being called as soon as I run my game. I need a way to call this IEnumerator function only when my hitTrigger == true, but I can't seem to get anything to work.
I've tried this on top of the IEnumerator function:
void Update()
{
if (GameObject.Find("FPSController").GetComponent<BoxCollide>().hitTrigger == true)
{
Debug.Log("Spawning now...");
StartCoroutine(Spawning());
}
}
But still can't even get any of the Debug.Log's to come through. Would appreciate some help on this!
Side Information
Find() and GetComponent()
You don't want to use GameObject.Find(...) in the Update method, as it's an expensive call. The Update method is called each frame, so you'd call GameObject.Find(...) 60 times in 1 second at 60fps.
So when you use GetComponent() or Find() you want to save a reference to these objects like shown in the snippets below.
Better locations to use methods like GetComponent() or GameObject.Find() are the Awake() and Start() methods.
Awake
Awake is used to initialize any variables or game state before the
game starts. Awake is called only once during the lifetime of the
script instance. Awake is called after all objects are initialized so
you can safely speak to other objects or query them using eg.
GameObject.FindWithTag. [...]
Explanation is taken from the linked documentation.
Start
Start is called on the frame when a script is enabled just before any
of the Update methods is called the first time. Like the Awake
function, Start is called exactly once in the lifetime of the script.
However, Awake is called when the script object is initialised,
regardless of whether or not the script is enabled. Start may not be
called on the same frame as Awake if the script is not enabled at
initialisation time.
Explanation is also taken from the linked documentation
Possible Solution
Add the first Component (FPSControllerCollission) onto the object that holds your FPSController.
It makes use of unities OnTriggerEnter & OnTriggerExit methods.
This script is gonna set the IsTriggered bool to true, when a trigger entered the space of the box collider.
Note: A collider acts as a trigger, when the "Is Trigger" checkbox on
the component is checked.
You can do similar with OnCollisionEnter/Exit to recognize, when a Collider enters the space of the box collider.
Note that the following is only an example and you'll have to tweak /
integrate it into your code
[RequireComponent(typeof(BoxCollider))]
public class FPSControllerCollission : MonoBehaviour {
public bool IsTriggered;
private void OnTriggerEnter(Collider other) {
this.IsTriggered = true;
}
private void OnTriggerExit(Collider other) {
//Maybe check the tag/type of the other object here
this.IsTriggered = false;
}
}
The following SpawnController class could be integrated in the class you allready have.
public class SpawnController : MonoBehaviour {
private FPSControllerCollission _fpsControllerCollission;
private void Awake() {
this._fpsControllerCollission = FindObjectOfType<FPSControllerCollission>();
}
private void Update() {
if (this._fpsControllerCollission.IsTriggered) {
StartCoroutine(nameof(Spawning));
}
}
IEnumerator Spawning() {
Debug.Log("Spawning being called...");
if (this._fpsControllerCollission == true) {
Debug.Log("CUSTOMER SHOULD SPAWN!");
bool countStop = false;
int counter;
while (countStop == false) {
yield return new WaitForSeconds(2);
Debug.Log("Fisher Spawned!");
counter++;
spawnNewCharacter.SpawnCharacter();
if (counter >= 3) {
countStop = true;
}
}
}
}
}

Multiple triggers in Unity

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!

Play audio once when two identical objects collide [duplicate]

I have an object that moves towards another object and physically collides with it, I want that collision/colliding event to happen only once. I tried using a bool but it didn't work as intended. It seems that I'm doing something wrong.
bool doDamage = true;
void OnCollisionEnter2D(Collision2D other)
{
if (other.gameObject.tag == "Target" && doDamage)
{
doDamage = false;
// damage code
}
}
void OnCollisionExit2D(Collision2D other)
{
if (other.gameObject.tag == "Target")
{
doDamage = true;
}
}
Edit:
I want the "damage code" to run only once, even if the 2 objects are still in contact. This script is only assigned to 1 object, not both.
I don't know the easiest way to explain why your code is not working but I will try.
You have two GameObjects:
GameObject A with doDamage variable.
GameObject B with doDamage variable.
When GameObject A collides with GameObject B:
A.The OnCollisionEnter2D function is called on GameObject A.
if(other.gameObject.tag == "Target" && doDamage) executes because doDamage is true.
B.The doDamage variable from GameObject A is then set to false.
This does not affect the doDamage variable from GameObject B.
Then
C.The OnCollisionEnter2D function is called on GameObject B.
if(other.gameObject.tag == "Target" && doDamage) executes because doDamage is true.
D.The doDamage variable from GameObject B is then set to false.
Both your damage code will run because doDamage is always true in each OnCollisionEnter2D call. What you are currently doing is only affecting doDamage variable in each individual script.
What you are currently doing:
Setting doDamage in the local/this script to false while also checking if local/this doDamage is set or not.
What you need to do:
Set doDamage in the other script to false but read the local/this doDamage to check if it is set or not.
This is what it should look like:
public class DamageStatus : MonoBehaviour
{
bool detectedBefore = false;
void OnCollisionEnter2D(Collision2D other)
{
if (other.gameObject.CompareTag("Target"))
{
//Exit if we have already done some damage
if (detectedBefore)
{
return;
}
//Set the other detectedBefore variable to true
DamageStatus dmStat = other.gameObject.GetComponent<DamageStatus>();
if (dmStat)
{
dmStat.detectedBefore = true;
}
// Put damage/or code to run once below
}
}
void OnCollisionExit2D(Collision2D other)
{
if (other.gameObject.tag == "Target")
{
//Reset on exit?
detectedBefore = false;
}
}
}
If you want to run a snippet of code once, run the code directly in the callback event that you want to trigger your code.
void OnCollisionEnter2D(Collision2D other)
{
DoSomeDamageTo(other.gameObject);
}
This should only trigger once upon collision.
If you want this to only ever happen once (e.g. if the object holding the script were to hit something again), you'll need a flag to say that the damage has already been done:
bool hasHitSomething = false;
void OnCollisionEnter2D(Collision2D other)
{
if (!hasHitSomething)
{
DoSomeDamageTo(other.gameObject);
hasHitSomething = true;
}
}
I suspect given the code you've shared, that either the objects are colliding multiple times in engine (very possible if they're solid and physics-controlled), leading to OnCollisionEnter being called multiple times. OnCollisionEnter should only be called once per collision. If you are observing it being called multiple times, either you are actually observing multiple collisions, or you've found an obscure Unity bug. The former is much, much more likely.

Categories