Game Development Community

Layer based traversability for Torque3D terrains

by Konrad Kiss · 06/03/2009 (10:12 am) · 32 comments

My second resource regarding Torque3D terrains lets you set up terrain layers that can not be walked upon by your player (and/or your vehicles). This provides a very easy way to paint traversable terrain for your game.

I'm using it to restrict movement onto cliffs and riverbeds / ocean floors and other places where the player shouldn't wander off to. Use responsibly. ;)

In the end, your already cluttered terrain material editor will look like this: (I've marked the new feature with a red box)


www.xenocell.com/dev/pltcheckbox.jpg

Ok, let's get to work.

terrain/terrData.h
add before the last #endif
This is needed to be able to use this global function outside terrData
TerrainBlock* getTerrainUnderWorldPoint(const Point3F & wPos); // >>> <<< Per-layer traversability

terrain/terrMaterial.h
add as a protected member
bool mTraversable; // >>> <<< Per-layer traversability

add as a public member function
bool isTraversable() const { return mTraversable; } // >>> <<< Per-layer traversability

terrain/terrMaterial.cpp
change the class constructor
TerrainMaterial::TerrainMaterial()
   :  mSideProjection( false ),
      mDetailScale( 32.0f ),
      mDetailStrength( 1.0f ),
      mDetailDistance( 50.0f ),
		mTraversable( true ), // >>> <<< Per-layer traversability
      mParallaxScale( 0.0f )
{
}

add in initPersistFields, after the other fields
addField( "traversable", TypeBool, Offset( mTraversable, TerrainMaterial ) ); // >>> <<< Per-layer traversability

T3D/shapeBase.h
add as a protected member to the ShapeBase class
// >>> Per-layer traversability
	Point3F mLastTraversablePosition;
	// <<< Per-layer traversability

add as public members functions to the ShapeBase class
// >>> Per-layer traversability
	bool ShapeBase::isOnTraversableTerrain();
	Point3F getLastTraversablePosition() { return mLastTraversablePosition; }
	// <<< Per-layer traversability

T3D/shapeBase.cpp
add after other includes
// >>> Per-layer traversability
#include "terrain/terrData.h"
#include "terrain/terrMaterial.h"
// <<< Per-layer traversability

add at the end of the ShapeBase constructor
// >>> Per-layer traversability
	mLastTraversablePosition = Point3F(0,0,0);
	// <<< Per-layer traversability

add at the end of the file
I decided to add helper functions to ShapeBase, but do the actual checks in derived classes where needed...
// >>> Per-layer traversability
bool ShapeBase::isOnTraversableTerrain() {
	if (mLastTraversablePosition != getRenderPosition()) {
		// check terrain type below and see if it can be traversed
		// get the terrain block beneath
		TerrainBlock * terr = getTerrainUnderWorldPoint(getPosition());

		if (terr) {
			// now let's find our grid position
			Point3F pos = getRenderPosition();
			Point2I gridPos = terr->getGridPos(pos);

			// and get the material's properties at that grid position

			// Beta 5 - matIndex: (use this if you have a version of Torque 3D before Beta 5
			//U8 matIndex = terr->getMaterialIndex(gridPos.x, gridPos.y);
			// Beta 5 + matIndex:
			U8 matIndex = terr->getFile()->getLayerIndex(gridPos.x, gridPos.y);

			TerrainMaterial * terrMat = terr->getMaterial(matIndex);

			if (terrMat) {
				// we now can check if this material is traversable or not
				if (!terrMat->isTraversable()) {
					// this is not traversable
					return false;
				} else {
					mLastTraversablePosition = getRenderPosition();
				}
			}
		}
	}
	return true;
}
// <<< Per-layer traversability

T3D/player.cpp - and any other class where you need per layer traversability (ie. wheeledvehicle?)
In Player::processTick after
// If we're not being controlled by a client, let the
   // AI sub-module get a chance at producing a move.
   Move aiMove;
   if (!move && isServerObject() && getAIMove(&aiMove))
      move = &aiMove;
add the following:
// >>> Per-layer traversability
	if (isServerObject() && !isOnTraversableTerrain()) {
		Move tMove;
		tMove = NullMove;
		tMove.x = 0;
		tMove.y = 0;
		tMove.z = 0;
		Point3F cPos = getPosition();
		Point3F tPos = getLastTraversablePosition();
		Point3F iPos = tPos - cPos;
		iPos.normalize();
		setPosition(cPos+(iPos*0.1f), getRotation());
		if (move) {
			tMove.yaw = move->yaw;
			tMove.pitch = move->pitch;
			tMove.roll = move->roll;
			tMove.freeLook = move->freeLook;
			for (U32 i=0; i<MaxTriggerKeys; i++) {
				tMove.trigger[i] = move->trigger[i];
			}
			move = &tMove;
		}
		// let's stop here
		mVelocity.x = 0;
		mVelocity.y = 0;
	}
	// <<< Per-layer traversability

That's it for the engine changes, now we need to modify tools scripts to be able to use this within the terrain material editor.

tools/missionEditor/gui/guiTerrainMaterialDlg.ed.gui
overwrite the code for the control with the internalName attribute "sideProjectionCtrl" with the following
// >>> Per-layer traversability
            new GuiCheckBoxCtrl() {
               internalName = "sideProjectionCtrl";
               canSaveDynamicFields = "0";
               isContainer = "0";
               Profile = "GuiCheckBoxProfile";
               HorizSizing = "right";
               VertSizing = "bottom";
               Position = "55 35";
               Extent = "54 16";
               MinExtent = "8 2";
               canSave = "1";
               Visible = "1";
               tooltipprofile = "GuiToolTipProfile";
               hovertime = "1000";
               alpha = "1";
               color = "255 255 255 255";
               unitClip = "0 0 1 1";
               text = "Side Prj";
               groupNum = "-1";
               buttonType = "ToggleButton";
               useMouseEvents = "0";
               useInactiveState = "0";
            };
            new GuiCheckBoxCtrl() {
               internalName = "traversableCtrl";
               canSaveDynamicFields = "0";
               isContainer = "0";
               Profile = "GuiCheckBoxProfile";
               HorizSizing = "right";
               VertSizing = "bottom";
               Position = "116 35";
               Extent = "59 16";
               MinExtent = "8 2";
               canSave = "1";
               Visible = "1";
               tooltipprofile = "GuiToolTipProfile";
               hovertime = "1000";
               alpha = "1";
               color = "255 255 255 255";
               unitClip = "0 0 1 1";
               text = "Passable";
               groupNum = "-1";
               buttonType = "ToggleButton";
               useMouseEvents = "0";
               useInactiveState = "0";
            };
            // <<< Per-layer traversability

tools/missionEditor/scripts/interfaces/terrainMaterialDlg.ed.cs
a) after
%this-->sideProjectionCtrl.setValue( %mat.useSideProjection );
add
%this-->traversableCtrl.setValue( %mat.traversable ); // >>> <<< Per-layer traversability

b) after
%useSideProjection = %this-->sideProjectionCtrl.getValue();
add
%traversable = %this-->traversableCtrl.getValue(); // >>> <<< Per-layer traversability

c) after
%mat.useSideProjection != %useSideProjection ||
add
%mat.traversable != %traversable || // >>> <<< Per-layer traversability

d) finally, after
%mat.useSideProjection = %useSideProjection;
add
%mat.traversable = %traversable; // >>> <<< Per-layer traversability

This is it. If your player doesn't move right away, make sure you set the layer below your player to passable. You need to save your mission in order to save the changes you make to your terrain layers, so don't forget that!

Enjoy!


Update: The Player::processTick code was made more stable for AI.

Update (07/30/2009):

For this to work in a networked environment, make sure the server loads the terrain materials - it needs to know about them to decide if a terrain material is traversable or not.

Include this in, say, scripts/server/init.cs in initServer(), at the end of the function:
exec("art/terrains/materials.cs");

Page«First 1 2 Next»
#21
06/04/2009 (10:26 pm)
I haven't seen Caylo around much nowadays. I'm now missing his original sense of humor. :)

I just did an update on the resource, added a bit more stability for AIPlayers. Only the code that goes into Player::processTick was affected by the change.
#22
06/05/2009 (9:25 am)
ok what happened to the rating of resources i need triple 5 star rating for this one. O_Ov
#23
06/05/2009 (4:24 pm)
Looking to back port this to TGE1.5.2

Since 1.5.2 doesn't have terrMaterial.h/.cc does anyone know a corresponding file?

Thanks!

Tony
#24
06/05/2009 (4:32 pm)
@Kevin: Thanks, really nice of you to say that! I'm glad this proved to be so useful to everyone.

@Tony: I'm afraid that all you will be able to use is the idea and the code that takes care of stopping the player. You will need to find all terrain materials under your character and decide which one is visible the most (taking alpha and layer priority into account). It's going to be a bit harder to do there, still, possible.
#25
07/30/2009 (11:21 am)
Update:

For this to work in a networked environment, make sure the server loads the terrain materials - it needs to know about them to decide if a terrain material is traversable or not.

Include this in, say, scripts/server/init.cs in initServer(), at the end of the function:
exec("art/terrains/materials.cs");


The resource has been updated.
#26
09/16/2009 (11:04 am)
Comment moved to a separate thread.
#27
09/17/2009 (11:32 am)
@Jinnie: Let me include your original problem here:

Quote:
I've got a problem getting it to work in my project though. From the file locations, I'm guessing this is based on an earlier beta of T3D. In beta 5 (which I'm currently using), trying to compile the changes gives me two errors:

1) In terrmaterial.cpp
'TerrainMaterial' : illegal member initialization: 'mDetailScale' is not a base or member.
A hack would be to just declare it, but I'm not too sure if that'll have repercussions down the line.

2) In shapebase.cpp
'getMaterialIndex' : is not a member of 'TerrainBlock'

WaterBlock and some other terrain features seem to have their own implementation of getMaterialIndex; only TerrainBlock seems to be missing it.

Here come some answers:
1) You probably added everything in that code block, while you only have to add the variable marked with // >>> // <<<. The rest are there only for you to know where to insert the declaration. Beta 5 might have a different constructor, but the point is that you need to initialize that mTraversable there.

2) Change the problem line in ShapeBase.cpp to read:
U8 matIndex = terr->getFile()->getLayerIndex(gridPos.x, gridPos.y);

I've updated the resource to reflect this change. Thanks for bringing it to my attention!
#28
09/18/2009 (6:37 am)
Thanks for the reply! I've managed to get it to compile without any errors, and the passable checkbox appears in my terrain painter GUI (though overlapping a "Size" textbox), but it doesn't seem to affect the traversability of the terrain.

Which version are you using btw? Perhaps it could be a versioning issue here since I've checked and double checked my code to make sure it adds in whatever code you have at the top.
#29
09/18/2009 (11:49 am)
You're welcome. I'm using Beta 5 right now.

Check your console log for any warnings and error messages please. Also, make sure you add the code presented in the resource to player.cpp. Are you using this with a dedicated server? Then you'll need to check out reply #25 in this thread.
#30
09/18/2009 (12:58 pm)
Strange, that's a similar version to what I'm using.

From what I see, there's no errors pertaining to the resource. It's not used on a dedicated server, however, I added that bit of code anyway, just in case.
#31
09/18/2009 (1:12 pm)
Make sure that you save your mission after you edit the terrain layers to be (or not to be) traversable. That will overwrite the terrain material definitions. If you don't have "traversable" in your art/terrain/materials.cs then you've probably not yet saved your mission after applying the resource.
#32
09/18/2009 (1:47 pm)
I've got Traversable = "0"; for materials that are not supposed to be passable, so it seems like it's registering in that aspect. I could send you my console.log file in the event you wish to peruse it though I haven't had any luck seeing any errors related to the resource.
Page«First 1 2 Next»