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
#22
09/12/2007 (1:34 pm)
Once you've throw an object, is there anyway for that object to make a sound when it collides with another object/terrain and sound appropriate for the given material?
#23
09/27/2007 (3:24 pm)
I noticed that I could pick up items through walls. Here's my fix:... // Search for anything that is selectable; below are some examples
%searchMasks = (
$TypeMasks::ItemObjectType |
$TypeMasks::StaticShapeObjectType |
[b]// Make sure we aren't selecting through walls
$TypeMasks::TerrainObjectType |
$TypeMasks::InteriorObjectType[/b]
);
%object = ContainerRayCast(%startPoint, %endPoint, %searchMasks, %player);
if (isObject(%object))
{
[b]if ("Item" $= %object.getClassName())[/b]
{
%player.selectedObject = firstWord(%object);
%client.setSelectedObject(%player.selectedObject);
return;
}
}
%player.selectedObject = 0;
%client.clearSelectedObject();
#24
Got rid of that error by adding "bool mCollideable;" at line 71. But nothing happens now.
12/03/2007 (10:02 am)
../engine\game/item.h(139) : error C2065: 'mCollideable' : undeclared identifierGot rid of that error by adding "bool mCollideable;" at line 71. But nothing happens now.
#25
mCollideable is a typo. Re-name all instances of it to mCollidable (or mCollideable, either way, they need to be referencing the same variable).
Hope this helps! Great resource, even though I'm still trying to get this to work lol.
03/13/2008 (7:08 pm)
Maddermadcat:mCollideable is a typo. Re-name all instances of it to mCollidable (or mCollideable, either way, they need to be referencing the same variable).
Hope this helps! Great resource, even though I'm still trying to get this to work lol.
#26
11/10/2011 (8:00 am)
With a little wrenching I got this working with T3D 1.1 Final.
#27
in scripts/server/player.cs Armor::onCollision add
11/10/2011 (8:55 am)
Here is a bad, but fun, example of using the collision:in scripts/server/player.cs Armor::onCollision add
//Was old "Try and pickup all items"
if (%col.getClassName() $= "Item")
{
%vpos = %col.getWorldBoxCenter();
%pushDirection = VectorSub(%vpos,%obj.getWorldBoxCenter());
%pushDirection = VectorNormalize(%pushDirection);
%impulse = 12.0;
%mass = %col.getDataBlock().mass;
%pushVec = VectorScale(%pushDirection,%impulse * %mass);
%col.applyImpulse(%vpos, %pushVec);
return;
} 
Torque 3D Owner Chad Kilgore