Stencyl 3.4.0 is now out. Get it now!

How to take actor width/height into account for distance.

Omniso

  • Posts: 102
Turrets in my game will get the distance between them and a selected enemy. If that enemy is within a range they will fire. I've started to notice that after creating more larger enemies that they would have to seemingly get closer to a turret before they are considered in range compared to smaller enemies. I thought this might have to do with the fact that the width and height of the actor aren't considered when calculating distance and figured that might be result of the represented scenario in my game. How do
I fix this?

JeffreyDriver

  • Posts: 1312
I guess that your calculations are based on the XY centre of the actors. Can you post your code? Is this a behaviour you've created yourself?


Omniso

  • Posts: 102
Yes, they are based on the center (origin) of the actor.  I did create it.

corbanwolf

  • Posts: 10
Because in second case the x-center is further that it is in first case. When something is further it may not be in range, lol

JeffreyDriver

  • Posts: 1312
Try deducting the half width of your enemy actor from the 'range' attribute.

Omniso

  • Posts: 102
If I deducted the half width of the target from Range that would cause the range of the turret to be lower than it really is which would create a scenario where a large enemy would have to come even closer for it to fire. And if I added it that would cause the turret to attack an enemy who is visually in range if the enemy is actually a perfect square, otherwise that would cause it to shoot enemies out of range should their height be unequal to width.

JeffreyDriver

  • Posts: 1312
Sorry, I meant add. Is there much difference between the height and width of some of your actors? If the difference isn't that much, you can probably get away with it.

Otherwise, maybe the Physics Tools extension can help?

Omniso

  • Posts: 102
Unfortunately yes, there are several actors who's height and width are far from equal and cause issues. I thought so too that I could get away with it until I tested this on every actor and the difference was way too noticeable for some of them. I'll look into the Physics Tool extension, thanks.

merrak

  • *
  • Posts: 1643
If this is a top-down game then you need to find the point P on the (enemy) square that is closest to the turret T. Then check the distance between P and the T. If the enemies were circles then this would be an easy problem to solve--but because they're squares then it's a bit trickier.

Here's my solution. Note that it requires some vector algebra.

1. You can assume the closest point will be on one of the edges. Define four vectors, one for each edge.
2. For each edge vector compute the point on the vector that is closest to the turret.
3. If any of the distances computed in step 2 are within range then the enemy is within range of the turret.

Here's my code where I implemented this, but you would need to make a "VPoint2D" class. You might be able to use b2Vec2 instead (replace all references to VPoint2D with b2Vec2). I haven't tried it.

Code: [Select]
    // Project the point onto the vector uv
    public static function project( u:VPoint2D, v:VPoint2D, x:Float, y:Float ):VPoint2D
    {
        var seg = new VPoint2D( v.x - u.x, v.y - u.y );
        var pt = new VPoint2D( x - u.x, y - u.y );

        var seg_len = seg.length( );

        if ( seg_len == 0 )
        {
            VallasIsometric.vallasError( "2D01", "Zero length projection" );
            return u;
        }

        var proj_len:Float = pt.dot( seg ) / seg_len;

        if ( proj_len < 0 )
            return u;

        if ( proj_len > seg_len )
            return v;

        return new VPoint2D( seg.x * proj_len / seg_len, seg.y * proj_len / seg_len );
    }

    // Return the point on the vector uv closest to (x,y)
    public static function closestPoint( u:VPoint2D, v:VPoint2D, x:Float, y:Float ):VPoint2D
    {
        var proj:VPoint2D = VPoint2D.project( u, v, x, y );

        return new VPoint2D( proj.x + u.x, proj.y + u.y );
    }

Call the point returned by closestPoint P2. Check the distance between T and P2 and if it is within range then the turret can fire (to be more realistic, it shouldn't fire at the center, since it's still out of range. The turret should fire toward P2).

Another solution that would be less elegant would be to attach a larger, invisible actor to the enemies that acts as a sensor. When the "sensor actor" collides with the turret, then it triggers firing.