2
votes

Background: I've been doing a lot of studying in xna lately. I've been perfecting my game structure and creating essential classes for an average game, read a couple articles, watched a number of videos, and read bits of a book I have. So, after creating a good game structure I moved on to the actual game, a platformer. I currently have ordinary blocks, and a Player - and the problem is within the player.

Problem/Question: Colliding with blocks is usually fine, but sometimes collisions act strangely. IE: Some how the player is in the block ( normally one pixel in ), and this is a big problem because this could cause the player to be pushed some where across the map.

Player Game Logic: The player's movement is state based. The states are an enumerator of "ground, jump, and fall". Right now, the problem seems to be inside of the falling state. Sometimes the collision works correct, sometimes the player ends up one pixel inside of the block - which means if the player jumps he will be instantly pushed to the first block above the player - no matter how far.

Collision Logic: Inside the fall state the player is pushed towards the floor by gravity. Gravity gets added by the force every frame. Before changing the position, a collision method is called upon checking if there was a collision between any block within it's range of motion. Now, since this is a falling state the player is moving down, so when it collides a while loop pushes the player down by 1pixel every frame until it's meeting the block. And just as a back up, there's a loop that pushes the player out of the block. Then it sets the current state to ground. The jumping state follows the same kind of theory.

Falling State Script: Read above for explanation of this code.

case PlayerState.Fall:
    // falling sprite
        mySpriteManager.mySpriteCurrent = mySpriteManager.mySpriteIndex["PlayerSprites/player_fall"];
    // fall velocity change
        myVelocity.Y += myDforce;

    if ( Collision( otObjectList.wallIndex, myPosition.X + 1, myPosition.Y, myPosition.X + myWidth - 1, myPosition.Y + myHeight + myVelocity.Y) )
    {
        while( !Collision( otObjectList.wallIndex, myPosition.X + 1, myPosition.Y, myPosition.X + myWidth - 1, myPosition.Y + myHeight) )
            myPosition.Y += 1;
        while ( Collision(otObjectList.wallIndex, myPosition.X + 1, myPosition.Y, myPosition.X + myWidth - 1, myPosition.Y + myHeight))
            myPosition.Y -= 1;
        myVelocity.Y = 0;
        myCurrentState = PlayerState.Ground;
    }
    else
    {
            myPosition.Y += myVelocity.Y;
    }
break;

Collision Script: This runs through all solid objects until there is a collision and returns true if there is.

private bool Collision( List<ObjWall> otWalls, float x1, float y1, float x2, float y2)
{
    myBoundingBox.X = (int)Math.Ceiling(x1);
    myBoundingBox.Y = (int)Math.Ceiling(y1);
    myBoundingBox.Width = (int)Math.Ceiling(Math.Abs( x2 - x1));
    myBoundingBox.Height = (int)Math.Ceiling(Math.Abs( y2 - y1));
    foreach (ObjWall wall in otWalls)
    {
        if ( Box.Intersects( wall.Box ) )
        {
            return true;
        }
    }
    return false;
}

Variable Explanations:

myBoundingBox - every normal object builds off this abstract class. So, every object has a bounding rectangle.

Box - is a trait that comes from the abstract object class, it just returns the bounding rectangle.

myVelocity - is the players velocity, this is a Vector2 because floats provide cleaner looking gravity.

myDforce - is a float that changes the yvelocity in a positive force. I believe I should make it a constant.

ObjectList - this is a class that runs all Normal Object logic and contains a list of all the possible objects that can be in a room.

myPosition - This is another protected variable provided by the abstract class that normal objects are driven from. This is a vector 2, so it works using Floats.

For any more explanation in the code please ask! In advance, thanks for your time and Help. Cay.

1
are your Y positions not integers at any point ? if so you may be having truncation issues..Oren
No, They are floats - and are changed by .5 ( default value of myDforce ) every frame during the fall state - or jump state. So, how would I be able to make the numbers precise as possible( fix the truncation)? Also does casting change the number? I assume it does if it's truncated. IE (cast)4.4f would be 4?Cayman
When you "one pixel in", is it an X or Y pixel? Also, what does myPosition.X and myPosition.Y represent on the player? (i.e. top left/bottom left/bottom right etc.)thudbutt
One pixel in - for the falling state, which is one pixel in on the bottom of the rectangle along the y access. Myposition has been added to variable explanations. Thank you, Cay.Cayman
the first step would be to add some prints/use a debugger to check that truncation is indeed the issue here. once you isolate the problematic area you can think of ways to fix it. (maybe change the design to work with ints as much as possible to avoid the problem completly).Oren

1 Answers

0
votes

Your 2nd call to Collision currently only checks the current position and then moves the Player down regardless of whether that action would cause a collision.

I can see you should be dealing with this with your 3rd call to Collision and moving the player back up but to remove some complexity from your code (slightly), you could simply have the 2nd call to collision checking the position + 1 and not moving the player if true.

This removes the need for the 3rd call to Collision, matches the logic you're using for most of the falling with the first call to collision (i.e. moving by gravity but not if the move would collide with a wall) and reduces the places your error can occur.