Game Development Community

One way platforms solved

by Tom Ogburn · in Torque Game Builder · 06/23/2006 (7:47 am) · 20 replies

This does require Core code changes to 2 files: t2dSceneObject.h and t2dSceneObject.cc.

t2dSceneObject.h changes

around line 403
bool                    mCollisionCallback;
    bool                    mCollisionSuppress;
[b]    bool                    mCollisionCancel;[/b]

around line 534
void            setCollisionCircleScale( const F32 scale );
    void            setCollisionSuppress( const bool status );
[b]    void            setCollisionCancel( const bool status );[/b]

around line 630
inline bool                 getCollisionCallback(void) const        { return mCollisionCallback; };
    inline bool                 getCollisionSuppress(void) const        { return mCollisionSuppress; };
[b]    inline bool                 getCollisionCancel(void) const        { return mCollisionCancel; };[/b]


t2dSceneObject.cc changes

around line 495
mCollisionCallback(false),
                                        mCollisionSuppress(false),
[b]                                        mCollisionCancel(false),[/b]

around line 2487 after setCollisionSuppress
[b]//-----------------------------------------------------------------------------
// Set Collision Cancel.
//-----------------------------------------------------------------------------
ConsoleMethod(t2dSceneObject, setCollisionCancel, void, 3, 3, "(status?) - Sets the collision cancel status.")
{
    // Set Collision Cancel.
    object->setCollisionCancel( dAtob(argv[2]) );
}

// Set Collision Cancel.
void t2dSceneObject::setCollisionCancel( const bool status )
{
    // Collision Cancel.
    mCollisionCancel = status;
}[/b]

around line 7132 in t2dSceneObject::processCollisionStatus
// Check for Script Collision-Callbacks for both Source/Destination.
    // Source...
    if ( pSrcObject->getCollisionCallback() )
    {
        pSrcObject->onScriptCollision( pCollisionStatus );
    }

[b]    if (pSrcObject->getCollisionCancel())
    {
       //Reset Collision Cancel
       pSrcObject->setCollisionCancel(false);
       //Exit the function and do not process this collision
       return;
    }[/b]

Sample script code for a one way platform - highly modified from the old platformer tutorial

function t2dSceneObject::onCollision( %srcObj, %dstObj, %srcRef, %dstRef, %time, %normal, %contactCount, %contacts )
{
   switch (%srcObj.getGraphGroup())
   {
      case $playerGroup:
         switch (%dstObj.getGraphGroup())
         {
            case $backgroundGroup:
               collidePlayerPlatform(%normal);
            case $platformGroup:
		if (getWord(%normal,1) <= 0)
		{
			//We are falling or resting on a platform, so collide
	               collidePlayerPlatform(%normal);
		}
		else if  (getWord(%normal,1) > 0)
		{
			//We are going up, so don't collide if we are jumping and it is a platform
			[b]%srcObj.setCollisionCancel(true);[/b]
		}
		else
		{
//			echo("uncaptured collision normal: " @ %normal);
		}
         }
   }
}

#1
06/23/2006 (7:55 am)
Yeah, I thought about doing something like this, but simpler. Instead of doing a whole new method, I'd just read the return value of the onCollision script method, and if some key stirng is returned (like "noresponse"), then no response will be performed. AFAIK that's very easy to implement, very few changes are needed. The problem is I can't get TGB to compile! :( I'll have to redownload and reinstall VC2005 to see if that's the problem I'm having. Anyway, nice work Tom :)
#2
06/23/2006 (8:02 am)
Cool! Nice work.

But could you derive a new object and then contain the changes to their own source files? It would be much preferable to not have to make changes to files that would later require updating every time TGB is updated. It might also avoid conflicts if other, less considerate resources make changes to the core objects.

I'm going to try and do it all in script somehow, if possible. That makes porting to Mac a snap because there is no recompile necessary.
#3
06/23/2006 (8:25 am)
@Manuel
This was the simplest way I knew how. I wasn't sure how to read a return value from a call out to script. :)

@Josh
I spent a lot of time trying to figure out how to do it in script. The only way I could figure to do it was to disable collisions on the objects in the onCollision and set a callback to re-enable them at some small time in the future. That's actually what I was using, but it is very buggy. There's no good way to know how far in the future to re-enable collisions. Too short and they'll still be moving through the platform, too long and they'll have already passed back through it. I do wish you luck in finding a solution and please share if you do.

As for requiring adding every time TGB is updated, now that it is fully released there shouldn't be as many updates as we've been getting. Also, this is such requested functionality that I'm hoping GG will either add this or another solution.
#4
06/26/2006 (7:23 am)
Thanks , your a life saver !
#5
06/26/2006 (9:00 am)
@Tom: Now that I've been able to compile TGB and try this out I've found a problem with your sample script code. When the player jumps under a platform and surpases it, lands perfectly. But if the player starts falling (the jump force ends) when he's colliding with the platform, weird things happen. Most of the times the player gets teleported over the platform he's colliding with, but ocassionally the program just crashes. Have you thought about how to solve this problem? I guess you have it too, am I wrong?.
#6
06/26/2006 (5:06 pm)
Quote:
I spent a lot of time trying to figure out how to do it in script. The only way I could figure to do it was to disable collisions on the objects in the onCollision and set a callback to re-enable them at some small time in the future. That's actually what I was using, but it is very buggy. There's no good way to know how far in the future to re-enable collisions. Too short and they'll still be moving through the platform, too long and they'll have already passed back through it. I do wish you luck in finding a solution and please share if you do.
ditto.
#7
06/26/2006 (6:15 pm)
Use triggers. Put a trigger around the platform. In onEnter, check if the player is below the trigger. If it is, disable collisions. In onLeave, enable collisions.
#8
06/27/2006 (12:28 am)
AFAIK triggers are limited to rectangular areas, aren't they? Because I'm using this 'one way platforms' for mostly the whole level, which is a tile map, and has also non-rectangular tiles like slopes and the like. It'll be a pain in the ass to cover each tile with a trigger, too, and probably produce a significant performance hit.
#9
06/27/2006 (12:39 am)
Actually I don't see the problem. If you want him to jump through a plattform but correctly fall onto it, there is a quite easy way to do this:

*pseudo code*

OnCollision
If collideWithPlattform
if y-speed > 0
ignore collision // he is moving upward so can't fall on anything at all
else
land on plattform
#10
06/27/2006 (12:46 am)
When falling down you also have to ignore platforms you were already colliding with on the peak of your jump path.. otherwise you get strange collision behaviour. :)
#11
06/27/2006 (10:07 am)
Yeah ok, thats not clearly pointed out.
I assumed that for something like a plattformer, you would keep a stack or something similar to take care of potential plattforms below you.
#12
06/27/2006 (10:10 am)
Sorry if you got that wrong... I didnt want to critize your post, I wanted to add a piece of information to it that doesn't seem very obvious if you first think about it. I just remember that from experience when I tried to tackle with that problem. :)

And to solve this I would just do a collision cast over a small amount of time when starting to fall down. :)
#13
06/27/2006 (10:21 am)
Quote:
AFAIK triggers are limited to rectangular areas, aren't they?
Nope. Triggers are scene objects just like anything else in a TGB scene. Thus, they can define a custom collision poly.

Quote:
It'll be a pain in the ass to cover each tile with a trigger...
Not really. If you do it procedurally instead of in the level builder it would actually be pretty easy. In the onLevelLoaded method for your tile layer, iterate over the tiles and create a trigger over each one that has collision enabled. You could get fancy with a little extra work and contain connected tiles with collision in one trigger.

There will be a performance hit because there is more objects in the collision system, but it will not be "significant." TGB does a good job of quickly culling objects that won't be involved in particular collisions. Also, triggers completely skip the collision resolution step.
#14
06/27/2006 (1:21 pm)
Hi thanks for clearing that up , could you prob. post some sample code or send us in the right direction with regards to documentation on triggers ?

Thanks
Le Roi
#15
06/27/2006 (1:31 pm)
I second Le Roi. I'd love to see this working in code not only in ideas...
#16
06/28/2006 (12:23 am)
Ok, I've resolved the falling-when-still-colliding-with-platform thing, pretty easily. Adding a couple of boolean members to my player object, "colliding" and "collidingOld". I know, pretty bad names.

On a Player::update() method called by t2dSceneGraph::onUpdateScene, I do:

if (!%this.collidingOld)
%this.colliding = false;
%this.collidingOld = false;

Then, in Player::onCollide(...) I do:

function Player::onCollide(..)
{
// prerequisite: anything the player collides with, is a "platform", part of the level
%this.collidingOld = true;
if (getWord(%collisionNormal, 1) >= 0 || %this.colliding)
{
%this.colliding = true;
%this.setCollisionCancel(true);
return;
}

... rest if the code ...
}
#17
06/28/2006 (2:49 am)
THANKS !!! - IT WORKS LIKE A BOMB !!! , I'll try the triggers out as well , but for now this works !!!

Quick Question - When the player is jumping and he collides with the left or right side of the platform , how can I check and disable collisions for that as well ?

Thanks
Le Roi
#18
06/28/2006 (9:10 pm)
Alright, resource posted on TDN on how to use triggers as a script only solution to this problem. Link here.

I should add, I am not suggesting that this is a better solution than Tom's. His is obviously much more elegant. But it is the best script only implementation I can think of.

I don't have time at the moment to write up a decent trigger doc, but take a look at the code in this resource, and you'll see, they're pretty simple to use.
#19
06/29/2006 (12:18 am)
Thanks Adam , checking it out now
#20
06/29/2006 (2:12 pm)
I can't seem to get the triggers to work on my tilelayer that I extract from a tilemap proceduraly , anyways , has anyone figured out how to get it to work if I jump and collide with the platform from the right ,

getWord(%normal,0) <0 works but then I fall through the base layer ?

Thanks
Le Roi