OnCollisionEnter() not working in Unity3D - c#

I have an object with a mesh collider and a prefab with sphere collider. I want the instance of the prefab to be destroyed if the two collide.
I wrote the following in a script:
private void OnCollisionEnter(Collision c)
{
if (c == target)
Destroy(transform.gameObject);
print("something"); // Doesn't get printed
}
But it is not working. I have tried toggling isTrigger on both the objects.

I had the same problem of OnCollisionEnter not being called and found this question.
For me, the problem was that I was making a 2D game so the answer is to use the OnCollisionEnter2D function instead.

Have a look at this table
If you want your OnCollisionEnter to be called make sure:
(a) Both objects have a collider attached.
(b) None of the objects is a trigger collider (this will issue OnTrigger function & not OnCollisionEnter)
(c) One of the objects (doesn't matter which of them) is a rigid, non kinematic & non static object (the second don't have to be a rigid body).
(d) Due to computational difficulties MeshCollider might have hard times colliding with other mesh collider, use them with caution.
(e) Make sure both the objects are in the same layer (or at least that they collide in scene settings).
(f) If you are working in 2d - OnCollisionEnter2D will be called, rename your function.

Make sure one of them has a non-kinematic rigidbody attached. Taken from the Unity docs:
When a collision between two Colliders occurs and if at least one of them has a Rigidbody attached, three collision messages are sent out to the objects attached to them. These events can be handled in scripting, and allow you to create unique behaviors with or without making use of the built-in NVIDIA PhysX engine.
From here: Unity3D MeshCollider

I had a similar problem. The box collider wasn't as big as the collision object. Setting the x and z values to 2 units fixed the problem!

Have you tried using the OnTriggerEnter() method and setting a collider on the object to a trigger?
If it doesn't need to tell what object its colliding with you could do a simple
void OnTriggerEnter(){
Destroy(transform.gameObject);
}
Edit:
Also I have done OnCollision like this
private string hitobject;
void OnCollisionEnter(UnityEngine.Collision hit)
{
hitobject = hit.gameObject.tag;
if(hitobject == "Plane")
{
isgrounded = true;
}
}
None of the objects are triggers and they don't need rigid bodies to work.

Related

Cannot convert vector3 to collider?

I'm trying to make a 3D racing game on Unity and I am currently looking to program checkpoints. Right now I have hit a roadblock in which I can't convert vector3 to collider. Any advice?
private void OnTriggerEnter3D(Collider Checkpoint)
{
if (Checkpoint.tag == "Checkpoint")
{
Checkpoint = transform.position;
}
}
Checkpoint is a Collider data type. Try it with Checkpoint.transform.position
A Collider is not at all a Vector3. I invite you to read the documentation for both types to understand better what each is used for.
As mentioned in another comment, if you want to access a Collider position, you should use Collider.transform.position (so
Checkpoint.transform.position
in your case). A Collider component should be attached to the object your rigidbody is supposed to collide to.
If you want the Collider to respond phisically to impacts, as mentioned in the documentation, you should attach a Rigidbody to it.

Unity Physics.Raycast with LayerMask does not detect object on layer. Used bitshifting, tried inverting layer, still nothing works

novice to intermediate Unity developer here. I've been hitting a pretty significant roadblock the past ~2 days concerning the raycast detection of objects with specific layers. I've been researching this issue quite a lot, and all the solutions I've found don't seem to reflect the strange issue I'm facing.
Basically, the problem follows this sequence of events:
My player character has a vision cone shaped trigger mesh called 'InSightBox' which detects all objects with the tag 'Mob' and adds them to a List of colliders called 'MobsInRange'.
public List<Collider> mobsInRange;
public List<Collider> GetColliders()
{
return mobsInRange;
}
// Start is called before the first frame update
void Start()
{
mobsInRange = new List<Collider>();
}
// Update is called once per frame
void Update()
{
}
//add enemy with tag 'mob' to list
private void OnTriggerEnter(Collider other)
{
if(!mobsInRange.Contains(other) && other.tag == "Mob")
{
mobsInRange.Add(other);
}
}
//remove enemy with tag 'mob' to list
private void OnTriggerExit(Collider other)
{
if (mobsInRange.Contains(other) && other.tag == "Mob")
{
mobsInRange.Remove(other);
}
}
This list is then fed up to the root/parent player game object containing everything relating to the player.
public Transform closestMob;
public List<Collider> mobs;
public Transform GetClosestEnemy()
{
Transform tMin = null;
float minDist = Mathf.Infinity;
Vector3 currentPos = transform.position;
foreach(Collider trans in mobs)
{
//find enemy with closest distance and set tMin to it. Method returns tMin
float dist = Vector3.Distance(trans.transform.position, currentPos);
if(dist < minDist)
{
tMin = trans.transform;
minDist = dist;
}
}
//Debug.Log(tMin);
return tMin;
}
The player then uses a 'Look at' method to find the closest of all 'mobs' to the player. The player will set their forward transform to look at the closest mob.
Problem Step ---> 4) When the player raises their gun and attempts to shoot the closest enemy, a ray is cast with a layermask that looks only for objects on the layer 'Enemy', the 8th layer. When the ray detects the enemy, the enemy script should fire its 'TakeDamage' method which decreases the 'curHealth' variable by 8. Only problem is, the cast doesn't seem to detect the enemy object on the 'Enemy' layer.
LayerMask layerMask = 1 << 8;
void Fire()
{
//play the audio of the gunshot
StartCoroutine("SetPlaying");
RaycastHit hit;
//cast a ray from the player forward and check if the hit object is on layer 'Enemy'
if (Physics.Raycast(transform.position, transform.forward, out hit, Mathf.Infinity, layerMask))
{
//if hit object is an enemy, set its 'gotShot' bool to true
print("hit enemy");
closestMob.GetComponent<EnemyBase>().gotShot = true;
}
//play gunshot sound and stop player from turning
source.clip = fireSound;
source.PlayOneShot(fireSound, gunshotVolumeScale);
turnSpeed = 0;
}
I'll also note that all the solutions I've seen to this issue are not working for me. I declared an int variable called 'layerMask' and initialized it in Awake() by bit shifting layer 8 into it (i.e. int layerMask = 1 << 8), but it still isn't detecting it. The enemy contains all that I belive it should need for this to work, including a rigidbody, a capsule collider, the associated scripts, as well as being on the 'Enemy' layer.
This is where it gets weird (at least to my knowledge), when I invert the mask in the cast (~layerMask), it does exactly what I'd expect, and begins firing the code within the raycasts if statement when the player 'shoots' anything that doesn't have the 'Enemy' layer.
Any help would be suuuper appreciated as I'm getting to the point of slamming my face into the desk :/
Side Note: I'm getting to the point where I may just attach a 'fire range' cube trigger to my player and enabled it when the Fire() event is triggered, then have that check for game objects with the tag 'mob' as that kind of detection works most consistent to me.
First, be sure that you didn't confuse layers with tags. They are different.
Second, get rid of any implicit actions and references, i.e. don't use bitshifting, layer indices, or any non-straightforward reference. Instead, create something like this:
[SerializedField] private LayerMask _layerMask;
Use inspector to assign needed layer(s).
This way you will explicitly see, which layer you are using. This is useful not only for you, but for you in future, when you forget layers' indices. Also, for anyone who aren't familiar with the project.
Third is for debug. Be sure that your raycasting works as intended at other aspects:
Try remove layerMask and see if the ray goes where you want it to
Use custom gizmos to check if you cast the ray in a right direction
Try using RaycastAll. Maybe some objects catch (block) your ray earlier than you think
Looks like the issue was that I had my 'Enemy' prefab which contained the necessary components (RigidBody, Collider, scripts, NavmeshAgent) nested inside an empty game object. I thought at first to make something a prefab you needed to have it inside an Empty. I see now that is rather redundant and not necessary (at least in my case).
Physics.RaycastAll solved this issue as it no longer got 'halted' by the parent empty's collider and also hit the child.
I actually got it working just using a regular Physics.Raycast by rebuilding the Enemy prefab as just a single Capsule object (which I'll replace with character meshes later on).
Side Note
Before I got this new method working, I also used a different one that achieves the same goal in a pretty lightweight manner.
I added a long and thin box trigger to the front of my player so that it has enough distance to collide with any enemies within the shooting range.
I then enable the trigger any time the player enters their 'Aiming' state. If the trigger collides with any mesh that has the tag "Mob', it sets a bool on the player that indicates if the enemy is in range.
Then if the enemy is in range && the player enters the 'Firing Gun' state, a method in the Enemy Base script is fired that decrements the enemy health by a publicly decided variable called damagetaken.
Both the Raycast method and the box trigger method work equally well for me, but the box trigger one just took less time to figure out and gave me less headache haha.
Hope this helps anyone else in a bind!!!!!

Unity : is it possible to configure the scene for trigger colliders to only work with one layer?

Unity already manages the collisions and triggers well but I would like to know if I can optimise triggers to only react to a layer ?
Currently if I have many different objects with colliders in a scene I need to do :
void OnTriggerEnter (Collider other) {
if (other.gameObject.layer == myLayer) {
// do stuff here
}
}
But this means that the collisions where calculated with ALL of the colliders witch does not seem efficient.
If I want a trigger to react to the player and to the player only, is there a way to tell it to ignore all of the other layers to avoid unnecessary computing ?
Yes, using the Collision Matrix as described in the manual here.
Add a new layer mask (or just use the one you already have)
Uncheck all of the layers it can collide with except the "Player" layer
Change the game object's layer to the new modified layer
Now that object can only collide with the player, and only get triggered by the player (provided one of them has a rigid body component)

Rigidbodies touching but OnCollisionStay stops being called (Unity3D)

I want to simulate two pistons that replicates the behaviour of a balance. I'm doing this with spring joints and applying the inverse weight one another when OnCollisionStay is called. This are my pistons and their rigidbodies and joints. Are exactly the same on both.
This is my SpringForceTransmisor.cs code:
using UnityEngine;
public Rigidbody InverseJoint;
private void OnCollisionEnter(Collision collision) {
Debug.Log("Enter");
}
private void OnCollisionExit(Collision collision) {
Debug.Log("Exit");
}
private void OnCollisionStay(Collision collision) {
InverseJoint.AddForce(-(collision.rigidbody.mass * Physics.gravity));
Debug.Log("Stay");
}
And this is a video of what's happening.
So, according to the log showed on the video, OnCollisionStay() stops being called even if OnCollisionExit() had never been called. How is this possible? I've always thought that OnCollisionStay() is called every frame from OnCollisionEnter frame and OnCollisionExit frame.
Can anyone shed some light about what's going on here?
According to the documentation on OnCollisionStay, it says:
OnCollisionStay is called once per frame for every collider/rigidbody
that is touching rigidbody/collider.
Unfortunately, this is not true sometimes. The OnCollisionStay function is called few times in some cases and the call is then stopped. This is either a long time bug that has not been fixed for years or the documentation is wrong.
My usual advise to people is to abandon the OnCollisionStay function and simply set a boolean variable to true in the OnCollisionEnter function then set it to false in the OnCollisionExit function. You can then use the Update function as the OnCollisionStay function by checking that boolean variable in the Update function.
public Rigidbody InverseJoint;
bool collisionStay = false;
Collision collision = null;
private void OnCollisionEnter(Collision collision)
{
Debug.Log("Enter");
collisionStay = true;
this.collision = collision;
}
private void OnCollisionExit(Collision collision)
{
Debug.Log("Exit");
collisionStay = false;
this.collision = collision;
}
void Update()
{
if (collisionStay)
{
InverseJoint.AddForce(-(collision.rigidbody.mass * Physics.gravity));
Debug.Log("Stay");
}
}
The answer to your question would become obvious if you included in the video the part, where the "Enter" is being registered.
Now to explain what is happening, let me show an example on a traingulated sphere . Here is how it looks like (with some triangles intentionally made invisible).
Keep in mind that it is hollow, all it essentially is are a bunch of points that form triangles. Now say this is a mesh for some mesh collider component. If any other collider interacts with this sphere_mesh_collider, the only way for unity to detect it is by checking for the triangles of those two colliders having intersections. This is to say that ANY OTHER area that is not covered INSIDE the triangles will not be checked. In other words the volume of my sphere here will never detect collisions (nor call the collision stay, but the collider havent exited either, so neither it calls exit).
That is what I think is happening in your case, although I can not say for certain because I do not see the whole process of colliders starting to interact (Enter) and than coming apart (Exit).
Edit: You can easily observe this if you enable unity wireframe (or shaded wirefram) in the editor window.
Another note from the Unity documentation:
Note: [...] Collision stay events are not sent for sleeping Rigidbodies.
You could try setting Rigidbody.SleepThreshold to zero to see if that solves your problem.
Just solved this myself. This is because your rigidbodies are sleeping, try setting the rigid body sleep to "Never sleep" and it will work.

Object not being destroyed when shot with a bullet - UNITY3D C#

So in my game, there's a gun that sprays bullets, and I'm trying to make a gameObject destroy on collision with the bullets. The bullets are based off of one gameObject (Capsule). I've tried these two scripts so far:
using UnityEngine;
using System.Collections;
public class whenshot : MonoBehaviour {
void OnCollisionEnter(Collision col)
{
if (col.gameObject.name == "Bullet")
{
Destroy(col.gameObject);
}
}
}
and:
using UnityEngine;
using System.Collections;
public class whenshot : MonoBehaviour {
void OnCollisionEnter(Collision col)
{
if (col.gameObject.name == "Bullet")
{
Destroy(this); //the difference between the two is that I changed "col.gameObject" to "this"
}
}
}
I'm shooting the object but it's not disappearing/destroying itself. How can I fix this?
Here's a visual if it helps:
this refers to the object instance of the caller (this is basic OOP), i.e., whenshot, and not gameObject. So the second sample is effectively Destroying the instance of the script from the gameObject it is attached to.
The first script is technically fine, and should work, provided these conditions are met:
Either the projectile (bullet) or the target (or both) have a non-kinematic rigidbody component attached. (Unity docs.)
Both have 3D Collider components.
The name of every single bullet gameObject that collides with the target is exactly "Bullet".
All projectile objects have this script as a component.
Some suggestions
Use prefabs and tags: take your bullet primitive and store it as a prefab. Add a tag to the prefab called "Bullet". Do the same for the target and tag it as "Target". Tag the player as "Player". In the "gunController", set a reference to the bullet prefab and make it Instantiate bullets on whatever trigger you're using. In the bullet's script, use CompareTag("Target") instead of == and Destroy both the target gameObject and this.gameObject.
It seems to me that the above is the behaviour you want. If that is the case, there is no delay between collision and destruction, and hence no need to simulate physics whatsoever. Unless you have some other physics interactions with bullets/targets, mark the one without a rigidbody as a Trigger.
A Strong Suggestion
Go through Unity tutorials.
This is an example from a 2D Game I made a while back, but i think it might help.
void OnTriggerEnter2D(Collider2D other)
{
if (other.tag == "Destroyable")
{
Destroy(other.gameObject);
}
}
I used this to destroy certain blocks when the player would shoot them so many times, just switch them to the 3D Collider and Trigger, but it should do the trick for ya (i hope ^^).
edit: this script should be attached to your bullet prefab
Ok so I figured it out, it's kind of weird but apparently I was making the bullets move too fast... I had to slow down the "Bullet_Forward_Force" float to about 150f to make it work. Thanks to everyone who answered though.
I can't comment so I will make an answer:
You can make the bullet go fast, just set the collision detection to continious dynamic.
It has an almost %100 success rate.

Categories

Resources