-
Float Pushout
Hi
Im trying to create a 'Pushout Function' for a platform game engine.
Let me explain...
INDEX
- EXAMPLE
- THE CURRENT METHOD
- THE CURRENT METHOD: EFFICIENCY?
- THE CURRENT METHOD: PRECISSION?
- THE CURRENT METHOD: CONCLUSION
- THE NEW METHOD
- THE NEW METHOD: HOW IT WORKS
- THE NEW METHOD: RESULTS
- THE NEW METHOD: PROBLEM/ PRECISSION
EXAMPLE
https://dl.dropboxusercontent.com/u/...outExample.PNG
The example am i going to use throughout this whole post is this.
- You have a Player Object
- Player is moved from Point A to Point B
- Player is ( or would be ) stuck inside an Obstacle at Point B ( unless stated otherwise in a specific example in this post )
- Player need to get ‘unstuck’ or pushed out of the Obstacle
It’s ( A ) the process of moving Player from Point A to Point B, and ( B ) the method of pushing the player out of the Obsttacle, which is the focus of this post
THE CURRENT METHOD
https://dl.dropboxusercontent.com/u/...LoopMethod.PNG
The current method ( as far as I know ) for handeling Platform Game collisions, the costumized way, is to:
- First run a 'Fast Loop' that moves the Player ( a pixel at a time ) in the X direction
- Then run a 'Fast Loop' that moves the Player ( a pixel at a time ) in the Y direction.
- If at any point the Player is overlapping an Obstacle during this process, the Player is moved 1 pixel backwards and then the Fast Loop stops ( the Player is now standing right next to, or ontop the Obstacle )
THE CURRENT METHOD: EFFICIENCY?
As the illustration shows, the Player is moved from 'Point A' to 'Point B'.
What is the most effective rout from Point A to Point B, ACB, or AB?
Moving the Player, from Point A, along the X axis ( until he hits XB ) and then moving the Player along the Y axis ( until he hits YB ) is not the most efficient rout from A to B.
The most efficient rout from Point A to Point B is a stright line.
The current method uses more Fast Loops than is required for the move.
THE CURRENT METHOD: PRECISSION?
https://dl.dropboxusercontent.com/u/...Precission.PNG
There is also a “problem” with presision when using the current method.
Example:
Using the current method, Player end up colliding with the right side Obstacle and then drops down to the bottom Obstacle, when if we think about the situation logically, Player should be moving diagonally from A to B bypassing the right side Obstacle completely.
THE CURRENT METHOD: CONCLUSION
Many might say that these small “problems” or “detours” are negleghible and that it will have very little impact in an actuall creation.
Still, the problems are there, and I want to find a more efficient and accurate method.
THE NEW METHOD
https://dl.dropboxusercontent.com/u/...outExample.PNG
What I want to suggest is this NEW method of handeling Obstacle collisions:
- Player is “teleported” from point A to point B, without the use of any Fastloops at all
- Then IF Player is overlapping an Obstacle at point B, it is pushed out ( using Fast Loops ) in the opposite direction ( towards point A, in a straight line ) until it is no longer overlapping an obstacle.
- And of cource, if there are no ovelap/ collision at Point B, there is no need for ANY Fast Loops at all
Here is how it works in detail:
THE NEW METHOD: HOW IT WORKS ( IN 7 STEPS )
STEP 1 ( STORE OLD POSSITION FOR LATER USE )
- Player stores its old X Y coordinates ( Point A, PlayerOldX_ PlayerOldY_ )
STEP 2 ( MOVE PLAYER TO A NEW POSITION )
- Player is moved ( “teleported” ) from Point A to Point B.
STEP 3 ( STORE NEW POSSITION FOR LATER USE )
- Player stores its new X Y coordinates ( PlayerNewX_, PlayerNewY_ )
STEP 4 ( STORE DISTANCE MOVED, FOR EACH DIRECTION )
We store the distance moved for both X and Y ( old X/Y minus new X/Y )
- We will call these distances moved for Delta X and Delta Y
- Delta X and Delta Y will end up being the distances of incremental pushout
STEP 5 ( SCALE DELTA X AND Y, TO GET THE INCREMENTAL PUSHOUT DISTANCES )
DeltaX_ and DeltaY_ needs to, uniformly, get scaled down to the nearest whole pixel.
To do this correctly we need to take into consideration what directional move ( DeltaX_ or DeltaY_ ) was the largest ( for effectiveness in the final pushout method ):
- If DX > DY:
DY = DY / abs( DX )
DX = DX / abs( DX )
- If DX > DY:
DX = DX / abs( DY )
DY = DY / abs( DY )
At this point we have scaled the total movement in the X and Y direction ( uniformly ) down to the nearest whole pixel
- Were gonna use this to incrementally push Player out of the Obstacle exactly 1 pixel for each pushout step ( this for pixel perfect acuracy ) unltill Player is no longer is overlapping the Obstacle
*( if pushout is more than 1 pixel per pushout, Player may end up 1 or more pixels above the Obstacle after the pushout method completes )
*( if pushout if less than 1 pixel per pushout, the pushout method will take an unessesary number of pushouts/ Fast Loops to cpmplete )
STEP 6A ( PUSH PLAYER OUT 1 PIXEL AT A TIME )
With the new Delta X and Y, we can now run the New Pushout ‘Fast Loop’ indefinetly, until Player is no longer overlapping Obstacle
- On loop “PUSHOUT”
-> Set Player X to PlayerNewX_ + DX_ * Loopindex “PUSHOUT”
-> Set Player Y to PlayerNewY_ + DY_ * Loopindex “PUSHOUT”
- If Player is NOT overlapping Obstacle
-> Stop loop “PUSHOUT”
( Player is now right next to Obstacle )
STEP 6B ( SMALL POSITION FIX )
We need to make a small adjustment to STEP 6A since MMF2 Object coordinates are not meant to use decimal coordinates.
When repositioning Player during the pushout method, we need to set it to the Int position like this:
- On loop “PUSHOUT”
-> Set Player X to int( PlayerNewX_ + DX_ * Loopindex “PUSHOUT” )
-> Set Player Y to int( PlayerNewY_ + DY_ * Loopindex “PUSHOUT” )
This makes sure Player only moves, visually, the ‘whole number of pixels’ moved in either direction.
EXAMPLE:
If Object moves 0.9 or -0.9 pixel in a direction, it isn’t ‘visually’ repositioned because it hasn’t yet moved a whole pixel
EXAMPLE:
If Object moves 2.7 or -2.7 pixel in a direction, Player is only repositioned 2 pixels because it hasn’t yet moved the complete 3rd pixel
STEP 7 ( FINAL PROBLEM/ ADJUSTMENT )
There is a final potential problem: How computers handle decimal numbers...
As you may or may not know, computers in general have certain problems with decimal numbers
For example, the number 0.1 is not exacltly 0.1
If you for example have 10, and continuously subtract 0.1, you wont end up at exacly 0.0
This is how we are going to try and reduce this potential root of inaccuracy:
We will say that 1 pixel contains 1,000,l000 sub pixels
- So if we displace Players position, 2 pixels for example, we move it ‘2,000,000 / 1,000,000’ sub pixels instead
- This helps minimize potential inaccuracy, by eliminating decimal digits, due to significant decimal numbers in the pushout calculation
- EXAMPLE: 10,000,000 ( equivelant to 10 ) continuously subtract 1,000,000 ( equivelant to 0,1 ), this does not suffer from the decimal inaccuracy problem, value will eventually reach exactly Zero ( 1,000,000 / 1,000,000 )
THE NEW METHOD: RESULTS
Lets look at the results
Here are 2 .mfa Frames:
- 1 containing the New Method WITHOUT the decimal fix ( excluding STEP 7 )
- 1 containing the New Method WITH the decimal fix ( including STEP 7 )
https://dl.dropboxusercontent.com/u/...ushoutDemo.mfa
THE NEW METHOD: PROBLEM/ PRECISION
We can Obviously see from the .mfa WITHOUT the decimal fix, that Player is displaced a few pixels when being pushed out repeatedly.
From the .mfa WITH the decimal fix, the problem seems to be fixed, but still if you continue to push the Player out of the Obstacle, it is sometimes displaced a pixel or 2, despite the decimal fix
And this ^ is the problem im struggeling with
What is causing this inacurrancy?
Despite all effort to eliminate errors and increase precission, the method still seems to suffer this inconsistency
I know it sounds kinda complicated, but im basically looking for any help as to what the cause of this inconsistency could be
( I have theory that its still suffering from the computers decimal precission issue despite the “fix”, but im not sure )