Is there a way to detect if the collider on the player (with a rigidbody) object is not colliding with any other collider in a 2D environment?
Not per se, but there are two ways you can get the information. It's easiest to keep an int counter, and increment it in OnCollisionEnter and decrement in OnCollisionExit. When the counter is 0, there are no collisions.
Another way, which will tell you which colliders are overlapping but not necessarily if they are touching, is to use a physics function. Note which type of collider you have--sphere, capsule, box. Knowing the size/shape/position of the collider, you can call Physics.OverlapBox, Physics.OverlapCapsule, or Physics.OverlapSphere. Give the function the same shape as your collider, and it will return the colliders that overlap that space. (For 2D colliders, you can use the Physics2D.Overlap* functions.)
/edit - actually piojo's idea with counters is better, just use int instead of bool.
One way would be to add an int to your script called collisions and set it to 0. Then if at any point the collider fires OnCollisionEnter just increment it, and in OnCollisionExit decrement it.
Something like this should work for 3D:
public int collisions = 0;
void OnCollisionEnter(Collision collision)
{
collisions++;
}
void OnCollisionExit(Collision collision)
{
collisions--;
}
https://docs.unity3d.com/ScriptReference/Collider.OnCollisionEnter.html
https://docs.unity3d.com/ScriptReference/Collider.OnCollisionExit.html
I don't understand what's with all the number keeping. I just did this for when my character jumps or falls off the edge and it works great.
Both my player and terrain have colliders. I tagged my terrain with a "Ground" tag.
Then check if I am currently in contact with the collider with OnCollisionStay()
void OnCollisionStay(Collision collision)
{
if (collision.collider.tag == "Ground")
{
if(animator.GetBool("falling") == true)
{
//If I am colliding with the Ground, and if falling is set to true
//set falling to false.
//In my Animator, I have a transition back to walking when falling = false.
animator.SetBool("falling", false);
falling = false;
}
}
}
void OnCollisionExit(Collision collision)
{
if (collision.collider.tag == "Ground")
{
//If I exit the Ground Collider, set falling to True.
//In my animator, I have a transition that changes the animation
//to Falling if falling is true.
animator.SetBool("falling", true);
falling = true;
}
}
void OnCollisionEnter(Collision collision)
{
if (collision.collider.tag == "Obstacle")
{
//If I collide with a wall, I want to fall backwards.
//In my animator controller, if ranIntoWall = true it plays a fall-
//backwards animation and has an exit time.
animator.SetBool("ranIntoWall", true);
falling = true;
//All player movement is inside of an if(!falling){} block
}
}
Related
So, i have a Player and a End object. The End has no Mesh renderer, but it has a Box Collider with a Slippary. So its invisible in the game, i tagged it with "End" and write this Script (in the Player).
private void OnCollisionEnter(Collision collision)
{
if (collision.collider.tag == "End")
{
Time.timeScale = 0f;
}
}
But when i hit the invisible object called End, the Player goes ahead. I want to fix this problem because my Player moves on when i finish the game and the coordinates influence the reached points. So it has really to stop (the Playermovement is anytime activated)
I am working on a simple game where the goal is to help a Player catch specific objects with a tag "Present".
After taking care of the models, animations I am now working on the collisions and counting UI.
For the collisions, on my Player Controller (I am using the ThirdPersonUserController from the player Unity Standard Assets - which simplified the whole animation process), I added the following functions:
void OnCollisionEnter(Collision other)
{
if (other.gameObject.tag == "Present")
{
Destroy(gameObject);
count = count - 1;
SetCountText();
}
}
void SetCountText()
{
countText.text = "Count: " + count.ToString();
if (count == 0)
{
winText.text = "Thanks to you the Christmas will be memorable!";
}
}
However, like this, when the Player hits an object with the tag "Present", even though the counting is working properly, the player disappears.
I have tried to change the OnCollisionEnter to an OnTriggerEnter, like this:
void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag("Present"))
{
Destroy(gameObject);
count = count - 1;
SetCountText();
}
}
However when the Player hits the objects with the tag "Present", they don't disappear.
My Player has a Capsule Collider and a Rigidbody.
The objects with the tag "Present" have a Box Collider and a Rigidbody.
Any guidance on how to make my Player stay in scene while removing the other objetcs and reducing the count is appreciated.
Few things. You are destroying the incorrect game object:
void OnCollisionEnter(Collision other)
{
if (other.gameObject.tag == "Present")
{
Destroy(gameObject); // this is destroying the current gameobject i.e. the player
count = count - 1;
SetCountText();
}
}
Update to:
void OnCollisionEnter(Collision other)
{
if (other.gameObject.CompareTag("Present"))
{
Destroy(other.gameObject); // this is destroying the other gameobject
count = count - 1;
SetCountText();
}
}
Always use CompareTag() which is optimized for performance.
Setting the Collider IsTrigger property will then make use of the OnTriggerEnter events and not the OnCollisionEnter anymore.
On the first physics update where the collision is detected, the
OnCollisionEnter function is called. During updates where contact is
maintained, OnCollisionStay is called and finally, OnCollisionExit
indicates that contact has been broken. Trigger colliders call the
analogous OnTriggerEnter, OnTriggerStay and OnTriggerExit functions.
See the docs
Your present object is using a non-trigger collider, so you should use OnCollisionEnter. The CompareTag call you tried in OnTriggerEnter is preferable. You should use that.
Also, you are currently trying to destroy the gameobject that the ThirdPersonUserController is attached to (gameObject). Instead, destroy the gameobject of the colliding collider (other.gameObject) with Destroy(other.gameObject);:
void OnCollisionEnter(Collision other)
{
if (other.gameObject.CompareTag("Present"))
{
Destroy(other.gameObject);
count = count - 1;
SetCountText();
}
}
So, I'm making a coin system. When a player collides with trigger collider, i want it to disable only the object it collided with.
this.SetActive(false);
Finding object by name
void OnTriggerEnter (Collider other)
{
if (other.tag == "Coin")
{
this.SetActive(false);
}
}
/
You were very close with your original solution, but you may be misunderstanding what is actually happening here. So I've documented my solution to show the difference.
/* The following script is called when a Rigidbody detects a collider that is
* is set to be a trigger. It then passes that collider as a parameter, to this
* function.
*/
void OnTriggerEnter (Collider other)
{
// So here we have the other / coin collider
// If the gameObject that the collider belongs to has a tag of coin
if (other.gameObject.CompareTag("Coin"))
{
// Set the gameObject that the collider belongs to as SetActive(false)
other.gameObject.SetActive(false);
}
}
If you want the coin to be removed from the scene, because you don't expect it to ever be reactivated, then you can amend this code to the following:
void OnTriggerEnter (Collider other)
{
if (other.gameObject.CompareTag("Coin"))
{
// Removes the coin from your scene instead
Destroy(other.gameObject);
}
}
I tried using OnCollisionExit, but it doesn't detect the other object's box collider 2D being disabled (When I disable the other objects box collider 2D it doesn't detect it as stopping being collided). I need the other objects collider to be disabled because I'm using it as a range indicator for punching and I don't want it to interact with other objects(example: The player pushes away the enemy with his range indicator). Is there another method I could use?
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.collider.tag == "PunchRange")
{
Player.GetComponent<Fight>().PunchInRange = true;
}
if (collision.collider.tag == "KickRange")
{
Player.GetComponent<Fight>().KickInRange = true;
}
}
private void OnCollisionExit2D(Collision2D collision)
{
if (collision.collider.tag == "PunchRange")
{
Player.GetComponent<Fight>().PunchInRange = false;
}
if (collision.collider.tag == "KickRange")
{
Player.GetComponent<Fight>().KickInRange = false;
}
}
I want it to detect whenever it is not colliding with an object even when the object's box collider 2D is disabled.
You have to restructure your introduction as it is so convoluted idk what you exactly want.
To detect collision or its lack you have to use colliders(maybe put additional collider but as a trigger). However what I understood is that you want to use them as triggers. On Collider2D component there is value isTrigger that you can set. Setting it makes the collider still work but not phisically (other colliders can pass thru it). Set that on the collider and use:
https://docs.unity3d.com/ScriptReference/MonoBehaviour.OnTriggerExit2D.html
That should work for you.
I am trying to play a particle effect when an enemy is killed but it seems to play on a randomly selected one rather than the one that was hit. However the enemy that was hit still disappears and still add points to the score.
At the moment I have three scripts to carry this out (All have been shortened so I'm only showing the relevant code):
One which is attached to boxes that are thrown at enemies that detects if they have collided with an enemy prefab.
void OnCollisionEnter (Collision theCollision) {
if (canProjectileKill == true) {
// If the projectile hits any game object with the tag "Enemy" or "EnemyContainer".
if (theCollision.gameObject.tag == "Enemy") {
GameObject.Find("EnemyExplosion").GetComponent<enemyDeath>().ProjectileHasHitEnemy();
// Destroy the projectile itself.
Destroy (gameObject);
// Destroy the game object that the projectile has collided with (E.g. the enemy).
Destroy (theCollision.gameObject);
GameObject.Find("Manager").GetComponent<projectileSpawner>().deleteProjectile();
}
}
}
Another that is attached to the enemy prefabs which detects if they have been hit by a box.
void OnCollisionEnter (Collision theCollision) {
if(theCollision.gameObject.tag == "Projectile") {
GameObject.Find("EnemyExplosion").GetComponent<enemyDeath>().EnemyHasBeenHit();
}
}
I then run an if statement asking if both the box has hit the enemy prefab AND if the enemy prefab has been hit by the box in an attempt to identify a single prefab rather than all of them. However this still doesn't work.
public bool HasProjectileHitEnemy;
public bool HasEnemyBeenHitByProjectile;
void Start () {
gameObject.particleSystem.Stop();
HasProjectileHitEnemy = false;
HasEnemyBeenHitByProjectile = false;
}
public void ProjectileHasHitEnemy () {
// From projectile.
HasProjectileHitEnemy = true;
}
public void EnemyHasBeenHit () {
// From enemy.
HasEnemyBeenHitByProjectile = true;
PlayParticleSystem();
}
public void PlayParticleSystem () {
if (HasEnemyBeenHitByProjectile == true && HasProjectileHitEnemy == true) {
gameObject.particleSystem.Play();
HasProjectileHitEnemy = false;
HasEnemyBeenHitByProjectile = false;
}
}
}
I am aware this is a long question but I have been stuck on this for over a week, so any help would be much appreciated. Thank you :)
I'm not sure what kind of object EnemyExplosion is, but your problem seems to be the search for this object. During OnCollisionEnter you know exactly between which objects the collision occurred. But now you're starting a search for any object that is called EnemyExplosion. That's the reason your particles appear random.
Update:
Ok, with your structure something like that
EnemyContainer
- EnemyExplosion
- Particle System
- EnemyModel
- Collider
If EnemyModel contains the collider, you can get to EnemyExplosion and finally enemyDeath the following way.
var explosion = theCollision.transform.parent.gameObject.GetComponent<EnemyExplosion>();
explosion.GetComponent<enemyDeath>().ProjectileHasHitEnemy();
Now that you're accessing the correct object, you can remove some of your double checks and rely on one collider event.
I seem to have found a way around this. Instead I've just set it to instantiate the particle system whenever an enemy detects that it has collided with a projectile. I then use a Coroutine to delete the particle system 2 seconds after.