Alternate Collision Masks
by Bill Vee · 07/01/2008 (9:59 am) · 10 comments
Download Code File
I originally created this code to allow players to pass thru the atlas terrain while in an interior.
This resource will allow you to set an objects collision mask on the fly between a standard mask and an alternate one.
This is useful for when you need an object like the player to not collide with the terrain while inside an interior.
While the setempty option can be used on a legacy/mega terrain to do the same thing , there are situations were this is not practical.
The setempty method creates a square hole in the terrain and depending on your needs your interior might not perfectly align itself to the empty hole.
And as for TGEA's atlas terrains go , there is no setempty option.
We start with gamebase.h.
Change
to a public area of the Gamebase decleration
Just after DECLARE_CONOBJECT(GameBase); would be fine.
Now open GameBase.cc/cpp and add to the end of GameBase::packUpdate
To the end of Gamebase.cc/cpp
Ok done with gamebase.
Ok heres how the alternate collision masking works.
All the classes that use collision masks derive from GameBase.
Using the Projectile class as an example.
Comment out or delete the defines for csmStaticCollisionMask and csmDynamicCollisionMask.
Just the defines not all the references for them.
We are moving the defines to GameBase.h.
Just comment out or delete the following from projectile.cc
Then move it to the Projectile constructor like this.
sStandardCollisionMask will hold the original collision mask and
sAlternateCollisionMask will hold the alternate mask.
Then comment out or delete
And change csmDamageableMask from
Now place
I used the projectile class as an example but you should get the idea.
To use a different class like the Player just do the same thing.
Comment out or delete the original static collision masks defines.
Move them to the constructor with sStandardCollisionMask used as the original mask and sAlternateCollisionMask used as the alternate mask.
Add
and in Player.cc/cpp add
Then from script call
And use
Enjoy.
I originally created this code to allow players to pass thru the atlas terrain while in an interior.
This resource will allow you to set an objects collision mask on the fly between a standard mask and an alternate one.
This is useful for when you need an object like the player to not collide with the terrain while inside an interior.
While the setempty option can be used on a legacy/mega terrain to do the same thing , there are situations were this is not practical.
The setempty method creates a square hole in the terrain and depending on your needs your interior might not perfectly align itself to the empty hole.
And as for TGEA's atlas terrains go , there is no setempty option.
We start with gamebase.h.
Change
enum GameBaseMasks {
InitialUpdateMask = Parent::NextFreeMask,
DataBlockMask = InitialUpdateMask << 1,
ExtendedInfoMask = DataBlockMask << 1,
ControlMask = ExtendedInfoMask << 1,
NextFreeMask = ControlMask << 1
};Toenum GameBaseMasks {
InitialUpdateMask = Parent::NextFreeMask,
DataBlockMask = InitialUpdateMask << 1,
ExtendedInfoMask = DataBlockMask << 1,
ControlMask = ExtendedInfoMask << 1,
CollisionMask = ControlMask << 1,
NextFreeMask = CollisionMask << 1
};Then addbool mUseAlternateCollision; void UpdateCollisionMask(); //New collision Masks U32 sAlternateCollisionMask; U32 sStandardCollisionMask; //Projectile , Debris U32 csmStaticCollisionMask; U32 csmDynamicCollisionMask; //Player , FlyingVehicle , HoverVehicle , RigidShape , Item , WheeledVehicle //This is for future changes. Don't uncomment unless you are actually using them. //It will cause problems if you don't set them up correctly. //U32 sCollisionMoveMask; //U32 sServerCollisionContactMask; //U32 sClientCollisionContactMask; //U32 sServerCollisionMask; //U32 sClientCollisionMask; void SetStandardCollision(bool UseStandardCollision);
to a public area of the Gamebase decleration
Just after DECLARE_CONOBJECT(GameBase); would be fine.
Now open GameBase.cc/cpp and add to the end of GameBase::packUpdate
if (stream->writeFlag(mask & CollisionMask))
{
stream->writeFlag(mUseAlternateCollision);
}just beforereturn 0;Then add to the end of GameBase::unpackUpdate
if (stream->readFlag())
{
mUseAlternateCollision = stream->readFlag();
}Then add void GameBase::SetStandardCollision(bool Collide)
{
mUseAlternateCollision = !Collide;
setMaskBits(CollisionMask);
}
ConsoleMethod( GameBase, SetStandardCollision, void, 3, 3, "SetStandardCollision True or False")
{
return object->SetStandardCollision(dAtob(argv[2]));
}
void GameBase::UpdateCollisionMask()
{
if(mUseAlternateCollision)
{
csmStaticCollisionMask = sAlternateCollisionMask;
}
else
{
csmStaticCollisionMask = sStandardCollisionMask;
}
}To the end of Gamebase.cc/cpp
Ok done with gamebase.
Ok heres how the alternate collision masking works.
All the classes that use collision masks derive from GameBase.
Using the Projectile class as an example.
Comment out or delete the defines for csmStaticCollisionMask and csmDynamicCollisionMask.
Just the defines not all the references for them.
We are moving the defines to GameBase.h.
Just comment out or delete the following from projectile.cc
const U32 Projectile::csmStaticCollisionMask = TerrainObjectType |
InteriorObjectType |
StaticObjectType;
const U32 Projectile::csmDynamicCollisionMask = PlayerObjectType |
VehicleObjectType |
DamagableItemObjectType;Then move it to the Projectile constructor like this.
mUseAlternateCollision = false; sStandardCollisionMask = TerrainObjectType | InteriorObjectType | StaticObjectType; sAlternateCollisionMask = InteriorObjectType; csmStaticCollisionMask = sStandardCollisionMask; csmDynamicCollisionMask = PlayerObjectType | VehicleObjectType | DamagableItemObjectType; csmDamageableMask = csmDynamicCollisionMask;
sStandardCollisionMask will hold the original collision mask and
sAlternateCollisionMask will hold the alternate mask.
Then comment out or delete
static const U32 csmStaticCollisionMask;
static const U32 csmDynamicCollisionMask; From projectile.hAnd change csmDamageableMask from
static const U32 csmDamageableMask;to
U32 csmDamageableMask;
Now place
UpdateCollisionMask();In Projectile::processTick before
if (getContainer()->castRay(oldPosition, newPosition, csmDynamicCollisionMask | csmStaticCollisionMask, &rInfo) == true)to determine if you should use the default or alternate collision mask.
I used the projectile class as an example but you should get the idea.
To use a different class like the Player just do the same thing.
Comment out or delete the original static collision masks defines.
Move them to the constructor with sStandardCollisionMask used as the original mask and sAlternateCollisionMask used as the alternate mask.
Add
void UpdateCollisionMask();to the header
and in Player.cc/cpp add
void Player::UpdateCollisionMask()
{
if(mUseAlternateCollision)
{
"original collision mask name" = sAlternateCollisionMask;
}
else
{
"original collision mask name" = sStandardCollisionMask;
}
}Then in Player::processtick you addUpdateCollisionMask();before the first castray that uses the collision masks.
Then from script call
%object.SetStandardCollision(false);To use the alternate collison mask.
And use
%object.SetStandardCollision(true);To use the original mask.
Enjoy.
About the author
#2
One question though: Do you have a fan club? I want to join!
07/01/2008 (1:34 pm)
Aren't these last two resources just the Terrain Occluding Portal collision stuff split in two? Not complaining though, means less work for me! =DOne question though: Do you have a fan club? I want to join!
#3
Another reason was I ran across a post were someone wanting the projectiles to be trigger aware and my original resource,"Terrain occluding portal", doesn't clearly state that it can do that unless you read most of the resource.
@ Danni - I can see in a high latency dedicated server scenario that the client and server could be at some point running with 2 different sets of collisionmasks but unless the data packet was dropped completely it would resync upon the next packet update.
Is there another way to make sure the client and server are always synced?
07/01/2008 (4:55 pm)
@Nathan- Yes it is. As I said in the other resource I moved it to 3 different resources because thats what it really is. 3 distinctly different code modifications. I also stated in the original resource that the zip file still contains all three wrapped into one.Another reason was I ran across a post were someone wanting the projectiles to be trigger aware and my original resource,"Terrain occluding portal", doesn't clearly state that it can do that unless you read most of the resource.
@ Danni - I can see in a high latency dedicated server scenario that the client and server could be at some point running with 2 different sets of collisionmasks but unless the data packet was dropped completely it would resync upon the next packet update.
Is there another way to make sure the client and server are always synced?
#4
07/01/2008 (5:01 pm)
I'm having a few problems with this. I had to set mUseAlternateCollision to be false in the GameBase constructor, otherwise it would start off using the alternate collision, and since I've done that, I can only take a few steps until the game crashes. The weird thing though is that it was working perfectly when it was the interiors that were taken out of the alternate collision mask.
#5
The problem comes with the local player because in a networked environment, they actually move before the server tells them to. When you move, the values are packed into a "Move" client side and ran through ProcessTick, the Move is stored in a list (mMoveList), then is sent off to the server. Once the server gets the Move, it too runs Player::ProcessTick(Move) then sends the resulting position back to the local client. At this point, the local client will move to the position sent by the server and run any moves in mMoveList which are after the sent move again in ClientProcessList::clientCatchup (meaning Player::ProcessTick(Move) could be called a lot client side when this happens). This whole system is called Client Prediction and is a special case for only the local client, not Ghosts. This allows the client to move instantly yet allow the server to remain authoritative.
The important thing is that during all of those runs, the servers and clients state should be identical, meaning the client should alter their collisionMask first in their ProcessTick, the server should do the same once it receives the Move so the positions are the same. With this in place, it is redundant to send the updated masks to the local client, it could actually make matters worse (runs outside then falls through terrain because the server updates later).
Hope that all makes sense.
07/01/2008 (6:13 pm)
I just checked and I think you are actually OK. The problem comes with the local player because in a networked environment, they actually move before the server tells them to. When you move, the values are packed into a "Move" client side and ran through ProcessTick, the Move is stored in a list (mMoveList), then is sent off to the server. Once the server gets the Move, it too runs Player::ProcessTick(Move) then sends the resulting position back to the local client. At this point, the local client will move to the position sent by the server and run any moves in mMoveList which are after the sent move again in ClientProcessList::clientCatchup (meaning Player::ProcessTick(Move) could be called a lot client side when this happens). This whole system is called Client Prediction and is a special case for only the local client, not Ghosts. This allows the client to move instantly yet allow the server to remain authoritative.
The important thing is that during all of those runs, the servers and clients state should be identical, meaning the client should alter their collisionMask first in their ProcessTick, the server should do the same once it receives the Move so the positions are the same. With this in place, it is redundant to send the updated masks to the local client, it could actually make matters worse (runs outside then falls through terrain because the server updates later).
Hope that all makes sense.
#6
What object types did you remove when it crashed. If it was the terrain you have to take a little care with it as a terrain object typemask is a TerrainObjectType and a StaticObjectType and a StaticRenderedObjectType.
If you want the player to pass thru the terrain you have to remove all three from the alternate masks define.
@Danni- Thanks for the recheck. It took me a while to get the whole client / server process's. I thought for a sec I had it all wrong again:)
07/01/2008 (7:54 pm)
@Nathan- You are correct the constructor should be false if you want it to start with the default collision mask.What object types did you remove when it crashed. If it was the terrain you have to take a little care with it as a terrain object typemask is a TerrainObjectType and a StaticObjectType and a StaticRenderedObjectType.
If you want the player to pass thru the terrain you have to remove all three from the alternate masks define.
@Danni- Thanks for the recheck. It took me a while to get the whole client / server process's. I thought for a sec I had it all wrong again:)
#7
Edit -> No, I don't think that's it. There is no Static or RenderedStatic in the alternate collision, and it still crashes.
Edit2-> Never mind, it's working now. Go figure =P
07/02/2008 (7:02 am)
That might be where my problem is! I only removed the TerrainObjectType, and it crashes when I move (using standard collision), but if I don't move and use the alternate collision, I'll fall through the terrain.Edit -> No, I don't think that's it. There is no Static or RenderedStatic in the alternate collision, and it still crashes.
Edit2-> Never mind, it's working now. Go figure =P
#8
They were not commented out in my original post and I changed it a couple of hours later and hoped no one seen it.
They were meant as an example of how to do it for some of the other classes.
And it can cause the game to work fine one time then crash or exhibit odd behavior like a jittery player.
07/02/2008 (12:25 pm)
@Nathan Kent- Check to see if the following defines are commented out of gamebase.cc/cpp//U32 sCollisionMoveMask; //U32 sServerCollisionContactMask; //U32 sClientCollisionContactMask; //U32 sServerCollisionMask; //U32 sClientCollisionMask;
They were not commented out in my original post and I changed it a couple of hours later and hoped no one seen it.
They were meant as an example of how to do it for some of the other classes.
And it can cause the game to work fine one time then crash or exhibit odd behavior like a jittery player.
#9
07/02/2008 (2:18 pm)
Those aren't commented in mine, but the game doesn't crash anymore, and I never had problems with a jittery player.
#10
07/02/2008 (2:36 pm)
It sounds like you implemented this resource in the player class so as long as you don't have them re-defined there then all should be well. 
Torque 3D Owner Danni
One question though, why would you want to send when the collision is changed, surely you want to do it both client and server side at the appropriate times otherwise this could potentially block when a user has around 70-100+ ping on a dedicated server?