Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Collisions in simple platform game  (Read 4541 times)

0 Members and 1 Guest are viewing this topic.

Lambert

  • Newbie
  • *
  • Posts: 8
    • View Profile
Collisions in simple platform game
« on: September 08, 2013, 02:10:35 pm »
I'm creating a simple Mario-style game. It's my first project like this. I have problem with collision system in my game.

My map object has this method for checking collisions:
int Map::checkPosition(double x, double y, bool returnY)
{
        if(x>800 || x<0 || y>600 || y<0)
                if(returnY == false) return false;
                else return 600;
        else
                for(sf::Sprite sprite : mapEnviromentSprites_)
                        if(x > sprite.getPosition().x && x < sprite.getPosition().x + sprite.getGlobalBounds().width && y > sprite.getPosition().y && y < sprite.getPosition().y + sprite.getGlobalBounds().height)
                                if(returnY == false) return false;
                                else return sprite.getPosition().y;
        return true;
}

My player object uses it by checking few "checkpoints" on character sprite and, if every returns true, allows change to character position made by this method:
void Player::movePlayer(Map& map, int frameTime)
{
       //temporary variables until everything is working fine here
        const float moveSpeed = 150.0f;
        static float jumpTime = 0;
        static float jumpBeginY = 0;
        static bool jumpBeginYMarker = false;
        static float deltaTime;

        const float jumpMaxHeight = 100.0f;
        const float jumpMaxTime = 1.0f;
        const float a = 4*jumpMaxHeight/(jumpMaxTime*jumpMaxTime);
        const float b = 4*jumpMaxHeight/jumpMaxTime;
       
        //I'm taking current values of position of character sprite to modify them later
        x = player.getPosition().x;
        y = player.getPosition().y;
        //I get frame time right after displaying window in main loop, as microseconds
        deltaTime = frameTime*0.000001f;

        //I'm using PlayerState enum type to determine what texture should I set to character spite
        if(sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
                playerState_ = PlayerState::CROUCH;

        //I want to move character sprite only if he isn't pressing at obstacle at any point (up, down corners and middle point at approbate site)
       //Here is one problem - if I check raw values I can "freeze" on obstacle until I release left/right arrow, but if I check a little further, no less than 2 pixels, everything is fine
        else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right) && map.checkPosition(x+player.getGlobalBounds().width/2+0.1,y+player.getGlobalBounds().height/2) && map.checkPosition(x+player.getGlobalBounds().width/2+0.1,y-player.getGlobalBounds().height/2) && map.checkPosition(x+player.getGlobalBounds().width/2+0.1,y))
                {
                        playerState_ = PlayerState::WALK;
                        x += moveSpeed * deltaTime;
                        player.setScale(1,1);
                }

        else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left) && map.checkPosition(x-player.getGlobalBounds().width/2-0.1,y+player.getGlobalBounds().height/2) && map.checkPosition(x-player.getGlobalBounds().width/2-0.1,y-player.getGlobalBounds().height/2) && map.checkPosition(x-player.getGlobalBounds().width/2-0.1,y))
                {
                        playerState_ = PlayerState::WALK;
                        x -= moveSpeed * deltaTime;
                        player.setScale(-1,1);
                }
       
        //Obviously I don't want to allow jumping while jumping or falling)
        if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up) && !isFalling) isJumping = true;

        //I want player sprite to be in falling state always if it does not stand still on obstacle
        //Here is something bugged I think, isFalling is always on
        if(!isJumping && map.checkPosition(x+player.getGlobalBounds().width/2,y+player.getGlobalBounds().height/2-5) && map.checkPosition(x-player.getGlobalBounds().width/2,y+player.getGlobalBounds().height/2-5))
                isFalling = true;
        else isFalling = false;

        //again, PlayerState setting
                if(isJumping || isFalling)
                {
                        playerState_ = PlayerState::JUMP;
       
        //a part for taking y_0 of my jump
                        if(!jumpBeginYMarker)
                        {
                                        jumpBeginY = player.getPosition().y;
                                        jumpBeginYMarker = true;
                        }

         //equations for jumping and falling
                        if(isJumping) y = jumpBeginY -b*jumpTime + a*jumpTime*jumpTime;
                        if(isFalling) y = jumpBeginY + a*jumpTime*jumpTime;
        //updating jumping time, necessary in above equations
                        jumpTime += deltaTime;
        //I don't want to jump any longer if I hit obstacle with my head, so I start a fall
                        if(!map.checkPosition(x+player.getGlobalBounds().width/2,y-player.getGlobalBounds().height/2) || !map.checkPosition(x-player.getGlobalBounds().width/2,y-player.getGlobalBounds().height/2))
                        {
                                jumpTime = 0;
                                isJumping = false;
                                isFalling = true;
                                jumpBeginYMarker = false;
                        }
        //and again, I don't want to fall any longer if I hit obstacle with my feets
                        if(!map.checkPosition(x-player.getGlobalBounds().width/2,y+player.getGlobalBounds().height/2) || !map.checkPosition(x+player.getGlobalBounds().width/2,y+player.getGlobalBounds().height/2))
                        {
                                jumpTime = 0;
                                isJumping = false;
                                isFalling = false;
                                jumpBeginYMarker = false;
                                jumpBeginY = 0;
                        }
                }

        //another PlayerState stuff
        if(!sf::Keyboard::isKeyPressed(sf::Keyboard::Right) && !sf::Keyboard::isKeyPressed(sf::Keyboard::Left) && !sf::Keyboard::isKeyPressed(sf::Keyboard::Down) && isJumping == false && isFalling == false)
                playerState_ = PlayerState::STAND;

        //6 control points - up left/right, down left/right and middles of both sides
        //I don't want to allow player to take inaccessible areas, so I check above control points for collissions
        if(map.checkPosition(x+player.getGlobalBounds().width/2,y+player.getGlobalBounds().height/2) && map.checkPosition(x+player.getGlobalBounds().width/2,y-player.getGlobalBounds().height/2) && map.checkPosition(x-player.getGlobalBounds().width/2,y+player.getGlobalBounds().height/2) && map.checkPosition(x-player.getGlobalBounds().width/2,y-player.getGlobalBounds().height/2) && map.checkPosition(x+player.getGlobalBounds().width/2,y) && map.checkPosition(x-player.getGlobalBounds().width/2,y))
                player.setPosition(x,y);
}

There are three problems:
1. Falling never stops - when character is very close to surface of obstacle, it's just keep doing "little jumps", that last for around 0.001 second according to console log.
2. When I move on alone obstacle (70 pixel width), my character stands still (jumping ofc, like stated above) only on edges, but if it crosses centre (wide-wise) of this obstacle, it falls down through it. This problem doesn't exist if two or more obstacles are placed next to each other. It's like collision detecting method checks only corners of obstacles and all pixels around character sprite, but it's clearly designed in opposite way.
3. When I move horizontally towards an obstacle, especially when jumping, I freeze on it until I release left/right arrow. However if I set condition to check a two pixels further than player sprite, it works (in pasted code I tried to set 0.1, with negative result).

I'm sorry for my bad English, especially grammatical ones, it's not my primary language.
« Last Edit: September 08, 2013, 08:46:04 pm by Lambert »

Gobbles

  • Full Member
  • ***
  • Posts: 132
    • View Profile
    • Email
Re: Collisions in simple platform game
« Reply #1 on: September 08, 2013, 06:21:53 pm »
Maybe it's just me, but this is just a nightmare to read. My suggestion would be to take a step back and figure out what you're trying to achieve.

No offence, but stuff like this:
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right) && map.checkPosition(x+player.getGlobalBounds().width/2+0.1,y+player.getGlobalBounds().height/2) && map.checkPosition(x+player.getGlobalBounds().width/2+0.1,y-player.getGlobalBounds().height/2) && map.checkPosition(x+player.getGlobalBounds().width/2+0.1,y))
 

is not fun to work with, you have keyboard checks AND 3 collision checks in the same statement.

Also: http://sscce.org/ , we can't run a debugger on code fragments, and is that something you've tried?
Have you added print statements to see the state of values? Have you paused the application to run through this code line by line to see whats happening?

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6286
  • Thor Developer
    • View Profile
    • Bromeon
Re: Collisions in simple platform game
« Reply #2 on: September 08, 2013, 08:01:12 pm »
You should really clean up your code, there are so many questionable parts that I am not surprised of any bugs.

For example, the return type of Map::checkPosition() is int, why do you return true or false in some cases? You don't indent consistently, and your lines are far too long (at least for the forum). In the range-based for loop, you create unnecessary sf::Sprite copies. You mix float and double as well as other types for no apparent reasons. And so on...
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

Lambert

  • Newbie
  • *
  • Posts: 8
    • View Profile
Re: Collisions in simple platform game
« Reply #3 on: September 08, 2013, 08:27:01 pm »
Code is nightmare to read because it's in very beta stage. I'm changing little things everyday, adding and deleting stuff as well. I feel that it's pretty impractical to make this code clean at this stage, yet you may have right, I should cleaned it before posting here.

Gobbles - what is wrong with checking keyboard and collisions at once? I want to move sprite only if all these conditions are true, how to achieve it in different way? I will check this site, thanks. Of course I checked values in console, you can even see that in my post, when I'm explaining jumping bug ;) Yeah, I tried to use VS debugger as well and I have a small idea I mentioned below.

Nexus - yeah, as i stated above, I was constantly doing changes with code. For most time Map::checkPosition() was of type bool, but I wanted to add feature of returning value of Y axis of sprite that collide with character sprite. I forgot to add this to main post - I'm thinking that jumping bug may be caused by these equations for jumping and falling. They may took too big delta values for too small distance between collision point and current Y of character sprite. But it didn't work and I deleted part of code with setting obstacle Y to character Y in case of collision detection. I seriously doubt that mixing float and double may be a problem - I don't go under ~0.0001 values, but I'm total newbie here - seriously may it be so buggy? And about creating unnecessary copies of sf::Sprite - I don't understand? I just changed these for loops from old-fashioned way with (int i;i<vector.size();++i) and never used range version before.

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6286
  • Thor Developer
    • View Profile
    • Bromeon
Re: Collisions in simple platform game
« Reply #4 on: September 08, 2013, 08:35:37 pm »
I feel that it's pretty impractical to make this code clean at this stage
Absolutely not. It doesn't take longer to break lines and indent correctly, especially not with IDE support. The same applies to using the correct types and being consistent. Doing it later requires you to read and understand the code again, and effectively consume far more time than doing it right in the first place.

what is wrong with checking keyboard and collisions at once?
You should separate input and game logic. Decoupling functionality and keeping it modular is very important in OOP. It allows you to easily modify one part without affecting all the others, it gives a clearer view since things are handled locally, and it leads to fewer bugs because you can test and debug input and game logic independently.

I seriously doubt that mixing float and double may be a problem
It's not about being a problem, it's about consistency and code clarity. If there is no point in using double, simply stick to float. You will avoid unnecessary compiler warnings and potential problems.

And about creating unnecessary copies of sf::Sprite - I don't understand?
The way you're using the range-based for loop leads to copying the sprite for every element. You need a reference (to const) to refer to the element directly.
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

Lambert

  • Newbie
  • *
  • Posts: 8
    • View Profile
Re: Collisions in simple platform game
« Reply #5 on: September 08, 2013, 09:05:50 pm »
At the moment I added comments to my code, I hope it makes more sense now.

I'm thankful for all advices how to code better (that info about for loop was important for me, as I often play with algorithms too) and I'm aware of how my code may be illegible, yet I was expecting more collision-related replies. I wrote what exactly is wrong with my code, so please share with me your collision detection systems experiences. Like, for example, if I check correct positions and if my way is correct.

Gobbles

  • Full Member
  • ***
  • Posts: 132
    • View Profile
    • Email
Re: Collisions in simple platform game
« Reply #6 on: September 08, 2013, 09:31:25 pm »
The problem with your collision could very well stem from all these other issues. If the problem was clear black and white, well you wouldn't have posted this question in the first place.

//equations for jumping and falling
if(isJumping) y = jumpBeginY -b*jumpTime + a*jumpTime*jumpTime;
if(isFalling) y = jumpBeginY + a*jumpTime*jumpTime;
//updating jumping time, necessary in above equations
jumpTime += deltaTime;

This is a good example. Your doing multiplications with 'jumpTime', but that variable is a static you redeclare to 0 every single time you enter this function. Then you increase it's value based on 'deltaTime' but that doesn't matter as it's never used again until redeclared the next time you come back into the function.

Lambert

  • Newbie
  • *
  • Posts: 8
    • View Profile
Re: Collisions in simple platform game
« Reply #7 on: September 08, 2013, 10:18:05 pm »
O, I like the way you are thinking. Yet I believe static variables are declared only once. At least this is what I see, because while jumping (what I can totally do), right before landing and mini-jump bug, jumpTime takes almost exactly my jumpMaxTime value. BTW it's only temporary, I will make them Player class fields after everything works.

I'm sorry if I sound like i demand instant fix to my code, it wasn't my purpose. I just thought that as a total newbie I may do common errors with this collision detection, since I didn't read any tutorial, just wrote it from my head.
« Last Edit: September 08, 2013, 10:21:59 pm by Lambert »