Item Collision
by Leslie Young · 03/04/2003 (9:22 am) · 27 comments
I wanted an Item the player could collide against but also pick up if he look at it and press some key. After going through the code I made the following modifications to the Item class giving it the ability the be used as is or to be collided against and the ability for the player ti pick up the Item if he wants and throw it.
Here follows my first resource, so please bear with me, and please point out errors and better ways to do stuff :b
Note that code in BOLD must be added or changed:
Modifications in PLAYER.CC to enable collisions with items:
Around line 87:
I'll also make the variable "mCollideable" visible to the script so you can set it to enable or disable use of the new methods.
In ITEM.H:
Around line 142:
Around line 804 change the buildPolyList member:
In player.cs around Line 773:
I'll show you how I added the ability to pick up an item, mount it and then throw it away again, this code is modufied stuff to work with my current game and don't make use of the Inventory but you can pick up some ideas from it:
In default.bind.cs you can add to new key binds:
The following code is server side and you could dump the function in your ITEM.CS file:
While typing this I thought about the idea of using a static object in the world and on it's onCollide event destroy it and create an Item object (that looks the same) and mount that to the player, allowing him to throw it again and thus you would destroy that Item object after it landed in the world and create a static object again where it landed.
Here follows my first resource, so please bear with me, and please point out errors and better ways to do stuff :b
Note that code in BOLD must be added or changed:
Modifications in PLAYER.CC to enable collisions with items:
Around line 87:
static U32 sCollisionMoveMask = (TerrainObjectType | InteriorObjectType |
WaterObjectType | PlayerObjectType |
StaticShapeObjectType | VehicleObjectType |
ForceFieldObjectType |
[b]ItemObjectType |[/b] // - add Item object type to collision mask
PhysicalZoneObjectType | StaticTSObjectType);
static U32 sServerCollisionContactMask = (sCollisionMoveMask |
( [b]//ItemObjectType | - as it is included in sCollisionMoveMask [/b]
TriggerObjectType |
CorpseObjectType));With that done the player will collide against the Item's bounding box, so now we need to make changes in the Item class to make use use of the collision object you made in 3D Studio or Milkshape (Col-1, Collision-1)I'll also make the variable "mCollideable" visible to the script so you can set it to enable or disable use of the new methods.
In ITEM.H:
Around line 142:
bool onNewDataBlock(GameBaseData* dptr);
[b]bool isCollideable() { return mCollideable; } // add new meber function for collideable[/b]
bool isStatic() { return mStatic; }
bool isRotating() { return mRotate; }In ITEM.CCAround line 804 change the buildPolyList member:
bool Item::buildPolyList(AbstractPolyList* polyList, const Box3F& box, const SphereF& sphere)
{
[b]
// leslie - added a test for collideable to stop use of bounding box in
// collision test if needed
if (mCollideable) {
Parent::buildPolyList(polyList, box, sphere); // call the ShapeBase buildPolylist
} else { [/b]
// Collision with the item is always against the item's object
// space bounding box axis aligned in world space.
Point3F pos;
mObjToWorld.getColumn(3,&pos);
IMat.setColumn(3,pos);
polyList->setTransform(&IMat, mObjScale);
polyList->setObject(this);
polyList->addBox(mObjBox);
[b]}[/b]
return true;
}Around line 942, Add cIsCollideable:[b]
// leslie
static bool cIsCollideable(SimObject *ptr, S32, const char **)
{
Item* obj = static_cast<Item*>(ptr);
return obj->isCollideable();
}
// ...
[/b]
static bool cIsStatic(SimObject *ptr, S32, const char **)
{
Item* obj = static_cast<Item*>(ptr);
return obj->isStatic();
}Around line 1009, change the group name as there seems to some conflict with the name Misc that it was originally:void Item::initPersistFields()
{
Parent::initPersistFields();
[b]addGroup("More"); // leslie: Changed Group Header.[/b]
addField("collideable", TypeBool, Offset(mCollideable, Item));
addField("static", TypeBool, Offset(mStatic, Item));
addField("rotate", TypeBool, Offset(mRotate, Item));
[b]endGroup("More"); // leslie: Changed Group Footer.[/b]
}Add to consoleInit, line 1019:void Item::consoleInit()
{
[b]Con::addCommand("Item", "isCollideable", cIsCollideable, "obj.isCollideable()", 2, 2); // leslie [/b]
Con::addCommand("Item", "isStatic", cIsStatic, "obj.isStatic()", 2, 2);
Con::addCommand("Item", "isRotating", cIsRotating, "obj.isRotating()", 2, 2);
Con::addCommand("Item", "setCollisionTimeout", cSetCollisionTimeout, "obj.setCollisionTimeout(object)", 3, 3);
Con::addCommand("Item", "getLastStickyPos", cGetLastStickyPos, "obj.getLastStickyPos()", 2, 2);
Con::addCommand("Item", "getLastStickyNormal", cGetLastStickyNormal, "obj.getLastStickyNormal()", 2, 2);
Con::addVariable("Item::minWarpTicks",TypeF32,&sMinWarpTicks);
Con::addVariable("Item::maxWarpTicks",TypeS32,&sMaxWarpTicks);
}Line 1071: change buildConvex:void Item::buildConvex(const Box3F& box, Convex* convex)
{
[b]// leslie - added a test for collideable to stop use of bounding box in
// collision test if needed
if (mCollideable) {
Parent::buildConvex(box, convex);
} else {[/b]
if (mShapeInstance == NULL)
return;
...
...
mObjBox.getCenter(&cp->mCenter);
cp->mSize.x = mObjBox.len_x() / 2.0f;
cp->mSize.y = mObjBox.len_y() / 2.0f;
cp->mSize.z = mObjBox.len_z() / 2.0f;
[b]}[/b]
}All you need to do now is disable the auto pickup in player.cs so you can see the player collide against the Item and maybe add a server comamnd to allow the player to pick up the object and throw it.In player.cs around Line 773:
function Armor::onCollision(%this,%obj,%col)
{
if (%obj.getState() $= "Dead")
return;
// Try and pickup all items
[b]// if (%col.getClassName() $= "Item")
// %obj.pickup(%col); - just disable this for now[/b]
// Mount vehicles
%this = %col.getDataBlock();
...You could expand on your own code to allow auto pickup of onject that don't have collideable set to support both items that work like current and items that can be collided against, picked up and thrown.I'll show you how I added the ability to pick up an item, mount it and then throw it away again, this code is modufied stuff to work with my current game and don't make use of the Inventory but you can pick up some ideas from it:
In default.bind.cs you can add to new key binds:
moveMap.bindCmd(keyboard, "h", "commandToServer('pickup');", ""); // leslie
moveMap.bindCmd(keyboard, "g", "commandToServer('throw');", ""); // leslieAlso remeber to add the same lines to config.cs so you don't run into problems binds not being used.The following code is server side and you could dump the function in your ITEM.CS file:
// this allow pickup of an object visible up to 4 meter and mount it
function serverCmdpickup(%client)
{
%player = %client.player;
// check if there is an object that can be picked up in our view
%eye = %player.getEyeVector();
%vec = vectorScale(%eye, 4); // 4 meter
%startPoint = %player.getEyeTransform();
%endPoint = VectorAdd(%startPoint,%vec);
%searchMasks = $TypeMasks::ItemObjectType;
%object = ContainerRayCast(%startPoint, %endPoint, %searchMasks, %player);
if (%object) {
%object.getDatablock().onPickup(%object,%player);
} else {
//messageClient(%client, 'MsgTutorial', '\c0No object in range');
}
}
// allow the player to throw mounted object
function serverCmdthrow(%client)
{
// Throw the normally the mounted item
%player = %client.player;
if (%player.getMountedImage($BlockWeaponSlot) != 0) {
%img = %player.getMountedImage($BlockWeaponSlot); // weapon slot
%object = %img.item;
%object.onThrow(%player);
}
}in my game i created a new ITEM class set called BlockWeapon, this is the class used in the datablock and thus this will be the mebers called from the above onPickup/onThrow callsfunction BlockWeapon::onPickup(%this, %obj, %player)
{
// Called by the server command pickup, this will check if
// the player gor a block mounted and if not a new block
// will be mounted and the object on the terrain will be detroyed
// Is there a block mounted?
if (%player.getClassName() $= "Player" && // - fixme leslie - can prolly take this out as it suppose to allways be player object?
%player.getMountedImage($BlockWeaponSlot) == 0) {
%player.mountImage(%this.image, $BlockWeaponSlot); // Mount a block (%this, is the datablock)
%obj.delete(); // Remove object from the world
}
}
function BlockWeapon::onThrow(%this, %player)
{
// Called by server command throw, this will unmount the mounted
// block, create a new object in the world and give that
// object an initial velocity to make it move a distance
// Unmount the block, but first check what the object was
// for use when creating in the world.
%player.unmountImage($BlockWeaponSlot);
// Create the actual object in the world, and add it to
// the mission group so it's cleaned up when the mission is done.
%obj = new Item() {
datablock = %this;
rotate = false;
static = false;
collideable = true;
};
MissionGroup.add(%obj);
%this.throwBlock(%player, %obj); // Throw it (fron inventory code :)
}
function BlockWeapon::throwBlock(%this, %player, %obj)
{
// Throw the given object in the direction the player is looking
// The force value is hardcoded according to the current default
// object mass and mission gravity (20m/s^2).
%throwForce = %this.throwForce;
if (!%throwForce) %throwForce = 20;
// Start with the shape's eye vector...
%eye = %player.getEyeVector();
%vec = vectorScale(%eye, %throwForce);
// Add a vertical component to give the object a better arc
%verticalForce = %throwForce / 2;
%dot = vectorDot("0 0 1",%eye);
if (%dot < 0) %dot = -%dot;
%vec = vectorAdd(%vec,vectorScale("0 0 " @ %verticalForce,1 - %dot));
// Add the shape's velocity
%vec = vectorAdd(%vec,%player.getVelocity());
// Set the object's position and initial velocity
%pos = getBoxCenter(%player.getWorldBox());
%obj.setTransform(%pos);
%obj.applyImpulse(%pos,%vec);
// Since the object is thrown from the center of the
// shape, the object needs to avoid colliding with it's thrower.
%obj.setCollisionTimeout(%player);
}And that should do it :)While typing this I thought about the idea of using a static object in the world and on it's onCollide event destroy it and create an Item object (that looks the same) and mount that to the player, allowing him to throw it again and thus you would destroy that Item object after it landed in the world and create a static object again where it landed.
About the author
Recent Blogs
• TGEA GroundCover Terrain Surface Reference• Still around
• KBM update
• Finally some updates
• KBM.. More update
#2
03/04/2003 (4:32 pm)
I don't have ItemObjectType in the sCollisionMoveMask for my vehicle... but I still get collisions... I wonder why...
#3
...
bool Item::buildPolyList(AbstractPolyList* polyList, const Box3F& box, const SphereF& sphere)
...
@Tim, I think what you suggest is entirely possible as long as the model is defined as an item.
@Robert, I think your vehicle is vehicleObjectType not ItemObjectType.
03/05/2003 (2:06 am)
Leslie, very nice! Many thanks...we were getting to this point but have been using NULL difs to provide collision. One note/question so far: in the following line, you added the box and sphere?...
bool Item::buildPolyList(AbstractPolyList* polyList, const Box3F& box, const SphereF& sphere)
...
@Tim, I think what you suggest is entirely possible as long as the model is defined as an item.
@Robert, I think your vehicle is vehicleObjectType not ItemObjectType.
#4
03/05/2003 (9:30 am)
You can already drop structures like in T2; the mission editor drops structure objects just fine, after all, it's how they get into the map in the first place.
#5
Ran your item code above last night Leslie..ran like a champ!!!
03/05/2003 (10:27 am)
I ran into a problem updating the scaling of the player model because the animations wouldn't scale properly (the scale update is not in the player code because of this I believe) and therefore dropped it...I haven't tried realtime staticShape dropins. Will give it a try.Ran your item code above last night Leslie..ran like a champ!!!
#6
I wanted to use this code where the player would need to fill up a hole with a rock so he could get over it but the terrain distances are too large and gives a too big hole, so now I'm stuck with some design isues in my game.. a heck, I actually thought of a simpler game to make now, so I'll finish that first to learn more.
03/07/2003 (2:22 am)
Desmond: This "bool Item::buildPolyList(AbstractPolyList* polyList, const Box3F& box, const SphereF& sphere" wa already like that, I just added the if statement into that function (dont want to mess with the code too much and maybe break something :/ as I'm still new to all of it.I wanted to use this code where the player would need to fill up a hole with a rock so he could get over it but the terrain distances are too large and gives a too big hole, so now I'm stuck with some design isues in my game.. a heck, I actually thought of a simpler game to make now, so I'll finish that first to learn more.
#7
03/07/2003 (7:42 am)
How are you getting "holes" in your terrain? Are you finding "empty squares" in the terrain section of the mission file? If so, set them to "" or simply delete that parameter.
#8
03/10/2003 (10:11 am)
Nha, just pull down a point of terrain, but as the grid is 8x8 or something it is way too big and thus the hole is too wide
#9
I switched over to this version, but with collideable set to off, I wasn't passing through
items.
Here's my difference.. instead of the lines in item.cc :: buildConvex, buildPolyList
change updatePos in player.cc:
03/11/2003 (1:13 am)
I found this resource right after I got done building my version of the same thing. I switched over to this version, but with collideable set to off, I wasn't passing through
items.
Here's my difference.. instead of the lines in item.cc :: buildConvex, buildPolyList
change updatePos in player.cc:
if (pConvex->getObject()->getTypeMask() & sCollisionMoveMask) {
// SQUID
[b]if (pConvex->getObject()->getTypeMask() & ItemObjectType) {
Item* TempItem = static_cast<Item*>(pConvex->getObject());
if (TempItem->isCollideable() == true) {
Box3F convexBox = pConvex->getBoundingBox();
if (wBox.isOverlapped(convexBox)) {
// No need to seperate out the physical zones here, we want those
// to cause a fallthrough as well...
pConvex->getPolyList(&eaPolyList);
}
}
}
else {[/b]
Box3F convexBox = pConvex->getBoundingBox();
if (wBox.isOverlapped(convexBox)) {
// No need to seperate out the physical zones here, we want those
// to cause a fallthrough as well...
pConvex->getPolyList(&eaPolyList);
}
[b]}[/b]
// UN-SQUID
}
pList = pList->wLink.mNext;
#10
03/11/2003 (10:44 am)
cool, I'll check it out later
#11
03/12/2003 (10:22 am)
Joshua, did you mean collideable set to on?
#12
I am new to all of this and have a basic question.
In the function:
function serverCmdpickup(%client)
at the lcall to:
%object = ContainerRayCast(%startPoint, %endPoint, %searchMasks, %player);
I never get an object.
Can anyone help?
Thanks,
Chris
11/08/2004 (10:01 am)
hey guys,I am new to all of this and have a basic question.
In the function:
function serverCmdpickup(%client)
at the lcall to:
%object = ContainerRayCast(%startPoint, %endPoint, %searchMasks, %player);
I never get an object.
Can anyone help?
Thanks,
Chris
#13
02/16/2005 (5:51 pm)
great resource! Got this all working the other night. THere are afew bits and peices though which have to be changed due, I guess, cos of changes in the code base since this was first written. However it was a lot easier to get this working than starting from scratch!
#14
Tony,
What changes did you have to make to get this to work in the recent code?
06/05/2005 (10:13 am)
Great Resource!Tony,
What changes did you have to make to get this to work in the recent code?
#15
07/14/2005 (4:01 pm)
id also like to know what you changed - thanks
#16
The updates to player.cc were made as shown, no changes.
The updates to item.h were made as shown, no changes.
The updates to item.cc were made with some changes, as follows:
- The updates to the buildPolyList method were made as shown, but an additional change was needed. The declaration of the method should now read (changes are in bold):
It is actually shown that way in the resource above, but my version did not already declare the box and sphere variables.
- The next update, adding a cIsCollideable method, needed to be changed. Instead of the code listed (the code that starts with "static bool cIsCollideable"), use this code in its place, around line 924 (changes are in bold):
- The next update, to Item::initPersistFields(), was made as shown, no changes.
- The next update, adding to Item::consoleInit(), is no longer needed.
- The next update, to Item::buildConvex, was made as shown, no changes.
- The update to player.cs was made as shown, no changes.
That
11/01/2005 (8:42 pm)
I seem to have this working with version 1.3. I did so as follows:The updates to player.cc were made as shown, no changes.
The updates to item.h were made as shown, no changes.
The updates to item.cc were made with some changes, as follows:
- The updates to the buildPolyList method were made as shown, but an additional change was needed. The declaration of the method should now read (changes are in bold):
bool Item::buildPolyList(AbstractPolyList* polyList, const Box3F& [b]box[/b], const SphereF& [b]sphere[/b])
It is actually shown that way in the resource above, but my version did not already declare the box and sphere variables.
- The next update, adding a cIsCollideable method, needed to be changed. Instead of the code listed (the code that starts with "static bool cIsCollideable"), use this code in its place, around line 924 (changes are in bold):
[b]
// added a test for collideable
ConsoleMethod(Item, isCollideable, bool, 2, 2, "()"
"Is the object collideable?")
{
return object->isCollideable();
}[/b]
ConsoleMethod(Item, isStatic, bool, 2, 2, "()"
"Is the object static (ie, non-movable)?")
{
return object->isStatic();
}- The next update, to Item::initPersistFields(), was made as shown, no changes.
- The next update, adding to Item::consoleInit(), is no longer needed.
- The next update, to Item::buildConvex, was made as shown, no changes.
- The update to player.cs was made as shown, no changes.
That
#17
05/25/2006 (8:12 pm)
If anyone was wondering this works in TSE Milestone 3 after following Rubes' instructions for integrating this with TGE 1.3. Cool resource, thanks. (yes, I know I'm a few years late ;)
#18
05/25/2006 (8:24 pm)
Good to know, thanks.
#19
10/31/2006 (9:47 am)
I am using 1.5 and I can't get the %searchMask to pickup the ItemObjectType. I can collide with the item, but no object is found when I attempt to pick it up. I changed the %searchMask to PlayerObjectType and the AI Player is found. Also, my items do collide with my actual player when I get near to them, just not with the CastRay Search.
#20
--
Edit: Just had to rework the onCollision() function for that Item and mount an Image.
02/17/2007 (12:47 am)
I'm new to TGE. I wanted to have the character pick-up items, as long as they are colliding with it and press a button. Is there a way to find out what the Player is colliding with, without modifying the engine?--
Edit: Just had to rework the onCollision() function for that Item and mount an Image.

Torque Owner Roger Smith