- Home /
Detect Side of Collision
I'm making a brick breaker game, and I'm trying to detect which side of the bricks the ball is hitting, so I can know which way to make the ball bounce.
I have this script attached to each of the bricks, and I'm trying to use raycasting to figure out which side of the brick the ball hits. It works part of the time, but sometimes the ball goes right through the object. Does anyone know how I can fix this?
using UnityEngine;
using System.Collections;
public class Brick : MonoBehaviour {
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
void OnCollisionEnter(Collision collision) {
// Raycasting to determine what side of the brick the ball hits
Ray myRay = new Ray(transform.position, collision.gameObject.transform.position);
RaycastHit myRayHit;
Physics.Raycast(myRay, out myRayHit);
Vector3 myNormal = myRayHit.normal;
myNormal = myRayHit.transform.TransformDirection(myNormal);
if (myNormal == gameObject.transform.forward) {
// If the ball hits the top of the brick
Destroy(gameObject);
collision.gameObject.GetComponent<Ball>().speedZ = -collision.gameObject.GetComponent<Ball>().speedZ;
}
else if (myNormal == -gameObject.transform.forward) {
// If the ball hits the bottom of the brick
Destroy(gameObject);
collision.gameObject.GetComponent<Ball>().speedZ = -collision.gameObject.GetComponent<Ball>().speedZ;
}
else if (myNormal == gameObject.transform.right) {
// If the ball hits the right of the brick
Destroy(gameObject);
collision.gameObject.GetComponent<Ball>().speedX = -collision.gameObject.GetComponent<Ball>().speedX;
}
else if (myNormal == -gameObject.transform.right) {
// If the ball hits the left of the brick
Destroy(gameObject);
collision.gameObject.GetComponent<Ball>().speedX = -collision.gameObject.GetComponent<Ball>().speedX;
}
}
}
Why not just use some simple maths to figure out collisions?
Because I can't get the position of the surface of the object, so if I try and compare positions of the bricks to the ball, then the ball would have overlapped with the brick when it detects the collision.
Answer by fafase · Jun 13, 2013 at 05:55 AM
Simply use dot product/Angle and cross product to figure out where it comes from.
Use the normal of the hit and compare to a world vector of your choice. With dot only you can determine if it is front or back of the box.
To determine if side you can use also cross product.
float angle = Vector3.Angle(hit.normal,Vector3.forward);
if(Mathf.Approximately(angle, 0))// back
if(Mathf.Approximately(angle, 180))// front
if(Mathf.Approximately(angle, 90)){
Vector3 cross = Vector.Cross(Vector3.forward,hit.normal);
if(cross.y > 0) // Right
else // left
}
That should be it.
Is the hit the same "myRayHit" that I have in my code already, or is it something else?
This should go on the sphere and hit is:
Vector3 hit = hit.contacts[0].normal;
It's not working, I put a debug log statement inside each of the if blocks and none of them print out when I hit a brick, so it seems like none of the if statements are working.
This is my OnCollisionEnter function, do you think you could take a look? It's attached to the ball. The tags are there to control bouncing off the walls as well, that part works fine.
void OnCollisionEnter(Collision collision) {
if (collision.gameObject.tag == "horizontalWall") {
speedZ = -speedZ;
}
else if (collision.gameObject.tag == "verticalWall") {
speedX = -speedX;
}
else if (collision.gameObject.tag == "brick") {
Vector3 hit = collision.contacts[0].normal;
Debug.Log(hit);
float angle = Vector3.Angle(hit, Vector3.forward);
if ($$anonymous$$athf.Approximately(angle, 0)) { // top
Destroy(collision.gameObject);
speedZ = -speedZ;
}
if ($$anonymous$$athf.Approximately(angle, 180)) { // bottom
Destroy(collision.gameObject);
speedZ = -speedZ;
}
if ($$anonymous$$athf.Approximately(angle, 90)) {
Vector3 cross = Vector3.Cross(Vector3.forward, hit);
if (cross.y > 0) { // right
Destroy(collision.gameObject);
speedX = -speedX;
}
else { // left
Destroy(collision.gameObject);
speedX = -speedX;
}
}
}
}
I would recommend to place the debug at
else if (collision.gameObject.tag == "brick")
Then try with Dot product:
if (Vector3.Dot(hit,Vector3.forward) > 0) { // top
// Back
}else if(Vector3.Dot(hit,Vector3.forward) < 0){
// Front
}else if(Vector3.Dot(hit,Vector3.forward) == 0){
// Sides
}
Answer by Forest3 · Sep 21, 2015 at 09:35 AM
Hi,
I just wanted to share the code I made based on the replies from @fafase. It has slight modifications to work in a 2D project.
Notice that I decided to display what side of the PLAYER is hitting the wall, not what side of the wall was touched. I mean, it displays "Left" for the left side of the player (which is hitting the right side of a wall). I did this because it makes more sense for my game.
void OnCollisionEnter(Collision col)
{
if (col.gameObject.tag.Equals("Wall"))
{
Vector3 hit = col.contacts[0].normal;
Debug.Log(hit);
float angle = Vector3.Angle(hit, Vector3.up);
if (Mathf.Approximately(angle, 0))
{
//Down
Debug.Log("Down");
}
if(Mathf.Approximately(angle, 180))
{
//Up
Debug.Log("Up");
}
if(Mathf.Approximately(angle, 90)){
// Sides
Vector3 cross = Vector3.Cross(Vector3.forward, hit);
if (cross.y > 0)
{ // left side of the player
Debug.Log("Left");
}
else
{ // right side of the player
Debug.Log("Right");
}
}
}
}
In my particular case the player is rectangular and there's no way to touch the corners, so this works great.
Best regards!
Works great for my prototype. Thank you much appreciated
For this to work in a 2D project with Unity 2020.1.6f1, rewrite the first line as :
void OnCollisionEnter2D(Collision2D col)
Your answer
Follow this Question
Related Questions
Rigid Body Collision Detection 1 Answer
Very fast objects and collision detect + Optimize issue 1 Answer
Detect if ball is on a platform or if it fell 4 Answers
Trouble with raycast hitting a tagged object 0 Answers
Jittery Collision Detection 0 Answers