PlatformJumping: CastCollision Vs. Pick[Line/Rect] Vs. Triggers
by Eric Robinson · in Torque Game Builder · 10/03/2006 (11:35 pm) · 69 replies
Okay, I've looked around through the forums, looked into the script functions, [in some cases] skimmed the source, and spent considerable time pondering the issue of jumping/falling for platformer style games.
Here's what I understand as drawbacks for the three different jumping styles:
1) The CastCollision route. This is the route taken in the TGB MiniPlatformerTutorial. The issue I have with this algorithm [at least as implemented in the tutorial] is that it requires you to call "onUpdateScene()" for movement resulting in at least two calls to castCollision() per frame update (the second call was to fix the bug described and fixed here). I feel like this is very inefficient. I believe that movement should not necessarily require a call to "onUpdateScene()".
2) The Pick[Line/Rect] route. I will admit that I don't fully understand this method. From what I understand, however, it would require the same constant updating as the castCollision route.
3) The Triggers route. I don't remember where I saw it (can't find the forum link) but I read a while back about someone using some triggers to determine how physics works when someone jumps. Essentially you map triggers to all the platforms (or walkable surfaces) somewhat akin to the way OneWayCollision platforms work. When the player is in the trigger (onEnter) you set the constantForceY to zero and walk along the platform. Then, when you leave the trigger (onLeave) by walking off the platform or jumping, then you set the constantForceY to something non-zero. The catch here? You have to have triggers everywhere watching for the player (or anything else in their collision groups). Big levels = lots and lots of triggers requiring collision detection. Is this type of collision worse than the tileLayer collision stuff? If not then one could potentially just shut off collision on the tileLayer-level platforms and then use the collision with the triggers to run on...
So I guess it boils down to this: which is more efficient? The castCollision/pickSomething route or the triggers route? The prior requires constant extraneous collision detection while the latter adds a huge amount of objects to the collision system (though you could cut out a whole lot by turning off collision on the tileLayer).
Thoughts?
Here's what I understand as drawbacks for the three different jumping styles:
1) The CastCollision route. This is the route taken in the TGB MiniPlatformerTutorial. The issue I have with this algorithm [at least as implemented in the tutorial] is that it requires you to call "onUpdateScene()" for movement resulting in at least two calls to castCollision() per frame update (the second call was to fix the bug described and fixed here). I feel like this is very inefficient. I believe that movement should not necessarily require a call to "onUpdateScene()".
2) The Pick[Line/Rect] route. I will admit that I don't fully understand this method. From what I understand, however, it would require the same constant updating as the castCollision route.
3) The Triggers route. I don't remember where I saw it (can't find the forum link) but I read a while back about someone using some triggers to determine how physics works when someone jumps. Essentially you map triggers to all the platforms (or walkable surfaces) somewhat akin to the way OneWayCollision platforms work. When the player is in the trigger (onEnter) you set the constantForceY to zero and walk along the platform. Then, when you leave the trigger (onLeave) by walking off the platform or jumping, then you set the constantForceY to something non-zero. The catch here? You have to have triggers everywhere watching for the player (or anything else in their collision groups). Big levels = lots and lots of triggers requiring collision detection. Is this type of collision worse than the tileLayer collision stuff? If not then one could potentially just shut off collision on the tileLayer-level platforms and then use the collision with the triggers to run on...
So I guess it boils down to this: which is more efficient? The castCollision/pickSomething route or the triggers route? The prior requires constant extraneous collision detection while the latter adds a huge amount of objects to the collision system (though you could cut out a whole lot by turning off collision on the tileLayer).
Thoughts?
#2
The nice thing about the cast collision is that it's going to work no matter what, you can rely on it for any size of platform or any kind of obstruction. In the grand scheme of this this issue is so tiny in terms of a performance impact that it's not worth spending a ton of time trying to optimize.
As programmers we often like to geek out on whats the absolute best performing, totally optimal solution. In many cases however the little thing that we are hyper focusing on really has 0 impact on the final product. The whole point of using TGB is to try and focus on making games not technology. ;)
10/04/2006 (1:46 am)
From what I can tell the physics system is perhaps the most performant code in TGB. Despite a few issues that impede it's usability it's really an impressive system. Every time the scene updates and physics are enabled there are going to be a series of physics operation that are performed. Say those operations total to "n" operations, your two CastCollisions are "n + 2". I really don't think you are going to have any performance problems with two extra collision checks. More then likely if you run into slowdowns from physics they are going to come from having 100's of objects bouncing around your level bumping into one another.The nice thing about the cast collision is that it's going to work no matter what, you can rely on it for any size of platform or any kind of obstruction. In the grand scheme of this this issue is so tiny in terms of a performance impact that it's not worth spending a ton of time trying to optimize.
As programmers we often like to geek out on whats the absolute best performing, totally optimal solution. In many cases however the little thing that we are hyper focusing on really has 0 impact on the final product. The whole point of using TGB is to try and focus on making games not technology. ;)
#3
Edit: Pick also lets you do things like store a list of all the types of platforms you're currently standing on, rather than just the first one that cought a collision with the character.
10/04/2006 (2:07 am)
Personally, I like to avoid wasting resources whenever possible. Aside from that, pick is just easier to code and you don't have to mess with the character's collision or physics settings. You get the exact same end result, but cleaner and faster. Cast is not a bad way, but IMHO pick is better.Edit: Pick also lets you do things like store a list of all the types of platforms you're currently standing on, rather than just the first one that cought a collision with the character.
#4
It does not involve setting custom collision polys or anything like that, that was a solution someone else suggested.
The point I'm trying to make is, Pick or CastCollision, spending a ton of time analyzing the performance of each is a waste of energy. If one is better then the other it is only by the smallest fraction of a millisecond. If your game needs to reclaim this fraction of performance then you are probably pushing TGB beyond what it is capable of.
To someone making a game in TGB if you have something that works, regardless of the implementation and it's not causing any performance problems just keep going, get the rest of your gameplay code and content in there. It's a far better use of your time if your trying to finish a game.
For you Thomas, since you actually work on the technology you darn well better look at ever fraction of a millisecond because what you build will be re-used over and over again by many people. I fully expect it to be rock solid :)
10/04/2006 (2:34 am)
The solution in the miniplatformer tutorial doesn't change the player object collision polygon at all. It uses what ever is set on the object and just uses castCollision to make sure that the LinearVelocityX gets set to 0 right when a collision is about to happen to the left or right (to prevent riding up the walls) and castsCollision straight down to see if it's time to turn off the Constant Y force acting as gravity (to prevent getting stuck to the ground).It does not involve setting custom collision polys or anything like that, that was a solution someone else suggested.
The point I'm trying to make is, Pick or CastCollision, spending a ton of time analyzing the performance of each is a waste of energy. If one is better then the other it is only by the smallest fraction of a millisecond. If your game needs to reclaim this fraction of performance then you are probably pushing TGB beyond what it is capable of.
To someone making a game in TGB if you have something that works, regardless of the implementation and it's not causing any performance problems just keep going, get the rest of your gameplay code and content in there. It's a far better use of your time if your trying to finish a game.
For you Thomas, since you actually work on the technology you darn well better look at ever fraction of a millisecond because what you build will be re-used over and over again by many people. I fully expect it to be rock solid :)
#5
You're right, though. I'm used to thinking of things in terms of 'best way' and for actual game dev, it's just not awlays neccesary. Still, I think with a full enough level, collision checking can be very taxing and if you're not wary of some of the things I mentioned above, or just performance in general, it can come back to haunt you later on.
10/04/2006 (4:05 am)
You've obviously never seen my code. If you had you wouldn't be expecting 'rock' solid so much as maybe 'apple' or 'chicken pot pie' solid. ;)You're right, though. I'm used to thinking of things in terms of 'best way' and for actual game dev, it's just not awlays neccesary. Still, I think with a full enough level, collision checking can be very taxing and if you're not wary of some of the things I mentioned above, or just performance in general, it can come back to haunt you later on.
#6
@Tom:
To set up the triggers, call a function on onLevelLoad that places the triggers over all the platforms (as you mentioned for the single sceneObjects used to replace consecutive tiles) and, perhaps, offsets them from the triggers slightly so that the constant force can be killed prior to actually touching the platforms (potentially saving processing power?).
Why use triggers? Because you can't ask an object when it stops colliding (jump or fall). The only downside (and the one I'm trying to understand the consequences of) is: how big of a hit do we take on performance for triggers? As far as I understand it, triggers use the collision system to detect things. Adding this would double the objects collided with in the level (naively 2n, where n is the number of platforms - assume tiles) unless you shut off collision with the underlying tiles (which, on second thought, could be a really bad idea - what if you have a player bumping into the underside of those tiles...?).
This is actually exactly the way that the miniPlatformerTutorial does the platform stuff. The trade-off here would be constant castCollision calls (minimum of two per scene update, occasionally three) vs object-count and trigger drawbacks (whatever they may be). Thoughts on this method? Seems... fairly solid to me. The only difference is that you don't get the fun physics responses like "BOUNCE" using this method (which would be the standard for basic run-'n-jump platformers, right?). You could but then you'd have to enable the underlying tiles' collision/physics...
I feel better about the pickLine method, though, thanks! I've a few questions, however. PickLine would still require the onSceneUpdate call, correct? Is there a tutorial or anything on how to properly use that? Or even on how to determine the types of objects you've connected with? I remember back in the old Platformer tutorial there was something about platformerGroups in the collision checks but I can't recall exactly how that worked. Guess I could look into it but I remember it not being explained in the tutorial.
@Dan: I understand about the physics system. I just find that my engine occasionally hiccups when I'm running around and I thought that perhaps this was due to the castCollision calls. It also feels... hackish which makes me nervous. It did suffice to get me up and running into prototype mode, though!
As for pushing the engine beyond what it is designed for, I'm nowhere near that. I'm preparing for future code (as outlined in my design doc) that requires a rather precise timing system. Hiccups are that systems worst enemy. I'm using my prototyping time to ensure that the later code won't require crazy updates due to some performance catch I overlooked.
As for the whole code issue... I'm really looking forward to the next major code revision. Particularly the updated audio sub-system I hear is in the works. But there have apparently been a bunch of physics / collision fixes that will make our lives a bit more sane in the long run. I really wish there were a place to see what GG was currently working on... wait, is there?
Edited: changed "velocity" to "force" where applicable.
10/04/2006 (5:27 pm)
Wow. Thanks for the responses. My responses:@Tom:
Quote:3) I'm gonna debunk this one right off the bat. This is way counter-intuitive. Aside from that, I can't immagine this working in any sort of way that feels comfortable or looks correct. As for colliding directly with the triggers, you can't get physics responses from triggers. Even if you could, it would be no different from standing directly on a tile layer or scene object. In other words, not a viable sollution.I'd like to clarify something about this method. When I say 'walk on the triggers' I merely mean "turn off constant Y force." This does not shut off universal gravity and it does not take much processor speed at all. In essence your functions would look thusly:
// Shut off the constant force and stop the player from falling (nullify gravity for this object alone).
// Only use the setLinearVelocity line if you want the player to clamp to the platform on landing.
// Otherwise, rely on the physics responses of underlying tileLayer.
platformTrig::onEnter(%this)
{
%this.setLinearVelocityY(0);
%this.setConstantForceY(0);
}
// We're either jumping or falling. Turn on the constant force!
platformTrig::onLeave(%this)
{
%this.setConstantForceY($gravityConstant);
}See, you don't need to use physics responses. You simply set the constant vertical velocity to zero for the object that has contacted the platform. Further, you could actually do some quick coding to kill all collision checks with platforms below you (the exception being for non-level surfaces): offset the objects from the platform by a tiny amount, not perceivable to the player.To set up the triggers, call a function on onLevelLoad that places the triggers over all the platforms (as you mentioned for the single sceneObjects used to replace consecutive tiles) and, perhaps, offsets them from the triggers slightly so that the constant force can be killed prior to actually touching the platforms (potentially saving processing power?).
Why use triggers? Because you can't ask an object when it stops colliding (jump or fall). The only downside (and the one I'm trying to understand the consequences of) is: how big of a hit do we take on performance for triggers? As far as I understand it, triggers use the collision system to detect things. Adding this would double the objects collided with in the level (naively 2n, where n is the number of platforms - assume tiles) unless you shut off collision with the underlying tiles (which, on second thought, could be a really bad idea - what if you have a player bumping into the underside of those tiles...?).
This is actually exactly the way that the miniPlatformerTutorial does the platform stuff. The trade-off here would be constant castCollision calls (minimum of two per scene update, occasionally three) vs object-count and trigger drawbacks (whatever they may be). Thoughts on this method? Seems... fairly solid to me. The only difference is that you don't get the fun physics responses like "BOUNCE" using this method (which would be the standard for basic run-'n-jump platformers, right?). You could but then you'd have to enable the underlying tiles' collision/physics...
I feel better about the pickLine method, though, thanks! I've a few questions, however. PickLine would still require the onSceneUpdate call, correct? Is there a tutorial or anything on how to properly use that? Or even on how to determine the types of objects you've connected with? I remember back in the old Platformer tutorial there was something about platformerGroups in the collision checks but I can't recall exactly how that worked. Guess I could look into it but I remember it not being explained in the tutorial.
@Dan: I understand about the physics system. I just find that my engine occasionally hiccups when I'm running around and I thought that perhaps this was due to the castCollision calls. It also feels... hackish which makes me nervous. It did suffice to get me up and running into prototype mode, though!
Quote:As programmers we often like to geek out on whats the absolute best performing, totally optimal solution. In many cases however the little thing that we are hyper focusing on really has 0 impact on the final product. The whole point of using TGB is to try and focus on making games not technology. ;)I understand that. The issue is that I would really like to A) make the code as streamlined as possible as I'm aiming for all three architectures as well as ancient systems, and B) make the code readable for my artists, musicians etc. Inserting code to the main game loop can be confusing. In order to use the castCollision function we have to temporarily store the characters attributes, imbue special testing attributes on it, test, and then restore the attributes. Twice. Per scene update. It certainly works... but takes a second to grasp.
As for pushing the engine beyond what it is designed for, I'm nowhere near that. I'm preparing for future code (as outlined in my design doc) that requires a rather precise timing system. Hiccups are that systems worst enemy. I'm using my prototyping time to ensure that the later code won't require crazy updates due to some performance catch I overlooked.
As for the whole code issue... I'm really looking forward to the next major code revision. Particularly the updated audio sub-system I hear is in the works. But there have apparently been a bunch of physics / collision fixes that will make our lives a bit more sane in the long run. I really wish there were a place to see what GG was currently working on... wait, is there?
Edited: changed "velocity" to "force" where applicable.
#7
And by the way, the fixes arent so much fixes as complete changes in the underlying system and they are no small thing. I think you'll be very happy, along with anyone who wants more precise physics. =)
10/05/2006 (11:27 am)
I'll see if I can dig up an example of how to use pick to ground check for you. And by the way, the fixes arent so much fixes as complete changes in the underlying system and they are no small thing. I think you'll be very happy, along with anyone who wants more precise physics. =)
#8
And then the usage:
Notice the updateOnGround flag being set to true here. This is the only place you would do that. Anywhere else you would just call isOnGround. This keeps you from wasting resources by doing extraneous calculations.
Let me know if you have any trouble with it.
10/05/2006 (12:18 pm)
Ok, here's an example of a ground check function that uses pick. It also uses a bitmask that defines specific types of game-specific objects.//---------------------------------------------------------------------------------------------
// Actor::isOnGround
// Determines whether or not the actor is on the ground.
//---------------------------------------------------------------------------------------------
function Actor::isOnGround( %this )
{
// Cache the result of the function so it is only called once per frame.
if( !%this.updateOnGround )
return %this.onGround;
%this.updateOnGround = false;
// Definitely not on the ground if the actor is moving upward.
if( %this.getLinearVelocityY() < -0.1 )
{
%this.onGround = false;
%this.groundSurface = "";
return false;
}
// Pick a line across the feet of the actor.
%boundsY = getWord( %this.collisionBounds, 3 );
%leftX = getWord( %this.collisionBounds, 0 );
%rightX = getWord( %this.collisionBounds, 2 );
// flip the line if character is flipped
if( %this.getFlipX() )
{
%leftX = -%leftX;
%rightX = -%rightX;
}
// get world points
%left = %this.getWorldPoint( %leftX, %boundsY );
%right = %this.getWorldPoint( %rightX, %boundsY );
// reduce points from collision edges
%leftX = getWord( %left, 0 );
%rightX = getWord( %right, 0 );
%bottom = getWord( %left, 1 ) + 0.02;
%edgeWidth = ( %rightX - %leftX ) / 20;
%leftX += %edgeWidth;
%rightX -= %edgeWidth;
%left = %leftX SPC %bottom;
%right = %rightX SPC %bottom;
// pick the line
%objList = %this.getSceneGraph().pickLine( %left, %right, -1, -1, false, %this );
// Look for a platform in the pick list.
%count = getWordCount( %objList );
for( %i = 0; %i < %count; %i++ )
{
%currObj = getWord( %objList, %i );
if( %currObj.typeMask & $Game::ObjectType::Platform )
{
// Found a tile layer, so look for a tile with collision active.
if( %currObj.getClassName() $= "t2dTileLayer" )
{
%bottomCenter = %this.getWorldPoint( 0, %boundsY );
%tile = %currObj.pickTile( %bottomCenter );
%collision = %currObj.getTileCollision( %tile );
if( getWord( %collision, 0 ) )
{
%this.onGround = true;
%this.groundSurface = %currObj;
return true;
}
}
// Found a regular platform, so we're on the ground.
else
{
%this.onGround = true;
%this.groundSurface = %currObj;
return true;
}
}
}
// Found nothing. Not on the ground.
%this.onGround = false;
%this.groundSurface = "";
return false;
}And then the usage:
//---------------------------------------------------------------------------------------------
// Actor::update
// Called by the scenegraph on any Actor registered with the scene to update movement.
//---------------------------------------------------------------------------------------------
function Actor::update( %this )
{
...
// Update the onGround status of the actor
%this.updateOnGround = true;
%onGround = %this.isOnGround();
...Notice the updateOnGround flag being set to true here. This is the only place you would do that. Anywhere else you would just call isOnGround. This keeps you from wasting resources by doing extraneous calculations.
Let me know if you have any trouble with it.
#9
And here is the caching bit from Actor's onLevelLoaded callback:
10/05/2006 (12:23 pm)
Oops, I almost forgot. This uses cached collision bounds on each actor to save time while the game is running. Here is the helper function to get an object's collision bounds://---------------------------------------------------------------------------------------------
// t2dSceneObject::getCollisionBounds
// Get the bounding rect for an object's collision poly.
//---------------------------------------------------------------------------------------------
function t2dSceneObject::getCollisionBounds( %this )
{
%polylist = %this.getCollisionPoly();
%polyCount = getWordCount( %polylist ) / 2;
// Initialize the min and max values to the first point.
%firstX = getWord( %polyList, 0 );
%firstY = getWord( %polyList, 1 );
%polyMinX = %firstX;
%polyMaxX = %firstX;
%polyMinY = %firstY;
%polyMaxY = %firstY;
// Iterate over all the poly points, saving the min and max values.
for( %i = 1; %i < %polyCount; %i++ )
{
%thisX = getWord( %polyList, %i * 2 );
%thisY = getWord( %polyList, ( %i * 2 ) + 1 );
if( %thisX > %polyMaxX )
%polyMaxX = %thisX;
else if( %thisX < %polyMinX )
%polyMinX = %thisX;
if( %thisY > %polyMaxY )
%polyMaxY = %thisY;
else if( %thisY < %polyMinY )
%polyMinY = %thisY;
}
// Return the min and max values.
return %polyMinX SPC %polyMinY SPC %polyMaxX SPC %polyMaxY;
}And here is the caching bit from Actor's onLevelLoaded callback:
//---------------------------------------------------------------------------------------------
// Actor::onLevelLoaded
//---------------------------------------------------------------------------------------------
function Actor::onLevelLoaded( %this, %scenegraph )
{
// Grab the local collision poly bounds of the object. This is useful for querying whether
// or not the actor is colliding with certain things or where it is colliding.
%this.collisionBounds = %this.getCollisionBounds();
#10
But yeah. What do you think of the trigger setup with onEnter and onLeave set up? With that you could enable physics reception and not have to play with sceneUpdate()...
10/05/2006 (10:16 pm)
Cool! That looks awesome. I can see how you can use this without having to update every single frame (as long as you don't have the player receiving physics that could possibly bounce it around without player interaction)... which feels nice.But yeah. What do you think of the trigger setup with onEnter and onLeave set up? With that you could enable physics reception and not have to play with sceneUpdate()...
#11
At the risk of repeating myself,
The onUpdateScene callback exists to allow you to do things like this.
I said earlier that I think turning off gravity is a bad idea and spawning triggers is a bad idea, castCollision is ok, and pick is my favorite, and now you're asking me my opinion on using triggers to turn gravity off.
I hope this doesn't sound rude, I just honestly can't think of any other way to explain my thoughts on the topic other than what I already said.
Edit: SP
10/06/2006 (3:27 am)
It's like I said before, just because you're not doing anything that feels like it could be a hard performance hit doesn't mean you aren't. Having all platforms spawn triggers to let the player know that they are on the ground is far less efficient. The only reason it was suggested for one-way platforms is that it is possibly the only way to get one-way platforms to work without scratching at the source for several minutes. First, scene objects have a high overhead. You are using up memory that you don't need. Second, why work around something when there is a natural sollution that answers your exact question?At the risk of repeating myself,
Quote:
I'm gonna go ahead and say that Turning gravity off in general is a bad idea and pretty much only good for giving yourself a headache. I've seen it mentioned around and I've tried it out in several different ways and I strongly suggest against wasting your time trying to get it to work.
The onUpdateScene callback exists to allow you to do things like this.
I said earlier that I think turning off gravity is a bad idea and spawning triggers is a bad idea, castCollision is ok, and pick is my favorite, and now you're asking me my opinion on using triggers to turn gravity off.
I hope this doesn't sound rude, I just honestly can't think of any other way to explain my thoughts on the topic other than what I already said.
Edit: SP
#12
Why isn't there a variable "isColliding"? (There's got to be a hit related to having a trigger, right? They must add a poll every frame regardless of potential collisions...? How are they different?)
As for why you had to repeat yourself... I guess I just misinterpreted your previous comments. I thought you hadn't quite understood my version of the trigger implementation. Looks like I was mistaken ;D
When I say turning off gravity, I mean it as it is implemented in the miniPlatformerTutorial with a call to "setConstantForceY = 0". Do you recommend leaving that out altogether? I had thought it was a necessary part of making things work...
10/06/2006 (7:33 pm)
No, I don't think it's rude. I'd like to thank you for answering my question! I've been told in the past that castCollision and pickLine are faster than the triggers route. The thing is that I don't understand the why. Saying that SceneObjects bring a huge memory hit is good incentive to not use triggers. On the same token, I'd really like an explanation of how triggers work versus just collision. They rely on collision to determine the triggering, right? I checked in the code a bit and there's mention of turning on a callback of sorts... What I want to know is:Why isn't there a variable "isColliding"? (There's got to be a hit related to having a trigger, right? They must add a poll every frame regardless of potential collisions...? How are they different?)
As for why you had to repeat yourself... I guess I just misinterpreted your previous comments. I thought you hadn't quite understood my version of the trigger implementation. Looks like I was mistaken ;D
When I say turning off gravity, I mean it as it is implemented in the miniPlatformerTutorial with a call to "setConstantForceY = 0". Do you recommend leaving that out altogether? I had thought it was a necessary part of making things work...
#13
Right, you don't have to do that, even in slopes, setlinearvelocityY(0) on oncollision callback is enough to do the trick.
10/07/2006 (12:09 am)
Quote:I mean it as it is implemented in the miniPlatformerTutorial with a call to "setConstantForceY = 0". Do you recommend leaving that out altogether?
Right, you don't have to do that, even in slopes, setlinearvelocityY(0) on oncollision callback is enough to do the trick.
#14
@Eric - Triggers have an onStay callback that you have to explicitly enable by calling a function on that trigger (similar to the onFrameChange callback for t2dAnimatedSprites). This is called every scene update that something is in the trigger. You have to specifically enable it because, just like the onFrameChange callback, it is easy to do something in the onStay callback that will completely destroy your performance because it happens every frame.
10/07/2006 (2:55 am)
@Benjamin - Actually, that's not entirely true. What you wind up with is one frame's worth of your constant force every other frame which can cause you to sort of slide down a slope. This is due mostly to quirks in the physics system (meaning, it wasn't designed specifically for platformers, but rather as a sort of generic physics system). I know for a fact there are ways around it using only script because a co-worker has told me that they have done it, but I don't personally know how they did it. If all goes well, these types of problems will be a thing of the past. The TGB dev team is really going above and beyond in maintaining and upgrading the engine to help fill the community's needs. @Eric - Triggers have an onStay callback that you have to explicitly enable by calling a function on that trigger (similar to the onFrameChange callback for t2dAnimatedSprites). This is called every scene update that something is in the trigger. You have to specifically enable it because, just like the onFrameChange callback, it is easy to do something in the onStay callback that will completely destroy your performance because it happens every frame.
#15
Interesting code.. you say turning off gravity is a bad idea. I share that oppinion because gravity "acts" always and turning it off surely feels hacky. With your pickline method however, do you experience any velocity slowdowns due to friction against the ground or walls when your character moves? I had this happening in a prototype before when I just used the standard physics system with CLAMPing and I moved my character against a wall and jumped. Also those phenomenons seem to appear only if you use a custom collision polygon, not the standard full box.
10/08/2006 (11:12 am)
@ThomasInteresting code.. you say turning off gravity is a bad idea. I share that oppinion because gravity "acts" always and turning it off surely feels hacky. With your pickline method however, do you experience any velocity slowdowns due to friction against the ground or walls when your character moves? I had this happening in a prototype before when I just used the standard physics system with CLAMPing and I moved my character against a wall and jumped. Also those phenomenons seem to appear only if you use a custom collision polygon, not the standard full box.
#16
@Oliver - I've done some playing with the code. It appears that my issue (chronicled further down in the thread) really does stem from a bug in the code. I know that you've experienced the same problem as me [as you started the thread I just linked ;)]. Here's the fun part, though: the physics issues you mentioned above have been fixed for 1.1.2. I tested the code a bit to see if it really was related to set/getLinearVelocityY(). I explicitly set the constant force and force multiplier, left the physics on CLAMP, and commented out the following code:
Oh yeah, and I've a custom polygon on my character (though he does sport a perfectly horizontal line underneath him).
My next step is to use the pickLine method for jumping. I rather feel like leaving it the way it is (with just one castCollision whenever I want to jump) as that code is extremely simple and fairly clear. However, with the current state of [apparently] set/getLinearVelocityY, I simply can't go that way. PickLine is independent from the velocity of the character so it should suffice. We'll see how it goes!
10/08/2006 (6:57 pm)
@Thomas - Yeah, I figured that the onStay callback was pretty rough on the system. I played around with that for one of my games systems but worked out a better algorithm that just used onEnter and onLeave (this is for a trigger with a completely separate purpose from the platforms). However, the question I had was how onLeave works. I figure that onEnter works almost [if not] exactly like a normal onCollision. OnLeave, however, is not part of the normal collision system... at least not one that's revealed in script and any function that I can see in the source. How exactly does the engine handle onLeave callbacks? Anyone know how it works? Does it have to poll every frame where there's movement or something?@Oliver - I've done some playing with the code. It appears that my issue (chronicled further down in the thread) really does stem from a bug in the code. I know that you've experienced the same problem as me [as you started the thread I just linked ;)]. Here's the fun part, though: the physics issues you mentioned above have been fixed for 1.1.2. I tested the code a bit to see if it really was related to set/getLinearVelocityY(). I explicitly set the constant force and force multiplier, left the physics on CLAMP, and commented out the following code:
%collision = $pGuy.castCollision(0.005);
if(%collision !$= "" )
{
$pGuy.setLinearVelocityX(0);
}
$pGuy.setLinearVelocityY(100);
%collision = $pGuy.castCollision(0.005);
$pGuy.setLinearVelocityY(%yVelocity );
if(%collision $= "")
{
[b]$pGuy.setConstantForceY(100); // Changing the physics[/b]
}
else
{
[b]$pGuy.setConstantForceY(0); // Changing the physics[/b]
}That resulted in me being able to run around as I would expect without getting stuck (even when falling from one platform to the next). No tunneling, erratic behavior, or anything! (Though my test level does not currently contain a slope... couldn't test for that explicitly.) The oddities came back when I would jump as the jump code requires a call to set/getLinearVelocityY() (which also calls 'castCollision' though... hmm... maybe it's actually a problem with castCollision???):function playerJump()
{
[b]%yVelocity = $pGuy.getLinearVelocityY();
%xVelocity = $pGuy.getLinearVelocityX();
$pGuy.setLinearVelocityY(100);
$pGuy.setLinearVelocityX(0);[/b]
%collision = $pGuy.castCollision(0.005);
if(!(%collision $= ""))
{
[b]$pGuy.setLinearVelocityY(-225);[/b]
}
else
{
[b]$pGuy.setLinearVelocityY(%yVelocity);[/b]
}
[b]$pGuy.setLinearVelocityX(%xVelocity);[/b]
}So it appears that most of the issues related to having gravity constantly on are gone. I couldn't test everything but was very impressed with what I saw when just leaving it on.Oh yeah, and I've a custom polygon on my character (though he does sport a perfectly horizontal line underneath him).
My next step is to use the pickLine method for jumping. I rather feel like leaving it the way it is (with just one castCollision whenever I want to jump) as that code is extremely simple and fairly clear. However, with the current state of [apparently] set/getLinearVelocityY, I simply can't go that way. PickLine is independent from the velocity of the character so it should suffice. We'll see how it goes!
#17
You can go without pickLine and castCollision though if I remember right... back on T2D 1.0.2 I made a plattformer but I don't quite remember how I have done it. I think I checked the collision normal and when I wanted to jump I set a flag. Then in the update function I checked both and decided if the player may jump or not. It sounds a bit hacky aswell but it worked :)
10/08/2006 (10:33 pm)
Thanks for the reply, Eric! I will try it myself with "raw physics" and no hacks today. :)You can go without pickLine and castCollision though if I remember right... back on T2D 1.0.2 I made a plattformer but I don't quite remember how I have done it. I think I checked the collision normal and when I wanted to jump I set a flag. Then in the update function I checked both and decided if the player may jump or not. It sounds a bit hacky aswell but it worked :)
#18
could you please explain more??
10/09/2006 (1:11 am)
Eric I don't understand all your post what did you comment exactly in your script ubove to solve the problem?? could you please explain more??
#19
I just tried commenting out the castCollision stuff and use pure clamping to handle everything(except the jumping stuff as you have it aswell). I use a custom collision polygon now and everything works almost good. Just sometimes I cannot jump anymore, the animation switches between stand and fall if I collide with both ground and side and the animation changes to side when one edge of my player is in midair and one i still on the ground.
10/09/2006 (4:26 am)
@Eric,I just tried commenting out the castCollision stuff and use pure clamping to handle everything(except the jumping stuff as you have it aswell). I use a custom collision polygon now and everything works almost good. Just sometimes I cannot jump anymore, the animation switches between stand and fall if I collide with both ground and side and the animation changes to side when one edge of my player is in midair and one i still on the ground.
#20
10/09/2006 (1:59 pm)
I did some more testing. In the update function I echo'ed the %yVelocity to the console. For some reason, when I am standing on an edge with my character I get values greater than zero which causes the character to change to the jumpDown animation. The values get as big as 7 while walking.. I have no idea how to stop this from happening... I guess it has to do with keeping the gravity activated the whole time, even when the character is on the ground.
Torque Owner Thomas Buscaglia
2) This is similar to the first method, except you don't have to modify anything on the actual characters. It requires only one call per frame and you can set the mask on the pick so you only get platforms without having to worry about setting anything back afterwards.
3) I'm gonna debunk this one right off the bat. This is way counter-intuitive. Aside from that, I can't immagine this working in any sort of way that feels comfortable or looks correct. As for colliding directly with the triggers, you can't get physics responses from triggers. Even if you could, it would be no different from standing directly on a tile layer or scene object. In other words, not a viable sollution. While I'm at it, I'm gonna go ahead and say that Turning gravity off in general is a bad idea and pretty much only good for giving yourself a headache. I've seen it mentioned around and I've tried it out in several different ways and I strongly suggest against wasting your time trying to get it to work.
Keep in mind that just because you aren't explicitly checking for a collision with cast or pick, more objects will still slow down the system. I haven't looked at the source for the collision system at all, but I assume it would have to be at least O(n^2). In that case only checking for one extra collision per frame where you need it rather than one extra collision per frame on all platforms would always be more efficient.
Between 1 and 2, I choose 2. I tried both and 2 not only worked better, but it was easier to code. It just makes sense: you want to know if there is ground beneath your feet... so check for ground beneath your feet!
I use pickLine since it's really all you need unless you decide to have platforms with obnoxiously short collision polygons for some reason. Picks work exactly like collisions, so the fewer verts in the poly, the easier the collision system will be able to check against it. If you set up your collision groups intelligently you can just pass the pickLine call a mask with only your different platform groups to make the pick faster.
You can further speed up the process by adding an onLevelLoaded to your platform tile layers that detects consecutive connected platform tiles and spawns a single scene object for each platform series before turning collision off on those tiles. The fewer objects you have to check against, the more efficient it will be. Suprisingly, this process can be very fast (read: an unnoticeable difference in load time).
This is the best method I've used, but there are certain catches. Because picks return a list of objects that you have to check, if you don't do some of the things listed above to make sure it's efficient (especially setting the masks!) and your levels are packed with objects it can potentially get slow if you have tons of characters standing around. I've had up to about 50 characters on one level and didn't notice any slowdowns* (these all also had very basic AI and animation state machines running). That isn't to say that I tried more and it chunked down, that was just the greatest ammount I tried. I assume you would have to get into the hundreds of characters before you saw any considerable decline in performance on an average machine. It's just not a very expensive process if you do it right.
Also, you want to make sure you're not picking more than once per frame. You know the character hasn't moved, so cache the response and just return that next time there is a request for a ground check before the next frame. Since there are potentially plenty of times when you will need to know if the player is on the ground more than once per frame this is an area where you can waste a lot of resources if you're not careful.
Edit: SP