Game Development Community

Ray casting for tiles

by Andy Hawkins · in Torque Game Builder · 12/06/2008 (8:25 am) · 6 replies

I'm using this function below to ray cast from a player to a marker to see if there is a wall in the way. It appears to be working but what is the collision data I'm getting back from getTileCollision???

Collision tile info: 1 1.000000 1.000000 4 -1.000000 -1.000000 1.000000 -1.000000 1.000000 1.000000 -1.000000 1.000000

Here's the function
function rayCastForTiles(%x1, %y1, %x2, %y2, %rez)
{
   // move along line at resolultion (rez) looking for tiles
   %vector1 = %x1 SPC %y1;
   %vector2 = %x2 SPC %y2;
   
   %distX = mAbs(%x1 - %x2);
   %distY = mAbs(%y1 - %y2);

   %xPos = %x1; 
   %yPos = %y1;
   %xMov = 0;
   %yMov = 0;

   if (%x1 < %x2) %xMov = 1;
   if (%x1 > %x2) %xMov = -1;
   if (%y1 < %y2) %yMov = 1;
   if (%y1 > %y2) %yMov = -1;
  
   %stepSizeX = %distX * %rez;
   %stepSizeY = %distY * %rez;
   
   %granularX = mFloor( %distX / %stepSizeX );
   %granularY = mFloor( %distY / %stepSizeY );
  
   %intX = %x1;
   %intY = %y1;
   
   for ( %cnt = 0; %cnt < %granularX ; %cnt ++)
   {
      if (%xpos >0)
         %intX += %stepSizeX * %xMov;
               
      if (%ypos >0)   
         %intY += %stepSizeY * %yMov;
               
      %col = my_collisionTileLayer.getTileCollision( mFloor(%intX), mFloor(%intY) );
            
      %string = "Collision tile info: " @ %col;
      echo(%string);
   }   
}

#1
12/06/2008 (10:17 am)
Andy,

Not sure if you saw it or but I replied to your post not that long ago which I assume is related to this problem?

www.garagegames.com/mg/forums/result.thread.php?qt=81345

The "getTileCollision(...)" is simply a 'getter' to retrieve the collision setup of a tile.

The values retrieved are:

#0 - Collision Active (bool)
#1 - Collision Scale X (float)
#2 - Collision Scale Y (float)
#3 - Polygon Vertex Count (int)
(#4 ,#5) Polygon Vertex #0 (X,Y)
(#6, #7) Polygon Vertex #1 (X,Y)
(#.., #..) Polygon Vertex #n (X,Y)

etc...


Hope this helps,

Melv.
#2
12/06/2008 (10:20 am)
You can find more information on the tile-layer here:

TDN t2dTileLayer

Melv.
#3
12/06/2008 (2:38 pm)
Yes I just read both sets of your replies just now. Thanks Melv it all makes sense.

I think my rayCastForTiles method listed above is something that should be put into the source. I will have a go at modifying the pickLine as well (which I talk about in that thread).

As for this one, your description lets me finish solve the problem. So now on I can cast a ray from where the npc is to where they want to go, and if my line of code ...
%col = my_collisionTileLayer.getTileCollision( mFloor(%intX), mFloor(%intY) );
... sets getWord(%col,0) to 1, I know there was a collision with a collidable tile.

Presumably in the other thread I can now rayCastForTiles() which will return true or false, and then I can do a pickLine to check the collisin order.

I presume the pickLine would return the id of the tileLayer in the list and then I can iterate through the returned list and see if the tileLayer was infront of the point I was looking at.
#4
12/07/2008 (5:32 am)
Just to be clear Andy; you only collide with a tile-layer when you collide with one of its collidable tiles. You don't collide if there are no tiles to collide with.

"CastCollision(...)" or "CastCollisionList(...)" give you a method of sweeping an object against the world (without having to move it) and is much faster than the methods above.

Performing a ray-cast against a tile layer isn't that hard to do but you can do it without using a stepping algorithm as above e.g. by doing a sweep test. All of the code to achieve that is already written in the t2dTileLayer but there's just not a console-method to expose it.

I've added this as a feature request in our tracking system.

Melv.
#5
12/07/2008 (3:04 pm)
As mentioned in my other post I will check out the sweeping code and expose that, if required, because right now I only need to know if some wall was hit before the object I was looking for, essentially checking if the npc can "see" the player, or in the specific case of the breadCrumb object if the npc can see where the player had been.

However, the other thing your were talking about, the CastCollision and CastCollisionList, I assumed the object had to be set in motion to determine if after time it would collide with something. Is this not the case? I must have to provide a velocity vector of some sort right?

Here's my code anyway ...

Outside method return the closest visible breadCrumb if one exists. * Notice I've commented out the rayCastForTiles() call because I don't need that level of granularity right now.
// -----------------------------------------------------------------------------
function lookForClosestVisibleBreadCrumb(%who)
{
   %list_of_visible_breadCrumbs = "";
   %x1 = %who.getPositionX();
   %y1 = %who.getPositionY();
   %vector1 = %x1 SPC %y1;
   
   %graph = sceneWindow2D.getSceneGraph();
   %count = %graph.getSceneObjectCount();
   
   for ( %i=0; %i < %count; %i++ )
   {
      %obj = %graph.getSceneObject(%i);
      if (%obj.class $= "breadCrumb")
      {
         echo("Checking ray cast to breadCrumb...");
         %x2 = %obj.getPositionX();
         %y2 = %obj.getPositionY();
         %vector2 = %x2 SPC %y2;
         
         //if (rayCastForTiles(%x1, %y1, %x2, %y2, Masonik_collisionTileLayer.getId(), $RAY_CAST_REZ))
         //{
            %hitObj1First = checkCollisionOrder(%vector1, %vector2, %obj, 
                                               Masonik_collisionTileLayer.getId(),
                                               %graph); 
            if (%hitObj1First == true)
            {
               // path is clear to object - add to list of visible breadCrumbs
               if (getWordCount(%list_of_visible_breadCrumbs) > 0)
               {
                  %list_of_visible_breadCrumbs = %list_of_visible_breadCrumbs @ " " @ %obj;
               }
               else
               {
                  %list_of_visible_breadCrumbs = %obj;
               }
            }
         //}
      }      
   }
   echo("List of visible breadCrumbs: " @ %list_of_visible_breadCrumbs);
   
   if (getWordCount(%list_of_visible_breadCrumbs) == 0) return -1;
   
   // finally pick the closest one
   %closestObj = 0;
   %rangeCheck = 9999999999;
   for (%i = 0; %i < getWordCount(%list_of_visible_breadCrumbs); %i++)
   {
      %obj = getWord(%list_of_visible_breadCrumbs,%i);
      %x3 = %obj.getPositionX();
      %y3 = %obj.getPositionY();
      %vector2 = %x3 SPC %y3;
      
      %dist = VectorDist(%vector1, %vector2);
      
      if (%dist <  %rangeCheck)
      {
         %closestObj = %obj;
         %rangeCheck = %dist;
      }
   }
   return %closestObj;
}

Here's the checkCollisionOrder() method used above to determine if the breadCrumb was hit first or the breadCrumb..
// -----------------------------------------------------------------------------
function checkCollisionOrder(%vector1, %vector2, %obj1, %obj2, %graph)
{
   echo("checkCollisionOrder....");
   
   // do a pickline and see where %obj1 and %obj2 turned up
   %list_of_visible_objects = %graph.pickLine( %vector1, %vector2, 
                                               bit(1)|bit(3),bit(0), 
                                               true, 
                                               $player );
     
   // iterate through all id's found                               
   %c = GetWordCount( %list_of_visible_objects );
   if (%c > 0)
   {
      %foundObj1 = false;
      %foundObj2 = false;
      %collidedWithObj1First = false;
      %collidedWithObj2First = false;
      
      %i = 0;
      %finished = false;
      
      // now looking to see if the breadCrumb was infront of the wall or vice versa
      while( %i < %c && %finished == false )
      {
          %object = GetWord( %list_of_visible_objects, %i );
          
          // if this object is a the second object and we haven't
          // seen the first object...
          if (%object == %obj1) %foundObj1 = true;
          if (%object == %obj2) %foundObj2 = true;
          
          if (%object == %obj2 && %foundObj1 == false)
          {
             %collidedWithObj2First = true;
             %finished = true;
          }
          else
          {
             // if this object is a the first object and we haven't
             // seen the second object...
             if (%object == %obj1 && %foundObj2 == false)
             {
                %collidedWithObj1First = true;
                %finished = true;
             }
          }
          %i++;
      }
      
      // result?
      if (%collidedWithObj1First == true) return true;  // path is clear
      if (%collidedWithObj2First == true) return false;  // path is blocked
   }
   
   // presumption is test failed
   return false;
}

And finally the rayCastForTiles() method (which I don't use but can detect individual tiles)
// -----------------------------------------------------------------------------
function rayCastForTiles(%x1, %y1, %x2, %y2, %layer, %rez)
{
   echo("rayCastForTiles...");
   
   // move along line at resolultion (rez) looking for tiles
   %vector1 = %x1 SPC %y1;
   %vector2 = %x2 SPC %y2;
   
   %distX = mAbs(%x1 - %x2);
   %distY = mAbs(%y1 - %y2);
   %xPos = %x1; 
   %yPos = %y1;
   %xMov = 0;
   %yMov = 0;
   
   if (%x1 < %x2) %xMov = 1;
   if (%x1 > %x2) %xMov = -1;
   if (%y1 < %y2) %yMov = 1;
   if (%y1 > %y2) %yMov = -1;
   
   %stepSizeX = %distX * %rez;
   %stepSizeY = %distY * %rez;
   %granularX = mFloor( %distX / %stepSizeX );
   %granularY = mFloor( %distY / %stepSizeY );
   
   %intX = %x1;
   %intY = %y1;
   %foundOne = false;
   
   for ( %cnt = 0; %cnt < %granularX ; %cnt ++)
   {
      if (%xpos >0)
         %intX += %stepSizeX * %xMov;
               
      if (%ypos >0)   
         %intY += %stepSizeY * %yMov;
               
      %col = %layer.getTileCollision( mFloor(%intX), mFloor(%intY) );
      
      if (getWordCount(%col) != 0) %foundOne = true;
   }
   
   return %foundOne;
   
}
#6
12/08/2008 (1:53 am)
The "CastCollisionxxx(...)" calls were originally intended to check to see if an object will collide with anything over a specified period of time using its current velocity so it makes the call easy to use e.g. you only have to specify the time period.

You can obviously use this with an arbitrary velocity/position by setting those values prior to the call and restoring them afterwards should it be important. You could also use a dedicated "collision" object to do the checking if you wish.

For what you require, it would seem that you would want to use "castCollisionList()". The objects are returned in a collision order. You could go through each object in turn and determine if you hit anything before your "target" object. Of course with castCollision, it uses the objects collision-bound (polygon and/or circle) and not a ray-cast so for it's sweep test and it's very fast.

Melv.