I have a script that is a attached to a box, the box has two colliders on for physics and one (slightly bigger) for detecting OntriggerEnters when my player walks over it. I have a script attached to the box that does the following:
public class ColorChangeCollision : MonoBehaviour {
private GameObject Element;
private Color32 BaseColor, CollisionColor;
private Material ElementMaterial;
void Start () {
Element = this.gameObject;
BaseColor = new Color32(255,255,255,255);
CollisionColor = new Color32((byte)Random.Range(0, 255),(byte)Random.Range(0, 255),(byte)Random.Range(0, 255), 255);
ElementMaterial = Element.gameObject.GetComponent<Renderer>().material;
}
private void OnControllerColliderHit(ControllerColliderHit other)
{
Debug.Log("collision...");
ElementMaterial.color = CollisionColor;
}
private void OnTriggerEnter(Collider other)
{
Debug.Log("enter");
}
private void OnTriggerStay(Collider other)
{
Debug.Log("staying..");
}
private void OnTriggerExit(Collider other)
{
Debug.Log("left...");
ElementMaterial.color = BaseColor;
}
}
Main problem OnTriggerEnter or OnControllerColliderHit is never called nor are the other trigger events..
See below for an image of the setup of the box, and its components:
See here for my player who should be calling the OntriggerEnter or OnControllerColliderHit function of the box:
EDIT I modified all the elements to the suggestions of #Programmer. But the OnControllerColliderHit event is still not being called.. (note this function is attachted on the box )
There are 3 issues on how you setup your scene and your code:
1. You are missing a Rigidbody. This should be attached to the "Cube" GameObject since it's not ok to attach Rigidbody to GameObject with a CharacterController.
2. You have two BoxColliders attached to one GameObject ("Cube"). Do not do this. This can cause many issues including a callback function being called multiple times or non at-all.
What to do when you need both trigger and non trigger collider?
Create an empty child GameObject put the trigger there. You can do that.
3. You are using CharacterController so you should remove the OnTriggerXXX callback functions. When using CharacterController, it recommended to use it's callback function OnControllerColliderHit.
void OnControllerColliderHit(ControllerColliderHit hit)
{
Rigidbody body = hit.collider.attachedRigidbody;
......
}
4. CharacterController is not mean to be used as triggers. They are used to detect collisions.
You have two options to make it detect triggers:
A. Remove the isTrigger from the "Cube" Object
Or
B. Add a child empty GameObject under the CharacterController then add the CapsuleCollider or any other collider to it. You don't have to mark it as isTrigger since the "Cube" is already marked as so. You also shouldn't have Rigidbody attached to this child collider.
Create a new layer named "CC" and make the CharacterController and the new child GameObject to be in the "CC" layer. Go to your physics collision matrix settings and make it so that "CC" layer cannot collide with "CC" layer. This will make sure that the CharacterController doesn't detect collsion from tis child object.
You can now use your OnTriggerXXX callback functions to detect collision between the CharacterController and the "Cube" object.
One of them needs to have rigidbody to detect collisions.
Related
Context
I'm developing an AbstractAliveEntity, which have basically three functions:
Sight: Detect interactable objects. (Collider + Raycast)
Detection: Detect anothers AbstractAliveEntities (Collider + Raycast)
Hearing: Hear noises (Collider)
Currently i'm creating via script empty gameObjects with these colliders.
What i want
I want to know which collider was trigger in OnTriggerEnter and OnTriggerExit
Code
Creating the Sphere collider
private void Start() {
// Creating empty gameObject
sightGameObject = new GameObject("Sight");
sightGameObject.transform.parent = transform;
sightGameObject.transform.localPosition = new Vector3(0, 0, 0);
// Add Component Sphere
_sightInteractable = sightGameObject.AddComponent<SphereCollider>();
// _sightInteractable = gameObject.AddComponent<SphereCollider>();
_sightInteractable.radius = radiusInteractableDetection;
_sightInteractable.isTrigger = true;
}
Detecting
private void OnTriggerEnter(Collider other) {
// How can i detect which collider was?? this.gameObject = "Player" (i want "Sight")
}
Since Unity is originally designed around a component based approach my approach would be to split up the three separate "detection" systems into separate GameObjects with their own collider and script.
AliveEntity
SightController
ColliderA
DetectionController
ColliderB
HearingController
ColliderC
Then you can use the OnTrigger in each separate script to fire a notification to the main AbstractAliveEntity which then handles them on a case by case basis.
Main Script
OnSight(Collider other) {
// Do sight related actions here
}
OnDetection(Collider other) {
// Do detetction related actions here
}
OnHearing(Collider other) {
// Do hearing related actions here
}
Then for each respetive detector:
// reference to master script
public AbstractAliveEntity aae;
OnTriggerEnter(Collider other) {
// replace function with whatever you want to use
aae.OnSight(other)
}
The Added advantage here is that you are now also free to design 'blind' or 'deaf' Entities without all too much hassle. (You simply do not add the respective components)
other.gameObject to get the gameObject the other collider is attached to.
Check unity documentation about collider here for more info.
I assigned a projectile to my enemy that shoots at the player, and this projectile has a collider2d which I marked as a trigger. And it doesn't seem to recognize my player collider. The projectile just goes through my player.
void OnTriggerEnter2d (Collider2D other)
{
Player _player = other.GetComponent<Player>();
if (_player != null)
_player.ChangeHealth(1);
Destroy(gameObject);
}
My Player Components:
There is a small typo in your method name. The "d" in "2d" needs to be capitalized.
onTriggerEnter2d ---> onTriggerEnter2D
void OnTriggerEnter2D (Collider2D other)
{
Player _player = other.GetComponent<Player>();
if (_player != null)
_player.ChangeHealth(1);
Destroy(gameObject);
}
Without seeing your code, I will make the assumption that your 'player' object does not have a Rigidbody2D attached to it.
I strongly suggest you read the docs on colliders and how they interact with each other but as a very brief summary; In order for collisions to occur between two objects, at least one of them must have a Rigidbody or Rigidbody2D attached to them.
Attach a Rigidbody2D component to your player (you may want to mark it as kinematic so collisions don't affect it's position/rotation etc.) and it should then work as you expect.
I would change your code in OnTriggerEnter2D to this:
void OnTriggerEnter2D (Collider2D other)
{
// In your scene you have just two objects, so not really required now
// may be interesting for you when you have more objects there
if(other.gameObject.CompareTag("Player"))
{
//Here add .gameObject to refer the GameObject which contains the collider2D
Player _player = other.gameObject.GetComponent<Player>();
if (_player != null)
_player.ChangeHealth(1);
Destroy(gameObject);
}
}
Besides that. You have added an image of the components in the GameObject Player. Add the same for the Bullet, or at least check this two things:
The bullet is also in position Z = 0
The bullet has got a collider2D with the isTrigger feature as
true (checked), to detect the player.
I have two objects in my scene, one which only has a BoxCollider2D (the Column class) and the second object has a Rigibody2D as well as its own BoxCollider2D (Player class).
I added a script to the first object to have an OnCollisionEnter2D. I see it gets triggered when my second object collides with it, and it bounces my 2nd object back when it tries to enter.
I do see my OnCollisionEnter2D method getting called. But if I move my 2nd object again to my first object it gets bounced back again, however I don't see my OnCollisionEnter2D method getting called the 2nd time.
Is this intended behavior? If so, what method would get called every time a collision occurs between these two objects?
Note: I saw OnCollisionStay2D get called a few more times and then it stopped. I assume this is when it's bouncing my 2nd object out. I also see that OnCollisionExit2D never got called. I zoomed into the editor and saw clearly the green lines of the BoxCollider2D did not overlap between my objects so it should've exited the collision when it bounces it back.
public class Column : MonoBehaviour
{
private BoxCollider2D columnColl;
// Start is called before the first frame update
void Start()
{
columnColl = GetComponent<BoxCollider2D>();
}
// Update is called once per frame
void Update()
{
}
private void OnCollisionExit2D(Collision2D collision)
{
Debug.Log("Collision Exit");
}
void OnCollisionStay2D(Collision2D collision)
{
//Debug.Log("Collision Stayed");
}
void OnCollisionEnter2D(Collision2D collision)
{
Debug.Log("Collision Happened");
}
}
and
public class Player : MonoBehaviour
{
public float xMoveSpeed = 1f;
private Rigidbody2D rbody;
// Start is called before the first frame update
void Start()
{
rbody = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown("d"))
{
rbody.position = rbody.position + new Vector2(xMoveSpeed, 0);
}
}
}
You shouldn't "manually" change rbody.position.
Rather use Rigidbody.MovePosition
Use Rigidbody.MovePosition to move a Rigidbody, complying with the Rigidbody's interpolation setting.
If Rigidbody interpolation is enabled on the Rigidbody, calling Rigidbody.MovePosition results in a smooth transition between the two positions in any intermediate frames rendered. This should be used if you want to continuously move a rigidbody in each FixedUpdate.
Set Rigidbody.position instead, if you want to teleport a rigidbody from one position to another, with no intermediate positions being rendered.
You also should rather do it in FixedUpdate which is used for the Physics instead of Update
void FixedUpdate()
{
if (Input.GetKeyDown("d"))
{
rbody.MovePosition(rbody.position + new Vector2(xMoveSpeed, 0);
}
}
Also make sure at least one of the objects is not kinematic as to OnCollisionEnter:
Collision events are only sent if one of the colliders also has a non-kinematic rigidbody attached.
I want to check if my ship/s collided and not some other objects.
So this is the script that i attached to a GameObject and the GameObject have box collider and Rigidbody. The box collider: Is Trigger set to on. And he size is 500 600 500. The Rigidbody i didn't change anything and Use Gravity is on.
When running the game i have many cloned gameobjects each one Tagged as "Sphere" but in the script when i check the tag name the collider is "Untagged".
What i'm trying to do is to make sure the collided object is a cloned spaceship.
using UnityEngine;
using System.Collections;
public class InvisibleWalls : MonoBehaviour {
public float smooth = 1f;
private Vector3 targetAngles;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
void OnTriggerExit(Collider other)
{
if (other.tag == "Sphere")
{
targetAngles = other.transform.eulerAngles + 180f * Vector3.up;
other.transform.eulerAngles = Vector3.Lerp (other.transform.eulerAngles, targetAngles, smooth * Time.deltaTime);
}
}
}
This is the part where i'm trying to check and make that a ship is collided:
if (other.tag == "Sphere")
But when using break point it does stop on this line when the pbject collided but the other.tag the tag is "Untagged".
Screenshot showing the object spaceship cloned that is tagged as "Sphere"
And this screenshot showing the gameobject with the box collider and the rigidbody
From what i can gather,
You CrashLandedShip objects do not have colliders, adding colliders should work.
Also note that for triggers to work, One of the objects (the terrain or the ship) has to be a non-trigger (2 triggers will not cause a collision or trigger event)
So try this : Add a sphere collider to your CrashLandedShip_UpsideDown prefab and make sure they are not set with IsTrigger
The rest of your code looks fine.
I have a invicible GameObject called trigger, and when my Hero collides with it, a chandelier falls.
I want to give the chandelier a Rigidbody so it falls, and you can collide with it and maybe use it.
If you can explain to me how collision works and show how to do something if to gameObject collides its would realy cool still new at Unity.
using UnityEngine;
using System.Collections;
public class Collider : MonoBehaviour {
public GameObject chandelier;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
//When my hero collides with trigger go to the fall function
void OnTriggerEnter (Collider other) {
if (other.tag == "Trigger")
{
fall();
}
}
//Add Rigidbody to the GameObject called chandelier
void fall ()
{
chandelier.rigidbody.AddForce(1, 1, 1);
}
}
For collision to work at least one of your colliding gameObjects must have a rigidbody attached to it, since you using OnTriggerEnter method, the following setup should work:
First object:
-collider (Marked as trigger)
-rigidbody (Marked as kinematic)
Second object
-collider (Marked as trigger)
Then when the objects collide, "OnTriggerEnter" method will get called and you can add the physical
rigidbody to the second object, please notice that you cannot add a rigid body as you mentioned in your post
// This will cause to Exception if there is no rigidbody attached
chandelier.rigidbody.AddForce(1,1,1);
So basically you have to options:
-Add a rigidbody to your game object through Unity editor and set it to kinematic, then remove "Is kinematic" flag in your collision method, like this:
rigidbody.isKinematic = false;
-Add the rigidbody after collision using the following code:
gameObject.AddComponent< Rigidbody >();
Afterwards you can add the required force or if you only want it to fall, simply add gravity to the rigidbody:
rigidbody.useGravity = true;