Home > Blockchain >  C# Unity 3D - Is there a way to make custom raycast shapes?
C# Unity 3D - Is there a way to make custom raycast shapes?

Time:09-15

I have a ship that will shoot targets, but cannons on the right side should never try to shot at targets on the left side of the ship. Thus I have created sectors using the SignedAngle function, this works fine for testing, but its also kind of broken as you can see from the visualization below. I have tried using boxcast but alas it also doesnt work for this use case.

img1

The above image visualizes what my script does, but this is not a solution to my problem. As targets close to the side and front of the ship will be outside the sector, for clarification what I mean, see picture 3. img2

This second image shows what happens when we increase the angle, we can now detect more targets, but we have 2 big incorrect sectors marked in red which shouldnt be there. img3 Correct

Finally, this is how I think it should look, its still a cone, but the big difference is that it starts with a wide bottom, thus it resolves the problem Im having with the current SignedAngle function which determines everything from a single point in the middle.

This is the script for assigning targets to the correct list according to which sector they are in:

foreach (Transform target in EnemyListManager.instance.enemyShips.ToArray())
        {

            if (Vector3.Distance(transform.position, target.position) > ship.mainGunCaliber.range)
                continue;

            Vector3 toTarget = target.position - transform.position;
            print(Vector3.SignedAngle(hullParent.forward, toTarget, Vector3.up));

            if (Vector3.SignedAngle(hullParent.forward, toTarget, Vector3.up) >= bowMinAngle &&
                Vector3.SignedAngle(hullParent.forward, toTarget, Vector3.up) <= bowMaxAngle)
            {
                if (!bowTargets.Contains(target))
                {
                    RemoveFromOthers(target);
                    bowTargets.Add(target);
                    print("added target to Bow");
                }
                continue;
            }
            if (Vector3.SignedAngle(hullParent.forward, toTarget, Vector3.up) >= sbMinAngle &&
                Vector3.SignedAngle(hullParent.forward, toTarget, Vector3.up) <= sbMaxAngle)
            {
                if (!sbTargets.Contains(target))
                {
                    RemoveFromOthers(target);
                    sbTargets.Add(target);
                    print("added target to SB");

                }
                continue;

            }
            if (Vector3.SignedAngle(-hullParent.forward, toTarget, Vector3.up) >= aftMinAngle &&
                Vector3.SignedAngle(-hullParent.forward, toTarget, Vector3.up) <= aftMaxAngle)
            {
                if (!aftTargets.Contains(target))
                {
                    RemoveFromOthers(target);
                    aftTargets.Add(target);
                    print("added target to Aft");

                }
                continue;
            }
            if (Vector3.SignedAngle(hullParent.forward, toTarget, Vector3.up) >= psMinAngle &&
                Vector3.SignedAngle(hullParent.forward, toTarget, Vector3.up) <= psMaxAngle)
            {
                if (!psTargets.Contains(target))
                {
                    RemoveFromOthers(target);
                    psTargets.Add(target);
                    print("added target to PS");

                }

            }
        }

Any help would be appreciated on how to tackle this problem!

Thank you.

CodePudding user response:

There are a couple options I can think of.

  1. You can use 4 (or more, for up-down range as well) points to measure the angles from instead of the single point from the center that I'd previously suggested.

enter image description here

Your code, for the starboard side, would then be more like (I think, I'm nautically inclined so I may be wrong about the labelling lol):

if (Vector3.SignedAngle(markerBowStarboard.right, toTarget, Vector3.up) >= sbMinAngle &&
    Vector3.SignedAngle(markerAftStarboard.right, toTarget, Vector3.up) <= sbMaxAngle)
{
    if (!sbTargets.Contains(target))
    {
        RemoveFromOthers(target);
        sbTargets.Add(target);
        print("added target to SB");

    }
    continue;
}
  1. You could try to adapt something from enter image description here

    But again, it's sort of just trading one calculation for another.

    1. You could create a conic cylinder model, don't render it but give it a MeshCollider, and then when objects enter the object (OnColliderEnter, I think?) you can add them to that direction's list and remove when they exit (OnColliderExit maybe?). You may need to do some more checking on when it is fully inside the conic cylinder though, because I think OnColliderExit (or whatever it's called) is triggered when the model stops colliding with the actual mesh, regardless of whether it is inside or outside of the object.

    I'm not super sure I like any of those that much, but I'm surprised that there isn't a native "ConeCast" that can take near- and far-plane dimensions... but then I guess I'm just saying to Unity "I don't wanna do it, you do it" lol.

    CodePudding user response:

    Some random ideas here. I would try discard the not desired sector with 2 sphere casts of different diameter. (enter image description here

    You can set the length of detection with maxDistance argument of the spheres cast. This will determine the length of the cone, so the Starboard Aim Sector length. The zone green lined is for the points that are detected by the beam2 but not beam1.

    What to do with those points? Some are inside the cone, and some are not. A cone is simply an infinite number of circles whose size is defined by a linear equation that takes the distance from the point. So with the distance from the rays origin in the beams direction, you know the circle size of that section of the cone. You should then easily be able to know if your point is in/out that circle, so in/out of the cone.

  • Related