Jhon Piper
Jhon Piper

Reputation: 613

Make CircleCast stop once it hits something in Unity?

I am working on a game where a 2D projectile can ricochet off of walls. I currently have the object itself ricocheting just fine, however I would like an Angry Birds Style HUD (minus the gravity) that displays where the object is going to hit a wall, and show what angle it will ricochet at: (Pardon my drawing skills)

My end goal

I have this system working fine on the technical side, all except for the fact because the raycast is endless, so the actual result looks like this:

Current result

So to be clear, in the end, I want the 1st raycast to go out infinitely until it hits something, and then when it generates the 2nd second raycast where the first one hit somthing, have the second raycast only go out a pre-specified distance (Maybe 3f or something like that), or until it hits something, in which case it should stop.

My Script:

Transform firePoint;

void Update
{
     DrawPredictionDisplay();
}

private void DrawPredictionDisplay()
    {
        Vector2 origin = firePoint.transform.position; //unity has a built in type converter that converts vector3 to vector2 by dropping the z component
        direction = firePoint.transform.up;
        float radius = 0.4f;
        RaycastHit2D hit = Physics2D.CircleCast(origin, radius, direction);

        // Draw black line from firepoint to hit point 1
        Debug.DrawLine(origin, direction * 10000, UnityEngine.Color.black);

        if (hit)
        {          
            origin = hit.point + (hit.normal * radius);
            Vector2 secondDirection = Vector2.Reflect(direction, hit.normal);            

            // Create second raycast
            RaycastHit2D hit2 = Physics2D.CircleCast(origin, radius, secondDirection);

            if (hit2)
            {
                if(hit.collider.gameObject.tag != "Destroy")
                {
                    // Enable prediction points
                    for (int i = 0; i < numOfPoints; i++)
                    {
                        predictionPoints2[i].SetActive(true);
                    }

                    // Calculate reflect direction
                    Vector2 origin2 = hit2.point + (hit2.normal * radius);
                    // Draw blue line from hit point 1 to predicted reflect direction
                    Debug.DrawLine(origin, secondDirection * 10000, UnityEngine.Color.blue);
                }      
            }
            
        }
    }

    Vector2 predictionPointPosition(float time)
    {
        Vector2 position = (Vector2)firePoint.position + direction.normalized * 10f * time;
        return position;
    }

    Vector2 predictionPointPosition2(float time, Vector2 origin, Vector2 direction)
    {
        Vector2 position = origin + direction.normalized * 10f * time;
        return position;
    }

Notes:

While I would use regular raycasts, I found that normal Raycasts don't cut it because the raycasts are only 1 pixel wide and the object is (about) 512px by 512px, meaning the object would physically touch the wall before the raycast did, causing inaccuracy.

I've already created a system that generates dots along the raycast path in a similar vein to Angry Birds so that the player can see what the raycasts are doing in the game view, but since it's not relevant to the question I removed that part of the code from my script above. Which means all I need to do limit how far the raycasts go, not find a way for the player to see what's happening. (I'm addng this in the notes to avoid bringing up the conversation of whether or not the player can see what's happening.)

firepoint is the barrel/tip of the weapon that the projectile is fired from. (The weapon rotates according to/following to the mouse)

Upvotes: 1

Views: 2510

Answers (1)

Pluto
Pluto

Reputation: 4061

You can draw the line/path after you know if it hit an object or not:

Vector2 origin = firePoint.transform.position; //unity has a built in type converter that converts vector3 to vector2 by dropping the z component
direction = firePoint.transform.up;
float radius = 0.4f;
RaycastHit2D hit = Physics2D.CircleCast(origin, radius, direction);

var firstPathStart = origin;
var firstPathEnd = origin + direction * 10000;    

if (hit)
{          
    origin = hit.point + (hit.normal * radius);
    firstPathEnd = origin;
    
    Vector2 secondDirection = Vector2.Reflect(direction, hit.normal); 
    var secondPathStart = firstPathEnd;
    var secondPathEnd = origin + secondDirection * 3f;           

    // Create second raycast
    RaycastHit2D hit2 = Physics2D.CircleCast(origin, radius, secondDirection);

    if (hit2)
    {
        if(hit.collider.gameObject.tag != "Destroy")
        {
            // Enable prediction points
            for (int i = 0; i < numOfPoints; i++)
            {
                predictionPoints2[i].SetActive(true);
            }

            // Calculate reflect direction
            Vector2 origin2 = hit2.point + (hit2.normal * radius);
            secondPathEnd = origin2;
        }      
    }
    // Draw blue line from hit point 1 to predicted reflect direction
    Debug.DrawLine(secondPathStart, secondPathEnd, UnityEngine.Color.blue);                  
} 

// Draw black line from firepoint to hit point 1
Debug.DrawLine(firstPathStart, firstPathEnd, UnityEngine.Color.black);  

You can make a class to store the path information (how many segments does the path have, where each segment starts and ends), populate when you check for collisions and draw it at the end.

Upvotes: 1

Related Questions