user2686299 user2686299 - 9 days ago 5
C# Question

CircleCast goes through a collider

I'm working on a custom physics behaviour and use CircleCast to determine whether there is ground/wall or not. But in some cases CircleCast goes through a collider without hitting it. It seems that the issue appears when start circle a little bit overlaps a collider.

How do I fix it, or what do I do wrong?

using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MonoBehaviour
{
public Rigidbody2D ball;
public CircleCollider2D circleCollider;
public float speed;
public LayerMask layerMask;
public bool grounded;
public Transform graphics;
public Transform startCast, endCast;
public GameObject tempBall;

private int maxPathSize = 5;
private Queue<GameObject> path;
private float realRadius;
private Vector2 roughDirection;
private float rayLength;
private Vector2 realDirection;
private Vector2 gravity;
private Vector2 preferedDirectionOfMove;

// Use this for initialization
void Start()
{
path = new Queue<GameObject>();
realRadius = circleCollider.radius * ball.transform.localScale.x;
rayLength = realRadius + (realRadius / 10);
gravity = new Vector2(0, -9.81f);
}

// Update is called once per frame
void Update()
{
// if (Time.frameCount % 10 == 0)
{
preferedDirectionOfMove = gravity;
if (Input.GetKey(KeyCode.RightArrow) && grounded)
{
preferedDirectionOfMove = ball.transform.right;
}
if (Input.GetKey(KeyCode.LeftArrow) && grounded)
{
preferedDirectionOfMove = -ball.transform.right;
}

Move();

}
}

void Move()
{
if (grounded && preferedDirectionOfMove == gravity)
return;
Debug.Log("***********");
AddToPath(ball.position);
realDirection = (preferedDirectionOfMove * speed * Time.deltaTime);
Color color = Color.black;
Vector2 oldPos = ball.position;
Color colorX = RandomColor();
DrawX(ball.position, colorX, 0.2f, 1, 0.5f);
RaycastHit2D hit = Physics2D.CircleCast(ball.position, realRadius, realDirection.normalized, realDirection.magnitude, layerMask);
if (hit)
{
DrawX(hit.point, colorX, 0.2f, 0.5f, 1.5f);
SetAngle(hit);
ball.position = GetNewPosition(hit);
grounded = true;
Debug.Log("grounded: " + true);
}
else
{
ball.position += realDirection;
DrawX(ball.position, colorX, 0.2f, 0.5f, 1.5f);
Debug.Log("grounded: " + false);
color = Color.red;
grounded = false;
}
Debug.DrawLine(oldPos, ball.position, color, 1);
}

void AddToPath(Vector2 position)
{
GameObject go = Instantiate(tempBall, position, Quaternion.identity) as GameObject;
if (path.Count >= 5)
{
Destroy(path.Dequeue());
}
path.Enqueue(go);
}

void DrawX(Vector2 position, Color color, float size, float duration, float shape)
{
Debug.DrawLine(position - Vector2.one * (size / 2f), position + Vector2.one * (size / 2f), color, duration);
Debug.DrawLine(position + new Vector2(-1 * shape, 1) * (size / 2f), position + new Vector2(1, -1 * shape) * (size / 2f), color, duration);
}

Color RandomColor()
{
return new Color(Random.Range(0f, 1f), Random.Range(0f, 1f), Random.Range(0f, 1f), 1);
}

Vector2 GetNewPosition(RaycastHit2D hit)
{
return hit.point + ((Vector2)hit.normal * realRadius);
}

void SetAngle(RaycastHit2D hit)
{
float angle2 = AngleAtan2(hit.normal, Vector2.right);
ball.MoveRotation(angle2);
}

float AngleAtan2(Vector2 from, Vector2 to)
{
return Mathf.Rad2Deg * (Mathf.Atan2(to.y, to.x) - Mathf.Atan2(from.x, from.y));
}

void OnCollisionEnter2D(Collision2D collision)
{
Debug.Log("collision enter");
}
}

Answer

Not registering hits when the casted object overlaps a collider at the start position is the default behavior. To fix this, check the Queries Start In Colliders box in the Physics 2D Manager in Project Settings.

If you don't want this behavior for all queries, you may want to look at ray casting instead since raycasts have a much lower chance of starting in a collider. If you do decide to go with a ray cast, untick the above box and start the ray cast inside your player/ball's collider itself. That way you can be sure that you'll never miss any hits because of the ray starting inside the offending collider.

Comments