# Determine which cannons on a ship can hit a target [closed]

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.

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.

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.

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);
}
continue;
}
if (Vector3.SignedAngle(hullParent.forward, toTarget, Vector3.up) >= sbMinAngle &&
Vector3.SignedAngle(hullParent.forward, toTarget, Vector3.up) <= sbMaxAngle)
{
if (!sbTargets.Contains(target))
{
RemoveFromOthers(target);

}
continue;

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

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

}

}
}


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

Thank you.

• I’m voting to close this question because it is a cross-post from SO. Cross-posting is not allowed on stack-exchange sites. If you think you'll get more/better answers here, then please delete the question on SO and flag the question here for moderators so we can reopen it. Sep 14 at 21:37
• If you want, I can try to have the question over there migrated & merged here. Sep 14 at 21:53
• Its okay, I got some good answers. Thank you! Sep 14 at 21:56
• @Vaillancourt I just checked the answers on SO and I can suggest merging and migrating the question from SO to here as the ideas presented in the answers in SO may be valuable to game devs here. (but not sure what happens if the repliers on SO has no account here in gamedev) Sep 15 at 23:42

Real warships (before the modern age of relying primarily on guided-missile weaponry) typically have multiple guns that can cover one side of the ship.

Each gun will have its own firing arc. If you want to solve this in a realistic way that doesn't make the math more complex, use a separate arc for each gun emplacement.

• Its my fault for not mentioning this in the question, but I was straying away from this as each ship can have up to 120 cannons. By dividing the targets into 4 sectors I can essentially take all the calculations back from those cannons and do it all at once, which should be way more optimized. Sep 14 at 23:54
• I ended up using 2 different points on the ship to calculate the SignedAngle off of. One at the bow and one at the aft of the ship, this then allows me to "cast" a trapezoidal region, like you said it best. I would add it as an answer with code, but the question is closed. Sep 14 at 23:57

## Initial calculations

Calculate these once at the beginning as if your ship is static and at origin, and later transform enemy positions with respect to your ship.

• Write the equation for lines XY, WZ (a line that passes from the front point of the ship with slope s, and another one for the bottom of the ship with slope -s)
• Solve for points X, Y, W, Z by finding intersection points of inner-circle and outer-circle and lines XY and WZ.
• Write the equation for the line YW.
• Using points X, Y, W, Z, define regions A, B, C in Polar Coordinate System as explained in the original answer below. (in the image, for simplicity I placed marks for A, B, C outside the outer-circle, but, assume they define regions between the inner and outer circles, that is A is 1 & 2, B is 3 & 4, C is 5 & 6)

## Continuous calculations

• Using polar regions, test if an enemy (point E) is in one of the regions A, B, C.
• If point E is in one of the regions A, B, C, then test if point E is above or below the relevant line of the region it belongs to:
• Region A & Above line XY = Region 2
• Region B & Below line YW = Region 4
• Region C & Above line WZ = Region 5
• If point E is in one of the regions 2, 4, 5, then it is in range.

P.S. I wasn't sure if the inside of the inner-circle is in the target area, so excluded it above. But, if you also want to target the inside of the inner-circle, you can define the inner-circle where the points X and Y are located at the front and the bottom of the ship, and add an additional test to see if the point E is within the upper half of inner-circle (a pretty easy test with polar coordinates).

(Leaving the original answer here for people looking for a simpler solution.)

You can approach the problem with Polar Coordinate System. Then you can define the regions of interest by defining r_min, r_max, t_min, t_max here:

x = r * cos t
y = r * sin t

where

r_min < r < r_max

and

t_min < t < t_max


Then, for any given point (x, y), you can solve for (r, t) and see if the point is in one of the defined regions:

r = sqrt(x^2 + y^2), which is hypotenuse.

t = atan2(y, x), which is arctangent.
(see the referenced Wikipedia article for details of this function)

• This is not a complete answer. Also, unless I'm missing something, it doesn't seem to address the original question's concern - he wants to check if the target falls in a trapezoidal region, not in a sector. Sep 14 at 21:18
• @Kevin thank you for the notice. I updated the answer for testing against the trapezoidal region. Sep 15 at 23:25