Game Development Community

Building placement validation

by Justin Mosiman · in RTS Starter Kit · 12/30/2006 (10:16 pm) · 22 replies

I'm trying to improve the RTSBuilding placement system, and I am just totally out of ideas on this topic. If the RTSBuilding (of class RTSBuildingMarker with a parent of TSStatic) hits another object or is on uneven terrain, how do you check that?

I don't really want to wait until serverCmdPlaceBuilding in building.cs like the comments suggest because that would mean that I wouldn't be able to put a red overlay over the RTSBuildingMarker indicating that this is a bad area.

Does anybody have any ideas on the structure of implementing this? I don't need the code, I would be able to figure that part out, I'm just out of ideas on how to even approach this.

Any feedback would be greatly appreciated (I can make a resource once I get this working also).
Page «Previous 1 2
#1
12/30/2006 (10:51 pm)
Hmm, a little while ago I found something that I think might help you. I forget where it was exactly, but it involved Raycast on the terrain..

I'm looking for it in exact detail now, it's somewhere in the terrain code..

For now I'll say what I remember about it, It involved casting a ray from straight above the terrain downwards, using the castRay function, call that ray a variable, let's say Ray = CastRay( etc )

Then do Ray.slope, that should equal the slope of the portion of terrain the ray hit, bear in mind this might not be right. I'm in the process of looking through the code to find exactly what I saw, but it was something along those lines

Hopefully I'll find something, I'll get back to you when I do
#2
12/30/2006 (10:54 pm)
Aha, I found it it was in the foliage code, the stuff that says if foliage is allowed on a certain slope:

game/fx/FoliageReplicator.cc

line 695
// Invalidate if we are below Allowed Terrain Angle.
if (RayEvent.normal.z < mSin(mDegToRad(90.0f-mFieldData.mAllowedTerrainSlope))) CollisionResult = false;

On line 668 it has
CollisionResult = gClientContainer.castRay( FoliageStart, FoliageEnd, FXFOLIAGEREPLICATOR_COLLISION_MASK, &RayEvent);

The last argument there, &RayEvent, I suppose is the name of that Ray Instance, which is why on line 695 it's RayEvent.normal.z, not CollisionResult.normal.z,

This is a newb speaking, take with (almost) a grain of salt. I have almost no clue what Im talking about
#3
01/01/2007 (6:19 pm)
Thanks, that helped a ton. I guess I'll just do a ray cast for detecting overlapping objects since an object of class TSStatic doesn't have the collision detection like other classes.

I'll put up a resource when I get it all working nicely.

Edit: The more I think about it, it would be so much easier if there was some method within TSStatic that is called when it collides with something, but I couldn't find anything. Did I overlook it?
#4
02/22/2007 (3:01 pm)
Sorry for the late response. I added a script function mousemovewhilebuilding and call it from the mouse move if mBuilding.
#5
04/28/2007 (6:20 pm)
I know that it's been awhile since I last said that I would post a resource on how to get building placement working, and this post is finally it. It has been awhile since I played around with the building placement code, so I could have easily left something out. If something doesn't work let me know and I'll take a look at it.

First, for a diagram on how it works:
img205.imageshack.us/img205/5350/rtsbuildingprocessyb3.jpg

I'll start with the Torquescript code and then move onto engine changes. Text in bold and italics means that you need to change it to a value that corresponds with your game.

First, add the following to the end of the file ~/client/playGui.cs:
//Building placement
function startBuildingPlacement(%this, %building)
{
   // Building is already being placed
   if( isObject( $NewBuilding ) )
      return;
      
   $NewBuilding = new RTSBuildingMarker()
   {
      scale = "1 1 1";
      thedatablock = "[b][i]testBuilding[/i][/b]";
      shapeName = "[b][i]~/data/shapes/block/torque_logo.dts[/i][/b]";
   };
   PlayGui.continueBuildingPlacement($NewBuilding);
}

function GuiRTSTSCtrl::placeBuilding(%this)
{
   commandToServer('PlaceBuilding', $NewBuilding.getTransform(), $NewBuilding.thedatablock);
   $NewBuilding.delete();
}

Then add the following to ~/client/ui/playGui.gui:
new GuiBitmapButtonCtrl() {
      canSaveDynamicFields = "0";
      Profile = "GuiDefaultProfile";
      HorizSizing = "relative";
      VertSizing = "relative";
      position = "575 375";
      Extent = "65 105";
      MinExtent = "8 2";
      canSave = "1";
      Visible = "1";
      Command = "startBuildingPlacement(%this, \"[b][i]testBuilding[/i][/b]\");";
      hovertime = "1000";
      groupNum = "-1";
      buttonType = "PushButton";
      bitmap = "[b][i]./buttons/placeBuilding[/i][/b]";
   };

That was easy enough for the Torquescript changes, now for the engine changes.

Open the file SDK/engine/game/rts/guiRTSTSCtrl.h. I have completely redone how guiRTSTSCtrl works, so I won't be able to give specifics about where to put everything, but you should get the general idea.

I added the following private variables for the GuiRTSTSCtrl class:
bool              mPlacingBuilding; ///< Are we placing a building?
   RTSBuildingMarker *mNewBuilding; ///< The building marker that we are placing.

I'm pretty sure RTSBuildingMarker was included with the stock RTS kit under RTSBuilding.cc, but if it isn't, let me know and I'll post that class.

I then added the following public methods within GuiRTSTSCtrl:
/**
    * Start creation of a building engine side.  This will make a building marker
    * attatch to the cursor so it can be place properly.
	 * @param building the building that we are placing
    */
   void continueBuildingPlacement(RTSBuildingMarker *building);
   /**
    * Preform a series of checks to make sure the building marker is in a valid position.
	 * The checks include a terrain slope check as well as an object collision check.
	 */
   bool validateBuildingPlacement();
   /**
    * The button has been pressed to place the building and it has passed the checks,
	 * so lets place the building.
	 */
   void placeBuilding();

continued to next post because of length
#6
04/28/2007 (6:21 pm)
Now we are done with the header file, so open GuiRTSTSCtrl.cc.

I defined a variable at the top under DRAG_PIXEL TOLERANCE, which says:
// How many units outside of the bounding box do we want to expand the ray cast?
// For example, view the diagram below (B = Building, R = Ray Cast)
//
//       RRRRRRRRR
//       R       R
//       R |---| R
//       R | B | R
//       R |---| R
//       R       R
//       RRRRRRRRR 
//
// In this example, the expansion would be set to 2 since the ray cast expands two units on the bounding box.
#define BUILDING_RAYCAST_EXPANSION 0.5

Hopefully the ASCII art will work...

In the GUIRTSTSCtrl constructor, add the following:
mPlacingBuilding = false;
   mNewBuilding = NULL;

I then added the following at the end of GUIRTSTSCtrl::on3DMouseMove:
if (mPlacingBuilding)
      ((ShapeBase*)mNewBuilding)->setPosition(info.pos);

I modified the if statement within GuiRTSTSCtrl::on3DMouseDown to say
if(collide(event, info) && !mPlacingBuilding){
      mHitInfo = info;

Then change GuiRTSTSCtrl::on3DMouseUp add the following at the end:
if(mPlacingBuilding && validateBuildingPlacement())
		placeBuilding();

Within GuiRTSTSCtrol::on3DMouseDragged I have:
// If we are placing a building and the mouse is dragged, we want to rotate the building.
   if(mPlacingBuilding){
      Point2I localPoint = globalToLocalCoord(event.mousePoint);
		Point2I relativePoint = mDragStart - localPoint;

		// Determines what direction to rotate the building
		F32 theta = ((atan2(float(relativePoint.y),-float(relativePoint.x)) - M_PI2) * -1);
		if(theta < 0)
			theta += M_2PI;

		MatrixF zRot;
		zRot.set(EulerF(0,0,theta));
		zRot.setColumn(3,mNewBuilding->getPosition());
		((ShapeBase*)mNewBuilding)->setTransform(zRot);
   }

The following is up to you to decide what to do. If the building placement is invalid, you want the user to be notified somehow. For example you could change the building red or something. Right now all I have is some debug text saying that it is invalid. Obviously, this will have to change. I will be using TGEA to do some shader effects though so that is why I don't have any render code here. All of this code goes within GuiRTSTSCtrl::onRender
// If we are positioning a building, we want to validate the building position
   if(mPlacingBuilding && !validateBuildingPlacement()){
		//Building placement is invalid, I will wait for TGEA to do the actual render changes
		Con::executef(this, 2, "setDebugStatus", "Building Placement Faild");
   }

Now the following is easier to add. I added the following under GuiRTSTSCtrl::drawDamage.

void GuiRTSTSCtrl::continueBuildingPlacement(RTSBuildingMarker *building)
{
   //can only build one building at a time...
   if (mPlacingBuilding)
      return;

   mPlacingBuilding = true;
   mNewBuilding = building;
}

bool GuiRTSTSCtrl::validateBuildingPlacement()
{
   // Set the start and stop position for the ray cast
	Point3F start, end;
	start = end = mNewBuilding->getPosition();

	// We don't need to cast far because the object will already be on the ground
   start.z += 10;
	end.z -= 10;
	RayInfo rInfo; // Information about the object that we collided with
	
	// Cast the ray
	gClientContainer.castRay(start, end, TerrainObjectType, &rInfo);
	
	// If the normal equals one, then the terrain is flat.. So return false to say that this point is invalid.
	if(rInfo.normal.z != 1.f){
		return false;
	}

	// Now we know that the terrain is flat, lets make sure we don't collide with any objects.
	
	// TSStatic objects are client side only, it don't have the same collision detection that other objects
	// do (collision calculations are all done on the server).  Since we need collision detection when placing
	// the building, we need to add it ourselves.  

	// How it works: I am taking the four corners of the collision box and doing a ray cast from each point.
	// The results are four ray casts and it should cover all of the collision.  

	// topLeft: (min.x, max.y)
	// topRight: (max.x, max.y)
	// bottomLeft: (min.x, min.y)
	// bottomRight: (max.x, min.y)

	Point3F topLeft, topRight, bottomLeft, bottomRight;

	// Need to apply the expansion (see top of file for details)
	Point3F maxWorldBox = (mNewBuilding->getWorldBox().max + BUILDING_RAYCAST_EXPANSION);
	Point3F minWorldBox = (mNewBuilding->getWorldBox().min - BUILDING_RAYCAST_EXPANSION);

	// Objects will be at least 0.1f units tall
	F32 zPos = mNewBuilding->getPosition().z + 0.1f;

	// The z axis should be a straight line.  Every object will be at least 0.1f in height.
	topLeft = Point3F(minWorldBox.x, maxWorldBox.y, zPos);
	topRight = Point3F(maxWorldBox.x, maxWorldBox.y, zPos);
	bottomLeft = Point3F(minWorldBox.x, minWorldBox.y, zPos);
	bottomRight = Point3F(maxWorldBox.x, minWorldBox.y, zPos);

	// Now that we have that settled, we have to do the actual ray casts now to see if we collide with anything.
	bool collisionResult = false;
   collisionResult = gClientContainer.castRay(topLeft, topRight, mRayCastMask, &rInfo) ||
							gClientContainer.castRay(topRight, bottomRight, mRayCastMask, &rInfo) ||
							gClientContainer.castRay(bottomRight, bottomLeft, mRayCastMask, &rInfo) ||
							gClientContainer.castRay(bottomLeft, topLeft, mRayCastMask, &rInfo);

	if(collisionResult){
		// We have hit an object, so add the object to the list of collided objects.
		// And since we hit an object, we want mark this position as invalid.  Thus, return false.
		// addObject already checks to determine if the object is already in the set, so we don't need
		// to worry about multiple instances of the same object in the set.
		mCollision.addObject(rInfo.object);
		return false;
	}
	// We didn't hit an object, but that doesn't mean we are in the clear yet.
	// The building marker could be over an object and nothing would catch it, so we have to test four points
	// to make sure there isn't an object under us.
	// If there is an object under us, the position is invalid and we return false.
	// If there is an object in the collision list but isn't under us, that means we have moved away from
	// the object so we don't need to have it in our list anymore.
	for(int i = 0; i < mCollision.size(); i++){
		Point3F position = mCollision[i]->getPosition();
		if(minWorldBox.x < position.x && maxWorldBox.x > position.x &&
			minWorldBox.y < position.y &&	maxWorldBox.y > position.y)
			return false;
		else
			mCollision.removeObject(mCollision[i]);
	}
   return true;
}

void GuiRTSTSCtrl::placeBuilding()
{
   if (!mPlacingBuilding || !mNewBuilding)
      return;

   Con::executef(this, 1, "placeBuilding");

   mPlacingBuilding = false;
   mNewBuilding = NULL;
}

Now all we need to do is add the console method.
ConsoleMethod( GuiRTSTSCtrl, continueBuildingPlacement, void, 3, 3, "Starts the building placement engine side")
{
   SimObject* obj = Sim::findObject(dAtoi(argv[2]));

   if (RTSBuildingMarker *building = dynamic_cast<RTSBuildingMarker*>(Sim::findObject(dAtoi(argv[2]))))
      object->continueBuildingPlacement(building);
   else
      Con::errorf("Invalid object id for new building: %i", dAtoi(argv[2]));
}

Hopefully all of this code works. If you try it out and it doesn't work, let me know and I'll look at it. Once somebody posts that it works for them I'll make this into a tdn article.


Also, if anybody has any improvements, of course they are welcome.
#7
04/28/2007 (11:25 pm)
Thanks for the work Justin. Looks great.
Do you have an idea about the compat of your resource with this one?:

Peons to build buildings
#8
04/29/2007 (9:28 pm)
I don't see why the two couldn't be compatible, it looks like the only function that both of these resources use is serverCmdPlaceBuilding... with my resource hardly using it.

But that post reminded me that I didn't post the code for ~/server/rts/building.cs, so here is that file. Again, text in bold and italics is text that you have to change.

datablock RTSBuildingData(testBuilding)
{
   category = "[b][i]Buildings[/i][/b]";
   shapeFile = "[b][i]~/data/shapes/block/torque_logo.dts[/i][/b]";
};

function serverCmdPlaceBuilding(%conn, %transform, %data)
{
   %building = new RTSBuilding()
   {
      datablock = %data;
      scale = "1 1 1";
   };
   %building.setTransform( %transform );

   MissionCleanup.add(%building);
}

function RTSBuildingData::create(%data)
{
   %building = new RTSBuilding()
   {
      datablock = %data;
      scale = "1 1 1";
   };

   return %building;
}
#9
05/02/2007 (8:16 pm)
Sorry about that, I knew that I had to miss something.

Quote:
guirtstsctrl.cc(279) : error C2065: 'M_PI2' : undeclared identifier

Add the following code to engine/math/mConstants.h, it doesn't matter where you put it, but I put it under M_PI and above M_SQRT2:
#define M_PI2        1.57079632679489661923

Quote:
guirtstsctrl.cc(564) : error C2679: binary '+' : no operator found which takes a right-hand operand of type 'double' (or there is no acceptable conversion)

Add the following to engine/math/mPoint.h:
Lines 318-319:
Point3F  operator+(const F32&) const;
	Point3F  operator-(const F32&) const;
Lines 1303-1312 (lines are after you added the two above lines, between operator- and operator+=):
inline Point3F Point3F::operator+(const F32& _constant) const
{
   return Point3F(x + _constant, y + _constant,  z + _constant);
}


inline Point3F Point3F::operator-(const F32& _constant) const
{
   return Point3F(x - _constant, y - _constant,  z - _constant);
}

Quote:
guirtstsctrl.cc(578) : error C2065: 'mRayCastMask' : undeclared identifier
guirtstsctrl.cc(588) : error C2065: 'mCollision' : undeclared identifier

Add the following two variables to guiRTSTSCtrl.h, under RTSBuildingMarker *mNewBuilding is where I put it:
ObjectsList mCollision; ///< List of objects that collided with the ray cast for building placement.
	U32         mRayCastMask; ///< What do we want to collide with when doing the raycast?

Also add the following to the GuiRTSTSCtrl::GuiRTSTSCtrl constructor (in guiRTSTSCtrl.cpp):
mRayCastMask = InteriorObjectType |
	               WaterObjectType |
	               ShapeBaseObjectType |
	               StaticShapeObjectType |
	               ItemObjectType |
	               TerrainObjectType | 
	               StaticTSObjectType |
	               StaticObjectType;

Quote:
guirtstsctrl.cc(588) : error C2228: left of '.addObject' must have class/struct/union
guirtstsctrl.cc(597) : error C2228: left of '.size' must have class/struct/union
guirtstsctrl.cc(598) : error C2227: left of '->getPosition' must point to class/struct/union/generic type
guirtstsctrl.cc(603) : error C2228: left of '.removeObject' must have class/struct/union
Those errors should clear up after you add the two variables.

Let me know if there are any other issues, and thanks for dealing with untested code, I figured that it would be better to get it out there and have errors then to not post it at all.

Edit: it looks like the previous post was deleted...
#10
05/02/2007 (9:40 pm)
Well really thanks Justin! It appears to me that puting those errors before i really put an eye on them was not very considerated, so i decided to delete the post until i at least try to figure out something.

In the meantime you were reading the post!! It was really funny beacuse as i keep the firefox open, the forum connot update and as a result I never see your answer! and i put other post about the subject... (with some really-wrong-fixes :D) just to finally discover that you already have answered my preys :D

Im gonna check your post now and try again...
#11
05/02/2007 (10:05 pm)
Ok heres another:

its on the line:
ObjectsList mCollision; ///< List of objects that collided with the ray cast for building placement.


guirtstsctrl.h(179) : error C2146: syntax error : missing ';' before identifier 'mCollision'
guirtstsctrl.h(179) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
guirtstsctrl.h(179) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int

yes, the 2nd error appears twice...

Do you see something? :)
#12
05/03/2007 (7:14 am)
That looks like it is an issue with the line before it. Do you have a ';' at the end of the previous line?
#13
05/03/2007 (7:19 am)
The spell is well, i think is something in that line, in fact if i comment it, the error dissapear (that one, of course)

here's those lines if you wanna see as it is placed:

bool mPlacingBuilding;			///< Are we placing a building?
RTSBuildingMarker* mNewBuilding;	///< The building marker that we are placing.

ObjectsList mCollision; ///< List of objects that collided with the ray cast for building placement.
U32         mRayCastMask; ///< What do we want to collide with when doing the raycast?
#14
05/04/2007 (7:52 pm)
Do you have the ObjectsList class? I didn't think that I created that class, but I guess I may have.

Here's the code for the ObjectsList class in GuiRTSTSCtrl.h (added right after the class declaration for GuiRTSTSCtrl, so around line 31 for me, in the private section:
/** 
    * Adds the ability to keep a list of objects.
    * This nested class keeps the selection list as well as the list of collided objects for building placement.
    */
   class ObjectsList : public SimObject
   {
		typedef SimObject Parent;

   private:

		SimSet mObjectList; ///< List of objects in the list.

	public:

		/** 
		 * Deconstructor for ObjectsList.
		 */
		~ObjectsList();

		/** 
		 * Size of the object list.
		 * @return size of the list.
		 */
		U32 size()  { return(mObjectList.size()); }
		/** 
		 * Overloaded array that returns the object at the inputted index.
		 * @param index index of object
		 * @return object at index value.
		 */
		SceneObject * operator[] (S32 index) { return((SceneObject*)mObjectList[index]); }

		/** 
		 * Is the object in the list?
		 * @param obj object to be tested.
		 * @return Wether or not the object is in the list.
		 */
		bool objInSet(SceneObject * obj);

		/** 
		 * Adds the object.
		 * @param obj object to be added.
		 * @return was the object addition a success?
		 */
		bool addObject(SceneObject * obj);
		/** 
		 * Removes the object.
		 * @param obj object to be removed.
		 * @return was the object removal a success? 
		 */
		bool removeObject(SceneObject * obj);
		/** 
		 * Clears the list.
		 */
		void clear();
   };

And then the code for GuiRTSTSCtrl.cc (added after BUILDING_RAYCAST_EXPANSION):
//----------------------------------------------------------------------------
// Class guiRTSTSCtrl::ObjectsList
// Based upon WorldEditor::Selection
//   defined in engine/editor/worldEditor.cc line 190
//----------------------------------------------------------------------------

GuiRTSTSCtrl::ObjectsList::~ObjectsList()
{
}

bool GuiRTSTSCtrl::ObjectsList::objInSet(SceneObject * obj)
{
   return (mObjectList.find(mObjectList.begin(), mObjectList.end(), obj) != mObjectList.end());
}

bool GuiRTSTSCtrl::ObjectsList::addObject(SceneObject * obj)
{
   if(objInSet(obj))
      return(false);

   mObjectList.addObject(obj);
   deleteNotify(obj);

   return(true);
}

bool GuiRTSTSCtrl::ObjectsList::removeObject(SceneObject * obj)
{
   if(!objInSet(obj))
      return(false);

   mObjectList.removeObject(obj);
   clearNotify(obj);

   return(true);
}

void GuiRTSTSCtrl::ObjectsList::clear()
{
   while(mObjectList.size())
		removeObject((SceneObject*)mObjectList[0]);
}

I really didn't think that I added this much code to get building validation to work... but I guess I did.
#15
05/04/2007 (8:42 pm)
Well thats it! thanks man, its compiling now. :)
Im getting a punch of errors from the scripts, but im really tyred right now, i think im gonna go for it tomorrow.

Overhere we used to say "tomorrow will be other day"... may seems redundant, but speak about a fresh view. So, til tomorrow :D
#16
05/06/2007 (10:06 am)
Well the delay comes for this: I tried to make this in top of an improved version of the World Domination, but its not working and im almost lost. I think ill be opening a new pure-RTS Kit project and try it there.
#17
05/06/2007 (10:26 am)
That sounds good. This week I'm planning on adding it to a fresh version of the RTS Kit, following the instructions that I wrote above. If that all goes well I should be able to get a TDN article up so it is easier to follow.
#18
05/06/2007 (12:38 pm)
Ok that helps a lot. In fact much of the code you paste here its already coded.
Then I will make a resume of that, but Im on trouble again. This is the console output when the engine load:
Loading compiled script starter.RTS/server/scripts/items/building.cs.
Warning: (d:\torque\bpv\sdk\engine\console\consoleobject.cc @ 62) Couldn't find class rep for dynamic class: RTSBuildingData
starter.RTS/server/scripts/items/building.cs (13): Unable to instantiate non-conobject class RTSBuildingData.

This is when I launch the mission
Loading compiled script starter.RTS/server/scripts/items/building.cs.
Warning: (d:\torque\bpv\sdk\engine\console\consoleobject.cc @ 62) Couldn't find class rep for dynamic class: RTSBuildingData
starter.RTS/server/scripts/items/building.cs (13): Unable to instantiate non-conobject class RTSBuildingData.

And finally this when I try to build:
Starting to place building!
[b]starter.RTS/client/scripts/inputHandler.cs (26): Unable to find object: '-1' attempting to call function 'getClassName'
starter.RTS/client/scripts/inputHandler.cs (28): Unable to find object: '-1' attempting to call function 'getClassName'
starter.RTS/client/scripts/inputHandler.cs (30): Unable to find object: '-1' attempting to call function 'getClassName'[/b]
Client sending building notify! -47.2381 -54.5216 246.827 1 0 0 0 TestBuildingBlock
Mapping string: PlaceBuilding to index: 4
Object 'TestBuildingBlock' is not a member of the 'GameBaseData' data block class
starter.RTS/server/scripts/items/building.cs (23): Register object failed for object (null) of class RTSBuilding.
starter.RTS/server/scripts/items/building.cs (24): Unable to find object: '0' attempting to call function 'setTransform'
Set::add: Object "0" doesn't exist

I think its related with building.cs but I dont know your structure, so any ideas?
We (Im) are almost there!! :)

Udate: If i leave the building.cs as original, that is, without your modifs, only the errors marked in bold appear, and the building system run as out-of-the-box RTS kit
#19
05/08/2007 (6:48 am)
Well Justin, thinking about this, maybe is better to wait until yourself port it to a fresh RTS Kit, than be fighting around with this errors. I tell this beacuse Im a little lose on this one (in part, due to the fact of unknowing your own struture) and you are really the only one resolving the thing.

I can tell you, BIG parts of your C code are already there in the engine, the real changes are few, so maybe its more easy for you to give it a try with a fresh copy than keep seeing my continuos issues :D
Even if you want I can give you the already changed .cc and .h files, so you only have to compile and see the torquescript part.
#20
05/09/2007 (4:49 pm)
I was able to successfully build it, and the only thing that I left out was the RTSBuilding file. I made a lot of changes to that file from the RTS Kit, so I will just post the whole file below.

I took out a lot of stuff that I personally won't be using for my game, so you may want to look back at the original RTSBuilding.cc to see what I removed (mostly dealing with RTSBuildingMarker::renderObject). Anyway, here are the files:

RTSBuilding.h:
#ifndef _RTSBUILDING_H_
#define _RTSBUILDING_H_

#include "game/staticShape.h"
#include "game/tsStatic.h"
#include "game/player.h"

class RTSBuildingData : public StaticShapeData
{
   typedef StaticShapeData Parent;

private:

public:
   
   RTSBuildingData();


   /// Register dynamic fields in a subclass of ConsoleObject.
   static void initPersistFields();


   /**
    * Packs the data to send the datablock name.
    * @param stream connection stream
	*/
   void packData(BitStream* stream);
   /**
    * Unpacks the data from the received datablock name.
    * @param stream connection stream
	 */
   void unpackData(BitStream* stream);

   /// A simple console object - no special network attributes.
   DECLARE_CONOBJECT(RTSBuildingData);
};


class RTSBuilding : public StaticShape
{
   typedef StaticShape Parent;

public:

   RTSBuilding();


   /// A simple console object - no special network attributes.
   DECLARE_CONOBJECT(RTSBuilding);
};

/// A building marker is what attaches to the cursor in order to place
/// a building.  Usually, the marker will be translucent to show that
/// the building isn't actually placed, but what the user is seeing is
/// just a preview of where the building will be placed.
///
/// All the class consists of is basically an object that is completely
/// cutoff from the network (client-side only) and a specialized render
/// function.  All the render function does is allow for easy transparency.
class RTSBuildingMarker : public TSStatic
{
   typedef TSStatic Parent;

private:

   F32 mTrans; /// Trasparency value, 1.0 = no transparency, 0 = full transparency

public:

   RTSBuildingMarker();


   bool onAdd();
   void onRemove();

   void notifyCollision();

   void renderObject(SceneState* state, SceneRenderImage* image);


   /// A simple console object - no special network attributes.
   DECLARE_CONOBJECT(RTSBuildingMarker);
};

#endif

And RTSBuilding.cc:
#include "game/RTS/RTSBuilding.h"
#include "game/shapeBase.h"
#include "dgl/dgl.h"
#include "ts/tsShapeInstance.h"

//----------------------------------------------------------------------------
// RTSBuilding Datablock - currently not doing anything special
//----------------------------------------------------------------------------

IMPLEMENT_CO_DATABLOCK_V1(RTSBuildingData);

RTSBuildingData::RTSBuildingData()
{
}

void RTSBuildingData::packData(BitStream* stream)
{
   Parent::packData(stream);
}

void RTSBuildingData::unpackData(BitStream* stream)
{
   Parent::unpackData(stream);
}

void RTSBuildingData::initPersistFields()
{
   Parent::initPersistFields();
}

//----------------------------------------------------------------------------
// RTSBuilding
//----------------------------------------------------------------------------

IMPLEMENT_CO_NETOBJECT_V1(RTSBuilding);

RTSBuilding::RTSBuilding()
{
	mNetFlags.set(Ghostable);
}

//----------------------------------------------------------------------------
// RTSBuildingMaker
//----------------------------------------------------------------------------

IMPLEMENT_CONOBJECT(RTSBuildingMarker);

RTSBuildingMarker::RTSBuildingMarker()
{
      // Magic to make this a client-side only object.
      mNetFlags.clear(Ghostable);  //NOT ghostable - don't want this networked
      mNetFlags.set(IsGhost);      //however, it IS a client-side object

	  mTrans = 0.5f;
}
bool RTSBuildingMarker::onAdd()
{
   if( !Parent::onAdd() )
      return false;
   return true;
}

void RTSBuildingMarker::onRemove()
{
   Parent::onRemove();
}

void RTSBuildingMarker::renderObject(SceneState* state, SceneRenderImage* image)
{
   // Not going to work on transparency until TGEA
   Parent::renderObject(state,image);
}

That should get you up and running, let me know if you have any problems (I'll hopefully get that article up on TDN within a couple of days).
Page «Previous 1 2