Adventure Game Studio

AGS Support => Advanced Technical Forum => Topic started by: Calin Leafshade on Sat 28/05/2011 01:36:40

Title: A more difficult algorithm: Line of sight and ballistic range.
Post by: Calin Leafshade on Sat 28/05/2011 01:36:40
I have another, more difficult algorithm to implement and I dont quite understand it.

Ok so imagine this fellow here:

(http://i.imgur.com/vr2OB.png)

He is an archer stood high up. So he should be able to fire his arrows further than someone at ground level.

now look at this fellow:

(http://i.imgur.com/6ZyfY.png)

He has a big block of terrain in front of him which should block his line of sight.

Now how do i calculate where this guy can aim and also *how far* his shot will go in any particular direction.

I found this article on LOS but i dont really understand it much:

http://www-cs-students.stanford.edu/~amitp/Articles/LineOfSight.html

Help would be appreciated. I'll give you credit on ma gamez! (but only if SquareEnix dont want to work with me)
Title: Re: A more difficult algorithm: Line of sight and ballistic range.
Post by: abstauber on Sat 28/05/2011 12:27:43
First of all: wow!  :o

I'm sorry that I can't offer much help, as this is way out of my league. But I can offer some more stuff to read:
http://roguebasin.roguelikedevelopment.org/index.php?title=Articles#AI

At least those guys made me understand A* and they have a few article about LOS and FOV.

*pressing thumbs that those screenies make it to a final game*
Title: Re: A more difficult algorithm: Line of sight and ballistic range.
Post by: Calin Leafshade on Sat 28/05/2011 14:35:25
Thanks for the link.

If i use this (http://roguebasin.roguelikedevelopment.org/index.php?title=Eligloscode) algorithm and just ignore the higher = more distance thing then It should be fairly easy.

EDIT: Ok here's what i have:

(http://i.imgur.com/4Mcqb.png)

if the circled guy is shooting then the crossed tiles should not be visible to him if a straight line is used. Heres my algorithm:


void DoFov(Point2D *pos, float x,float y,  int radius)
{
 int i;
 float ox,oy;
 ox = IntToFloat(pos.X)+0.5;
 oy = IntToFloat(pos.Y)+0.5;
 while (i < radius){
   int index = tileIndex(FloatToInt(ox, eRoundNearest), FloatToInt(oy, eRoundNearest));
   if (!SaneIndex(index)) return;
   if(tiles[index].Height - tiles[tileIndex(pos.X, pos.Y)].Height > 1)return;
   tileMask[tileIndex(FloatToInt(ox, eRoundNearest), FloatToInt(oy, eRoundNearest))] = 1;//Set the tile to visible.
   ox+=x;
   oy+=y;
   i++;
 }
}

void SetFOV(Point2D *pos, int radius)
{
 float x,y;
 int i;
 ClearTileMask();
 while (i < 360){
   x = Maths.Cos(IntToFloat(i) * 0.01745);
   y = Maths.Sin(IntToFloat(i) * 0.01745);
   DoFov(pos, x, y, radius);
   i+=1;
 }
}


Title: Re: A more difficult algorithm: Line of sight and ballistic range.
Post by: Kweepa on Sat 28/05/2011 15:19:17
If you're going to fire the arrow at the same angle every time then you can (pre?)compute the path that the arrow will take and then check it against the map heights for the squares it goes through.

Is the idea to decide whether an arrow can reach a particular target?
Or a particular square?
Or to draw the range of the arrow in all directions?
Can you fire anywhere?

There are lots of different problems...

[EDIT] This http://www.youtube.com/watch?v=mpkTHyfr0pM is a fascinating series of videos about mechanical ballistics computers.
Title: Re: A more difficult algorithm: Line of sight and ballistic range.
Post by: Calin Leafshade on Sat 28/05/2011 15:25:36
The purpose of the function is to determine which squares an archer at position P with a shooting radius R can shoot at taking into account the heights of the various tiles which may get in the way.

Each tile on the map has a height value where 0 is ground level, 1 is 1 step up and 2 is 2 steps up. Any tile which cannot be shot through from any height (the pillars for instance) has a height value of 100.

I assumed a perfect linear trajectory to make things simpler.

In the diagram given the red tiles show the algorithms current output, i.e. which tiles can be reached with an arrow shot by the circled character.
Title: Re: A more difficult algorithm: Line of sight and ballistic range.
Post by: Kweepa on Sat 28/05/2011 16:07:13
Oh, I see.
I presume the arrow goes from the center of your square to the center of the target square
You should
- run over the potentially hittable squares (loop over x and y, and check if the target is within radius)
- using a Ray Cast algorithm, check each square that is touched by the line between the centers - this is a little different to a Bresenham routine since you want to visit every touched square
- as you change square, determine the height of the arrow at that point using  h0 + (d/D)*(h1-h0) and make sure it's higher than the new square
- if you reach the target square, mark it as visible
Title: Re: A more difficult algorithm: Line of sight and ballistic range.
Post by: Khris on Sat 28/05/2011 17:27:11
A few thoughts:

I checked Final Fantasy Tactics on the GameboyAdvance too see how they handled bows and it seems that shooting from somewhere higher didn't affect the range but the accuracy and thus hit percentage.

To calculate whether a tile blocks the arrow's path you can neglect the top; only the walls are important.
(A path that would hit the top of the tile but no wall is much too steep to occur.)
To calculate whether a wall blocks the arrow's path you can use a simple line vs. line intersection (actually two).

The easiest way to go about this is to express both lines in the form: s + k*d where s is the starting point and d the directional vector.
If the player is standing on tile 2;3, shooting at tile 5;4, the arrow's line is (1.5;2.5) + k*(3;1).
The first potential blocking wall is the left wall of tile 3;3: (2;2) + l*(0;1)

Here's line vs. line:

void LineIntersectsLine(float x1, float y1, float xs1, float ys1, float x2, float y2, float xs2, float ys2) {
 
  float denom = ys1*xs2 - xs1*ys2;

  if (denom == 0.0) {
    results = 0;
    return;
  }
 
  results = 1;
  resultf[0] = (ys2*(x1-x2) - xs2*(y1-y2))/denom; // factor for movement
  resultf[1] = (ys1*(x2-x1) - xs1*(y2-y1))/(-denom); // line factor
}


If the resulting k and l are both inside [0;1] then the lines intersect.
If the calculation does reveal a collision, check the wall height. Since we know the fraction of arrow path to the wall (k), we can simply insert this into a sideview equasion. The wall is vertical (in terms of x and y of the top view), so now it's (1.5;h) + k*(3;dh). k is known so collison_h = h + k*dh (Kweepa's equasion). All that's left to do is check collision_h against tile_height.
Title: Re: A more difficult algorithm: Line of sight and ballistic range.
Post by: Kweepa on Sat 28/05/2011 18:27:29
Here's an explanation of Ray Casting:
http://www.permadi.com/tutorial/raycast/rayc7.html

[EDIT] And here's an implementation (now tested and fixed!) :=


// new module script

struct Tile
{
 int Height;
 int Visible;
};

Tile tiles[1024];

function raycast(int sx, int sy, int sh, int tx, int ty, int th)
{
 int x = sx;
 int y = sy;

 int dx = tx - sx;
 int dy = ty - sy;

 float cdx = 10000.0;
 float cdy = 10000.0;
 if (dy != 0)
 {
   // change in x for a y step of 1
   cdx = IntToFloat(dx)/IntToFloat(dy);
   if (cdx < 0.0) cdx = -cdx;
 }
 if (dx != 0)
 {
   // change in y for an x step of 1
   cdy = IntToFloat(dy)/IntToFloat(dx);
   if (cdy < 0.0) cdy = -cdy;
 }
 
 float D = Maths.Sqrt(IntToFloat(dx*dx + dy*dy));

 // distance travelled
 float fx = 0.0;
 float fy = 0.0;

 // initial steps to get from the center of the tile to lines
 float hdx = 0.5*cdx;
 float hdy = 0.5;
 float vdx = 0.5;
 float vdy = 0.5*cdy;
 
 int xstep = 1, ystep = 1;
 if (dx < 0) xstep = -1;
 if (dy < 0) ystep = -1;

 bool done = false;
 while (!done)
 {
   // decide whether to use the h(orizontal) or v(ertical) step
   if (hdx < vdx)
   {
     // hit a horizontal line
     fx += hdx;
     fy += hdy;
     
      vdx -= hdx;
      vdy -= hdy;
     
     hdx = cdx;
     hdy = 1.0;
     
     y += ystep;
   }
   else
   {
     // hit a vertical line
     fx += vdx;
     fy += vdy;
     
      hdx -= vdx;
      hdy -= vdy;
     
     vdx = 1.0;
     vdy = cdy;
     
     x += xstep;
   }
   // calculate the distance
   float d = Maths.Sqrt(fx*fx + fy*fy);
   float h = IntToFloat(sh) + (d/D)*IntToFloat(th - sh);
   if (FloatToInt(h) < tiles[32*y + x].Height)
   {
     done = true;
   }
   else if (x == tx && y == ty)
   {
     // we're done
     // mark the target square as visible
     tiles[32*y + x].Visible = true;
     done = true;
   }
 }
}


(http://www.kweepa.com/step/ags/tech/RayCast.png)
Title: Re: A more difficult algorithm: Line of sight and ballistic range.
Post by: Calin Leafshade on Sun 29/05/2011 16:08:13
Ok so the raycast function works on a single tile (t) and determines if its visible from the source tile (s)?

so


   if (FloatToInt(h) < tiles[32*y + x].Height)
   {
     done = true; // This branch is if tile t is *NOT* visible?
   }
   else if (x == tx && y == ty)
   {
     // we're done
     // mark the target square as visible
     tiles[32*y + x].Visible = true; // and this is if tile t *IS* visible?
     done = true;
   }


correct?

And i can just replace those branches with return true or return false?
Title: Re: A more difficult algorithm: Line of sight and ballistic range.
Post by: Kweepa on Sun 29/05/2011 16:15:02
That's right.
Title: Re: A more difficult algorithm: Line of sight and ballistic range.
Post by: Calin Leafshade on Sun 29/05/2011 16:17:01
Divide By Zero at:


float h = IntToFloat(sh) + (d/D)*IntToFloat(th - sh);


Edit: Nevermind, Tried to calc the value for the shooters tile thus dy && dx = 0

Edit Again:

Ok well that seems to work but I think I might have to add some kind of fudge factor since It calcs the LOS essentially from the shooters feet I think. So for instance:

(http://i.imgur.com/PU3L8.png)

The circled shooter should really be able to hit the crossed square.
Title: Re: A more difficult algorithm: Line of sight and ballistic range.
Post by: Kweepa on Sun 29/05/2011 16:32:05
That's why I have you pass in sh and th. :=
Title: Re: A more difficult algorithm: Line of sight and ballistic range.
Post by: Calin Leafshade on Sun 29/05/2011 16:35:50
Yus, I just added 1 to the source height to simulate arm height.. Works nicely now. Thanks kweeeps