Game Development Community

Terrain Following Projectiles Trouble

by Chris Haigler · in Torque Game Engine · 05/31/2006 (2:25 pm) · 4 replies

I'm having some trouble with the 'Terrain Following Missiles' resource (found here: www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=6899).

Everything works correctly offline or when I host my own (non-dedicated) server but in networked games the projectile no longer follows the terrain. I did some quick debugging and found the code uses getTerrainHeight() which seems to be serverside-only. On the server, the terrain heights are reported accurately but the client ends up returning early as the Terrain object isn't found on the client sim. Essentially this is causing the client simulation to think the terrain height is always 0.0 which means (atleast on the clientside) that the projectiles are always above the terrain. This isn't the case, of course, as the terrain is filled with hills so you get projectiles that pass through sections of terrain rather than smoothly follow over them.

Has anyone managed to get this resource working for networked games? I thought about doing a simple raycast every tick to insure the projectile is always a certain number of units away from the terrain but there may be a better way (or even a simple change to the original resource) that produces better results.

Thanks for any help!

#1
05/31/2006 (3:09 pm)
Hmmm... would perhaps if you changed the check for the terrain to be

if(mDataBlock->isTerrainFollowing && isServerObject())

It might only check this on the server, and that holding authority would make it update right? Or perhaps try removing the isServerObject check from the getTerrainHeight function, because the terrain certainly _does_ exist on the client, it's just only checking it on the server... I'm not sure if doing either of those things would cause problems, but you could try...

Ultimately if for whatever reason you can only get the terrain height off the server, you could hold the offset value in a variable on the projectile and update that over the network to be passed into the clientside sim I suppose.
#2
05/31/2006 (8:37 pm)
Thanks for the reply.

I tried both of your suggestions without any luck, unfortunately.

Adding an isServerObject() to if(mDataBlock->isTerrainFollowing) results in the projectile behaving like a typical, non-terrain following projectile on the clientside (projectile fires straight and collides with terrain). I'm guessing it behaves correctly on the serverside which would make sense.

Here is the getTerrainHeight() function:

F32 getTerrainHeight(Point2F position)
{
   F32 height = 0.0;
   TerrainBlock *terrain = dynamic_cast<TerrainBlock*>(Sim::findObject("Terrain"));

   if(terrain)
   {
	  if(terrain->isServerObject())
	  {
		 Point3F offset;
		 terrain->getTransform().getColumn(3, &offset);

		 position -= Point2F(offset.x, offset.y);
		 terrain->getHeight(position, &height);
	  }
   }

   return height;
}

I put a few Con::errorf()'s just after the if(terrain) check and just after the if(terrain->isServerObject()) check to see how the client and server handled the function. On the server, everything happens as expected. The terrain height returns as a valid, useful number. On the client, however, I found it never made it to the if(terrain->isServerObject()) check because if(terrain) returned false. This is what originally lead me to post the Terrain object doesn't "exist" on the clientside sim.

Holding the offset in a variable might be something to explore.
#3
06/01/2006 (1:09 pm)
Sim::findObject("Terrain") will fail on the client, not because terrain doesn't exist on the client, but because the terrain object is not called "Terrain". The name is lost when the terrain object is ghosted to the client.

But no matter, because there are plenty of other ways to find the terrain object. Here's one idea that I think should work (although I haven't tested it):

TerrainBlock* terrain = NULL;

for (SimSetIterator obj(Sim::getRootGroup()); *obj; ++obj) {
	if ((*obj)->getType() & TerrainObjectType) {
		terrain = static_cast<TerrainBlock*>(*obj);
		// This is assuming there is only one TerrainBlock.
		break;
	}
}

if (terrain) {
	// etc...
}

Note the static_cast, which is much faster than a dynamic_cast, but is only safe if TerrainBlock is the only class which uses the TerrainObjectType. In TGE, I'm pretty sure that is the case. I don't know about TSE and the new Atlas terrain though. I would use the static_cast in TGE, but if you're concerned then simply substitute dynamic_cast.
#4
06/01/2006 (8:47 pm)
Very nice work, Scott. That did the trick. Just in time, too, as I wasn't any closer to solving the problem.

I'll be sure to post a link to this post in the 'Terrain Following Missiles' resource for anyone who might come up against the same issue as I did.

Thanks again!