I'm trying to program a maze in Unity where you have to pick up a key in order to open door somewhere else. I put colliders on both the key and the door and have set the script to do different things when the player collides with them. here's the script. The problem is that the key allows the player to pass through it, but it doesn't mark it as the player has collected the key nor does it destroy the key either. Both the door and key have "is Trigger" checked.
public class DetectCollide : MonoBehaviour
{
public static string hasKey = "n";
void Start()
{
}
void Update()
{
}
private void onTriggerEnter(Collider other)
{
if(other.name == "Main Camera")
{
if(gameObject.name == "Key")
{
hasKey = "y";
Destroy(gameObject);
}
if((gameObject.name == "ArchDoor") && (hasKey == "y"))
{
Destroy(gameObject);
}
}
}
}
Both the key and the player must contain a Collider component: https://docs.unity3d.com/ScriptReference/Collider.OnTriggerEnter.html
Both GameObjects must contain a Collider component. One must have Collider.isTrigger enabled, and contain a Rigidbody. If both GameObjects have Collider.isTrigger enabled, no collision happens. The same applies when both GameObjects do not have a Rigidbody component.
Are all the prerequisites in place?
Please also change the code and see if it enters at all in the function
private void onTriggerEnter(Collider other)
{
Debug.Log("OtherName: " + other.name + "gameObject: " + gameObject.name);
if(other.name == "Main Camera")
{
if(gameObject.name == "Key")
{
hasKey = "y";
Destroy(gameObject);
}
if((gameObject.name == "ArchDoor") && (hasKey == "y"))
{
Destroy(gameObject);
}
}
}
It appears to not even enter the function seeing as nothing happened
after the change
The problem lies withing your function name, Unitys custom functions (eg. Update, Start and OnTriggerEnter) are all case sensitive and will not be called if they don't have the exact same name.
Example:
private void OnTriggerEnter(Collider other) {}
Instead of onTriggerEnter(), because you need to make sure that the O on OnTriggerEnter() is higher case like it should be. Or your function won't be called and therefore not trigger your collision like you wanted.
Related
here is my code for the trigger
`
private AudioSource finishSound;
private bool levelCompleted = false; //play the audio once
void Start()
{
finishSound = GetComponent<AudioSource>();
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.name == "Player1" && !levelCompleted)
{
CompleteLevel();
levelCompleted = true;
Invoke("CompleteLevel", 1.5f); //delay finish
}
}
private void CompleteLevel()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
}
}`
I know this code is only for one Player. How do I do it requiring 2 players at the door to finish the level?
An approach for this would be to cache the players and check against the total count. To see if it's a player, I'd advice to use tags instead of using the name of the object for a lot of reasons. I assume, the tag "Player" is used to identify the player.
List<GameObject> players = new List<GameObject>();
private void OnTriggerEnter2D(Collider2D collision)
{
// Add a new player if they enter the trigger
if (collision.gameObject.CompareTag("Player") && !players.Contains(collision.gameObject))
{
players.Add(collision.gameObject);
}
// Both players are in the trigger
if (players.Count == 2 && !levelCompleted)
{
CompleteLevel();
levelCompleted = true;
Invoke("CompleteLevel", 1.5f); //delay finish
}
}
private void OnTriggerExit2D(Collider2D collision)
{
// Remove a player if they leave the trigger again
if (collision.gameObject.CompareTag("Player") && players.Contains(collision.gameObject))
{
players.Remove(collision.gameObject);
}
}
If you have a varying count of players, you probably don't want to hard code the amount of players in a trigger. You should have a total count somewhere to check against. A game manager of sorts, that tracks the expected amount of players.
One way would be to create flags for each player, not one. When player one enters the trigger, you set playerOneCompleted = true. Then, you check if second player also completed. If that's the case finish the game if not, do nothing.
What you would need to add is the method OnTriggerExit2D to set flags of respective players to false when they exit the area.
Since the code you presented is inside the player, I would advise moving it to the door's trigger, so you can reference both players more easily.
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.
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.
I am using unity 5 c# and I have a gameobject with 2 trigger colliders one of them is in a different location.
I need to be able to use OnTriggerStay2D and OnTriggerEnter2D for them but I need to find what trigger is being entered. Right now if I enter the 1st(polygon) trigger the OnTriggerEnter activates for the 2nd(box).
How can I Tell the two colliders apart???
public void OnTriggerEnter2D(Collider2D other) //2nd collider trigger
{
if (other.tag == "Player") {
Found = true; //if the player is in shooting range
Idle = false;
}
}
public void OnTriggerStay2D(Collider2D other) //1st collider trigger
{
if (Found != true) {
if (other.tag == "Player") {
Shield = true;
Idle = false;
}
}
}
public void OnTriggerExit2D(Collider2D other) //2nd collider trigger
{
if (other.tag == "Player") {
Found = false;
Shield = false;
Shooting = false;
Idle = true;
}
}
I have tried making the 1st trigger public void OnTriggerStay2D(PolygonCollider2D other) but it says "This message parameter has to be of type: Collider2D
The message will be ignored."
What I am trying to do is have a polygon trigger in front of the gameobject and a different box trigger closer to the gameobject so when you go near the gameobject you enter the 1st trigger and it puts its shield up but when you get close to it (within shooting range of it) it will put its shield down and start shooting you.
Well collider2d detects all types of 2d colliders. It doesn't matter if it's polygon or just a box. As the documentation suggestions it doesn't need to be public or private. It only takes a collider2d as it's argument however.
For debugging purposes why not use print?
Print("you've entered the trigger function");
Also I wouldn't use 2 different trigger colliders on the same GameObject. Why not just make 2 separate gameobjects so you can have more thorough detection. Each GameObject with its own trigger collider can have different tags.
If you have to use 2 trigger colliders on one object. Which isn't the best idea. You could use shapeCount to determine which one it's hitting. Although like I said I would warrant against doing 2 trigger colliders on the same object when whatever you're trying to do can be easier on two separate objects.
However links aren't usually prohibited I think. I would watch and study these videos. They're very useful for explaining the engine and they really aren't even that long.
https://unity3d.com/learn/tutorials/modules/beginner/2d
They even have a video explaining 2d colliders.
This is my fix. In one of my games I have a boulder, I have a trigger which will delete a block below it so it falls, I then have another trigger which tells the boulder to start moving left or right I then also have another trigger which will delete the boulder once the boulder comes in contact.
So what you can do is create 2 new game objects, create a new CS file and name them appropriately, then with those two new classes allow them to take in the gameobject you are referring to in your question.
Then when they are triggered you can use code from their class.
So your first class would become something like this
public void OnTriggerEnter2D(Collider2D other)
{
if (other.tag == "Player") {
Enemy.Found = true; //if the player is in shooting range
Enemy.Idle = false;
}
}
public void OnTriggerExit2D(Collider2D other)
{
if (other.tag == "Player") {
Enemy.Exit2DTrigger();
}
}
Then the other class would be something like this
public void OnTriggerStay2D(Collider2D other)
{
if (Enemy.Found != true) {
if (other.tag == "Player") {
Enemy.Shield = true;
IEnemy.dle = false;
}
}
}
public void OnTriggerExit2D(Collider2D other)
{
if (other.tag == "Player") {
Enemy.Exit2DTrigger();
}
}
Then in your Enemy class you would have
public void Exit2DTrigger()
{
Found = false;
Shield = false;
Shooting = false;
Idle = true;
}
P.S. also don't you need to use other.gameObject.tag == "Player" ?
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