Game Development Community

Negative Times in castCollision

by Paul Sim · in Torque Game Builder · 01/09/2010 (5:07 am) · 6 replies

Hi, all.

I'm attempting to start work on a pseudo-platformer and have been having some issues with the strings I'm getting from the castCollision function.

I have the player's sprite calling castCollision(0.005) in onUpdate, and when a collision is about to occur with a reasonably vertical normal vector, I have the linear velocity of the player sprite change to zero.

The problem is, when I echo the returned string out, I see that the second word, which should be the time in the future that the collision occurs, is a negative value. The sprite falls a ways through the platform it's supposed to be colliding with before it stops, and my character's sprite is now embedded in the platform.

So, from the looks of it, the engine is reacting to a collision that was supposed to have happened in the past, but was somehow missed by the onUpdate/castCollision call. I haven't heard of this issue with anyone else; does anyone have and ideas of what I may be doing wrong?

Thanks in advance!

#1
01/09/2010 (7:00 am)
I can't say if you did something wrong or if this is a bug with TGB's collision system. There are ways to work around it though - instead of using castCollision, how about trying pickPoint instead? Here is an example of how I scripted a player object walking/falling off a platform.

function PlatformerControlsBehavior::updateMovementX(%this)
{
   %this.owner.setLinearVelocityX((%this.right - %this.left) * %this.horizontalSpeed);
   
   // Here we get a point just below the player object
   %positionX = %this.owner.getPositionX();
   %positionY = %this.owner.getPositionY();
   %sizeY = %this.owner.getSizeY() / 2;
   %pointY = %positionY + %sizeY + 1;
   %point = %positionX SPC %pointY;
   
   // Get the scene graph and then use pick point to find out if a platform is
   // below the player. If there is no platform below the player, gravity is
   // turned on. Note that platforms are all in group 1, objects in any other
   // group are ignored.
   %sceneGraph = sceneWindow2D.getSceneGraph();
   %object = %sceneGraph.pickPoint(%point, bit(1));
   
   if (!isObject(%object))
      %this.owner.setConstantForceY(%this.gravity);

   if (%this.right || %this.left)
      %this.schedule(100, "updateMovementX");
}
#2
01/10/2010 (3:50 am)
Thanks for the response!

I tried the pickPoint function as you suggested, but I still seem to be having something akin to the same problem. The response seems very inconsistent...when my character sprite first drops on to the platform, it will sometimes be hovering a good distance above the platform, and jumps from other distances will cause the block to occasionally fall partially through the platform before stopping.

What's frustrating about this is how inconsistent the system seems to be...if the block was simply hovering above the platform or simply falling some distance through the platform, I could just adjust the speed or offset of the block. Instead, I have a block that seems to never be sure where the platform is, or when the collision should occur, or whether there is an object at the point it is looking at.

I should probably note that I'm not currently using a Behavior to handle this; rather, just for simplicity in early testing stages, I'm using the onUpdate function for the player character's class. Again, is there something that I'm missing or doing wrong?
#3
01/10/2010 (5:09 am)
In general, a negative collision time variable means that you are overlapping with the target object. In your case, I don't know exactly what you are doing, but if you are using castCollision to detect appropriate platforms to stand on, then a negative value may be quite common.
Quote:What's frustrating about this is how inconsistent the system seems to be.
The system tells you what is happening in the world. If you are getting an inconsistent result for a series of operations, then there is a chance that the logic you are employing is causing the inconsistent information. The physics system in TGB isn't meant to expose such fine control to TorqueScript and I suspect that you're trying to do what the system isn't capable of handling.

The Platformer Kit was built to handle platformer type physics, while TGB was not. Thats not to say it isn't possible, its just not as simple as using castCollision to detect platforms ;)
#4
01/10/2010 (10:27 pm)
Heh, thanks for the reality check, Phillip. I'm guessing that I may be expecting the system to perform too many calculations too quickly, so the collisions aren't being detected until, as you say, the objects are overlapping. I'll head back to the drawing board and see if I can figure out a better way to detect standable platforms on the fly. I really appreciate the feedback, thanks!
#5
03/09/2010 (4:42 pm)
All, I thought I'd post the solution I came up with to this problem.

I experimented with using pickPoint instead of castCollision and ended up extending the idea into a pickLine that adjusts its length depending on how fast the character is moving. Right now, it only checks directly below the character's middle point; I plan on extending the check to check a full rectangle below the character and eventually to being able to determine how far on a platform the character is.

In any case, I planned to have this pickLine extend from the bottom of the character's center down some length relative to its speed, then to "snap" the character to the object it found to prevent the character from sinking into the platform or floating above it.

I then had to determine how long to make the line relative to the character's speed. I initially started out with the line length at 1/30 of the character's speed to roughly match the onUpdate function's frequency (1/30 ~ .032, and the onUpdate function is called every 32 ms). Unfortunately, this was enough to make the "snap" visible...the character didn't land on the platform smoothly, but kind of "jerked" down onto it as it neared. It was a tiny problem, but really bothered me thanks to my obsessive nitpicking.

I ended up projecting things into the future a little bit. I combined the pickLine with a schedule function that detected an upcoming collision and then didn't react to it immediately, but allowed the character to continue moving for a frame or two before activating the "snap." The combination has worked beautifully.

Here's something akin to what it looks like in the actual code. The referenced "deadStop" function zeros out linearVelocityY and the constantForceY applied to the object, and the "snapToTop" function takes care of locking the player to the top of the platform:

if(%this.getLinearVelocityY() > 0)
	{
		%lineLength = %this.getLinearVelocityY() / 60;
	
		%centerX = %this.getPositionX();
		%centerY = %this.getPositionY();
		
		%lineYStart = %this.getPositionY() + (%this.getHeight() / 2) + 1;
		%lineYEnd = %lineYStart + %lineLength;	
		
		%pickedLine = %this.SceneGraph.pickLine(%centerX, %lineYStart, %centerX, %lineYEnd);
		
		//if pickLine finds an object, snap the player to the object,
		//turn off the gravity acceleration and set the Y velocity to 0.
		//set airborne flag to false; player has landed on object
		
		if(%pickedLine !$= "")
		{
			//to prevent a "skip" when the block is snapped to the object, 
                        //we schedule the event to happen
			//a little bit in the future, so the path of player appears less interrupted.
			%object = getWord(%pickedLine, 0);		
			%this.schedule(32, "deadStopY", 0);
			%this.schedule(32, "snapToTop", %object);
			%this.airborne = false;
		}		
	}

Thanks for the help, everyone.
#6
03/10/2010 (10:46 am)
Glad to see that you found a good solution. Thanks for sharing.