Search code examples
c#unity-game-enginevectorraycasting

Can't place rays from some start to some end


I have a start and end, i need to place an adjustable amount of rays on the line from start to end. But I can't figure out how to correctle place them. I almost finished, but rays is not placed at the end.

Green (2, 0, 2) for start and Red (2, 0, -2) for end with distance between 4

This is my result with 2 rays, the center ray need to be at the end (red ray), and next rays add should be at the center of start and end rays

What happens if i adding third ray, the end ray position isn't in use

And 5 rays. The first and the last rays need to be at the start and the end. Other 3 rays should to be between

This is my code:

public class VehicleAroundCast : MonoBehaviour
{
    [SerializeField] private int sideRaysAmount;
    [SerializeField] private Vector3 offset;

    private void Update()
    {
        Vector3 startRayPos = transform.position + Vector3.left * offset.x + Vector3.forward * offset.z;
        Vector3 endRayPos = transform.position + Vector3.left * offset.x + Vector3.back * offset.z;
        float dist = Vector3.Distance(startRayPos, endRayPos);

        Debug.DrawRay(startRayPos, Vector3.down, Color.green);
        Debug.DrawRay(endRayPos, Vector3.down, Color.red);

        for (int i = 0; i < sideRaysAmount; i++)
        {
            float step = dist / sideRaysAmount * i;
            Vector3 position = (transform.position + Vector3.left * offset.x + Vector3.forward * offset.z) + Vector3.back * step;

            Debug.DrawRay(position, Vector3.down);
        }
    }
}

Solution

  • Following up from the clarification through the comment I'd do a couple of things a bit different. I'll just edit your code so that it should work as you'd need it to and explain it in code comments.

    public class VehicleAroundCast : MonoBehaviour
    {
        [SerializeField] [Min(0)] private int intermediateRayCount;
        // I renamed this field so the name is more descriptive, intermediate meaning between start and end
        // to make sure that no number below 0 can be assigned I added the [Min(0)] attribute, a number
        // below 0 would break the logic and wouldn't make sense
    
        [SerializeField] private Vector3 offset;
    
        private void Update ()
        {
            // we don't want to clog up our Update() method with tons of lines of code
            // this serves better maintainability as well
            CastRays();
        }
    
        private void CastRays ()
        {
            Vector3 startRayPos = transform.position + Vector3.left * offset.x + Vector3.forward * offset.z;
            Vector3 endRayPos = transform.position + Vector3.left * offset.x + Vector3.back * offset.z;
    
            // if we don't have any intermediate rays we can save on the calculation that is necessary when we
            // have intermediates, we just cast the the two rays and end the method by returning
            if (intermediateRayCount == 0)
            {
                Debug.DrawRay(startRayPos, Vector3.down, Color.green);
                Debug.DrawRay(endRayPos, Vector3.down, Color.red);
    
                return;
            }
    
            // instead of using Vector3.Distance() we'll calculate the directional vector manually this way
            // we can get not only the distance (directional vectors magnitude) but the direction to move
            // along to space the rays as well
    
            // subtarting the start position from the end position gives us the directional vector
            Vector3 direction = endRayPos - startRayPos;
            // a directional vectors magnitude gives the distance from start to end
            float distance = direction.magnitude;
            // after the distance has been established we normalize the direction so that it's length is 1
            direction.Normalize();
    
            // we have at least two rays (start and end) so our total is the rays between start and end plus two
            int totalRayCount = intermediateRayCount + 2;
            // the space between the individual rays, we have to subtract one from the totalRayCount in order to 
            // place the the last ray at the end position (yes this could be optimized but I want to write out 
            // the logic fully so that it's clear what happens)
            float spacing = distance / (totalRayCount - 1);
    
            for (int i = 0; i < totalRayCount; i++)
            {
                // we can simply get the ray position by adding the direction multiplied by the spacing multiplied
                // by the number of iterations we've gone through to the start position
                // since the direction is normalized we can multiply it by the distance between rays to give it
                // the length between two rays, if we then multiply it by the number of rays the current ray is,
                // we get the distance as well as direction the current ray has to be placed away from the start
                // position
                Vector3 rayPosition = startRayPos + direction * spacing * i;
    
                // i added some color so you can see start and end
                if (i == 0) 
                    Debug.DrawRay(startRayPos, Vector3.down, Color.green);
                else if (i == totalRayCount - 1) 
                    Debug.DrawRay(endRayPos, Vector3.down, Color.red);
                else 
                    Debug.DrawRay(rayPosition, Vector3.down, Color.white);
            }
        }
    }