3
votes

One of the features of the raycast controller for my platformer game is that the player can jump down through 1-way platforms. This works nicely, but I wanted to add additional functionality where the player will only be able to jump down if the platform to which he or she will jump is within a certain distance.

My platform controller's functionality for jumping down works like this:

  1. My character shoots a ray from below its box collider, and detects if it's on ground or not.
  2. If it is, and if the ground is tagged as a 1-way platform, and the player presses the jump down button, it will jump down to the next platform, regardless of distance.

I have tried making a jump down method that creates a new raycast from the center of the character's box collider (which is not shown in the code below, as I have been entirely unsuccessful so far), and checks for a collision at a certain distance. My problem is that it always detects the collider of the platform from which it is about to jump down, and never gets a chance to detect anything else.

How do I get this new raycast to ignore the first collision, potentially detect a collision within the ray’s distance, and if it hits something allow my character to jump down? Also, do I even need to do a new raycast, or can I just use the current raycasts on my controller?

Here's a bit of my raycast controller script that hopefully explains some of the functionality:

private void MoveVertically(ref Vector3 deltaMovement)
{
    float directionY = Mathf.Sign(deltaMovement.y);
    float rayLength = Mathf.Abs(deltaMovement.y) + skinWidth;

    for (int i = 0; i < verticalRayCount; i++)
    {
        Vector2 rayOrigin = (directionY == -1) ? raycastOrigins.bottomLeft : raycastOrigins.topLeft;
        rayOrigin += Vector2.right * (verticalRaySpacing * i + deltaMovement.x);

        RaycastHit2D hit = Physics2D.Raycast(rayOrigin, Vector2.up * directionY, rayLength, collisionMask);

        Debug.DrawRay(rayOrigin, Vector2.up * directionY * rayLength, Color.red);

        if (hit)
        {
            if (hit.collider.tag == "platformOneWay")
            {
                if (directionY == 1)
                {
                    State.IsCollidingWithOneWay = true;
                    State.IsCollidingAbove = false;
                    continue;
                }

                if (CanJumpDown)
                {
                    State.IsCollidingBelow = false;
                    continue;
                }
            }

            deltaMovement.y = (hit.distance - skinWidth) * directionY;
            rayLength = hit.distance;
2

2 Answers

5
votes

Actually, Unity3D provides an API called Physics2D.RaycastAll that returns not only the first raycast hit but all raycast hits within the maximum distance as an array.
Physics2D.RaycastAll takes two Vector2 as parameters (the raycast origin and direction) and returns an array of RaycastHit2D, which is ordered by the distance from the origin to the hit point.
Here is a bit of code that shows you what this function does:

public class Raycaster : MonoBehaviour
{
    private void Start()
    {
        RaycastHit2D[] hits = Physics2D.RaycastAll(transform.position, Vector2.down);
        for (int i = 0; i < hits.Length; ++i)
        {
            Debug.LogFormat("The name of collider {0} is \"{1}\".", 
                i, hits[i].collider.gameObject.name);
        }
    }
}
1
votes

Another way would be to create LayerMasks. Create a new layer in the Unity editor and assign the layer to the Game Object where your collider is attached it.

Get the layer mask ID in your code using LayerMask.GetMask and add it as an parameter. Now your Raycast will only cast colliders assigned to the layer.

var layerMask = LayerMask.GetMask ("myLayerName");
var ray = Camera.main.ScreenPointToRay (Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast (ray, out hit, 100.0f, layerMask)) {
    Debug.Log ("Got ya!");
}