0
\$\begingroup\$

I'm trying to make a platformer game without tiles. I've made a collision handler to handle collision between Player and Stage(platform)and a collision checker, here's the code:

public void handleCollision(GameObject OtherObject)
    {   
        if(this.CollidesWith(OtherObject))
        {

            if(OtherObject is Stage)
            {

                float rightEdgeDistance = OtherObject.BoundRect.X - (Position.X + this.BoundRect.Width);
                float leftEdgeDistance = OtherObject.BoundRect.X + OtherObject.BoundRect.Width - Position.X;

                float TopEdgeDistance = OtherObject.BoundRect.Y - (Position.Y + this.BoundRect.Height);
                float BottomEdgeDistance = OtherObject.BoundRect.Y + OtherObject.BoundRect.Height - Position.Y;


                float Left_Right_SmallerDistance = Math.Min(Math.Abs(rightEdgeDistance), Math.Abs(leftEdgeDistance));
                float Top_Bottom_SmallerDistance = Math.Min(Math.Abs(TopEdgeDistance), Math.Abs(BottomEdgeDistance));

                float smallerDistance = Math.Min(Math.Abs(Left_Right_SmallerDistance), Math.Abs(Top_Bottom_SmallerDistance));

                if (smallerDistance == Math.Abs(leftEdgeDistance))
                {
                    Position.X = OtherObject.BoundRect.X + OtherObject.BoundRect.Width;
                    Velocity.X = 0;
                }
                else if (smallerDistance == Math.Abs(rightEdgeDistance))
                {
                    Position.X = OtherObject.BoundRect.X - this.BoundRect.Width;
                    Velocity.X = 0;
                }
                else if (smallerDistance == Math.Abs(BottomEdgeDistance))
                {
                    Position.Y = OtherObject.BoundRect.Y + OtherObject.BoundRect.Height;
                    Velocity.Y = 0;
                }
                else if (smallerDistance == Math.Abs(TopEdgeDistance))
                {
                    Position.Y = OtherObject.BoundRect.Y - this.BoundRect.Height;
                    Velocity.Y = 0;
                }
            }
        }
}

My collision checker:

public bool CollidesWith(GameObject OtherObject)
    {
            return BoundRect.Intersects(OtherObject.BoundRect);
    }

Here's my inputHandler method for the Player class:

public void inputHandler(GameTime gameTime)
    {
        Velocity = Vector2.Zero;

        if (Keyboard.GetState().IsKeyDown(Keys.W) )
        {
            Velocity.Y -= moveSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds;
        }
        else if (Keyboard.GetState().IsKeyDown(Keys.S) )
        {
            Velocity.Y += moveSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds;
        } 
        else
            Velocity.Y = 0;

        if (Keyboard.GetState().IsKeyDown(Keys.A) )
        {
            Velocity.X -= moveSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds;
        }
        else if (Keyboard.GetState().IsKeyDown(Keys.D) )
        {
            Velocity.X += moveSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds;
        }
        else
            Velocity.X = 0;


    }

And here's my code for the Update method in Player:

public void Update(GameTime gameTime,List<Stage> lstOtherObject) 
    {
        inputHandler(gameTime);
        foreach (GameObject OtherObject in lstOtherObject) { handleCollision(OtherObject); }

        Position += Velocity;
    }

This works, if I don't continue to press the keys in the bounded direction. If I do, it will stutter in and out of the platform bounds. Like this: It goes a bit into the grey platformenter image description here and then it goes out of the grey platform to the intended place.enter image description here Can someone help? I want to learn, but I cant seems to figure out whats wrong with this.I tried many things, but still I can't achieve what I want, that is for the player to stop when it reaches the bounds of the platform and never going into it.Appreciate if someone can bring a clear solution and explanation to this. Thanks.

\$\endgroup\$
0
\$\begingroup\$

I have looked into your code and I could see some wrong procedures ( I think , I might be wrong though ) but I will try to look more into it to see what are its problems . However , since your main goal is to stop the rectangles from intersecting each other , I have another way that works well ( mostly ) and I think is more efficient than your algorithm . ( I know that as a not a pro myself that you want an explanation why your way didn't work , I will post it in another answer).

So what I actually use in my way is that rather than changing the position of the object when it hits another , I prevent the change if its going to hit it one frame ahead .

If we changed the code of HandleCollision() method to my way it will look like this :

public void handleCollision(GameObject OtherObject)
{   
    if(OtherObject is Stage)
    {
            if(CollidesWith(OtherObject , Position.X + Velocity.X (float)gameTime.ElapsedGameTime.TotalSeconds , Position.Y)
            {
                Velocity.X = 0;  
            }
            if(CollidesWith(OtherObject , Position.X, Position.Y  + Velocity.Y (float)gameTime.ElapsedGameTime.TotalSeconds )
            {
                Velocity.Y = 0;   
            }
    }
}

And the CollideWith() method

public bool CollidesWith(GameObject OtherObject , float X , float Y)
{
        return New Rectangle(X,Y,BoundRect.width,BoundRect.height).Intersects(OtherObject.BoundRect);
}

So now , the explanation : As you see in the image below , ( P square is player , ghost is change in Pos in next frame and the purple is another rectangle ) when we add the velocity ( change in position , multiplied by the time to get the exact change in the next frame ) to the position , we change it .

No intersection

So the handle collision method checks if that change would cause an intersection between the rectangles of the objects , if it does then the velocity will be 0 .

Collide

But how do we achieve this ? well first of all lets take a look at the CollidesWith method . We add 2 new parameters X and Y , We will use these to create our new Rectangle , but Why ? The new rectangle will have the position as X and Y and the dimensions will be the same as the BoundRect , we do this so we are able to manipulate the position of collision so we are able to detect if the player , for example collides 10 pixels ahead ( Posision.X + 10 ) and of course use it to determine our "Ghost" rectangle position , which is the change that will affect the Position of the player ( Velocity ) .

And in the HandleCollision() method we call the CollidesWith() method two times , one for X collision and the other for Y collision , each time setting the X or Y of the CollidesWith() Method as the position of the current object + its velocity . Since Velocity is the change in the position then we simply apply that change by adding it to the position , that way we get the future change before it actually applies , with it we check if we should move the player or not , if not the we set the velocity to 0 to make sure that the position won't change .

In the input handle method you should simply change the velocity to the value you would like , then in the update method after the input handle method we place the handle collision ( must be after it ) so it detects of the collision after setting the velocity , and after that we apply the velocity to the position .

With this way of collision there is a disadvantage I found , probably you think of it now , what if the object velocity per frame is 5 pixels ? would that mean that if its 3 pixels close the other rectangle it wont move to it because if it moved that 5 pixels it will intersect the other rectangle ? meaning that object will stay at a distance of 3 pixels from the rectangle ? Yes this is right a high speed moving object will probably stay at a distance from the another rectangle , but this can be solved very quickly in many ways , here is a very easy one after changing the HandleCollision() method :

public void handleCollision(GameObject OtherObject)
{   
    if(OtherObject is Stage)
    {
            float horizontalPoint;
            float verticalPoint;
            if(Position.X < OtherObject.Position.X){horizontalPoint = OtherObject.Position.X - BoundRect.width}
            if(Position.X > OtherObject.Position.X) {horizontalPoint = OtherObject.Position.X + OtherObject.BoundRect.width}
            if(Position.Y < OtherObject.Position.Y){verticalPoint = OtherObject.Position.Y - BoundRect.height}
            if(Position.Y > OtherObject.Position.Y) {verticalPoint = OtherObject.Position.Y + OtherObject.BoundRect.height}
            if(CollidesWith(OtherObject , Position.X + Velocity.X (float)gameTime.ElapsedGameTime.TotalSeconds , Position.Y)
            {
                Velocity.X = 0;
                if(Position.X != horizontalPoint){Position.X = horizontalPoint}
            }
            if(CollidesWith(OtherObject , Position.X, Position.Y  + Velocity.Y (float)gameTime.ElapsedGameTime.TotalSeconds )
            {
                Velocity.Y = 0;
                if(Position.Y != verticalPoint){Position.Y = verticalPoint}
            }
    }
}

We here simply get some point calculations to get the point where our position should be after colliding with the rectangle , and if its not then we sit it to be .

Hope that helped ,note that game objects don`t actually move , they teleport from point to another , so in most collision methods ( if not all ) if your velocity per frame is more that that the width of the bound rectangle it will probably go through it .

\$\endgroup\$
  • \$\begingroup\$ I'll try this, and I'll get to you, as I'm pretty sleepy right now. Thanks for the answer, I think I get the idea. I'll tell you if anything occurs. Once again , thanks dude. \$\endgroup\$ – Adi Faidz Jan 11 '15 at 18:22
  • \$\begingroup\$ Thanks for informing me of the error , happy to help you too . Make sure tick your answer as correct so anyone knows that its the right one . \$\endgroup\$ – Mohamed Atef Jan 12 '15 at 18:22
  • \$\begingroup\$ I want to, but I need to wait 18 hours lol, now only 3 hours left :D \$\endgroup\$ – Adi Faidz Jan 13 '15 at 5:14
0
\$\begingroup\$

You were right about checking it before it collides but the handling collision algorithm you code will not solve it. As it doesn't handle all the case that can occur.

Lets say that the player comes and grounded on the top of the platform. Yes the case of:

if(Position.Y < OtherObject.Position.Y){verticalPoint = OtherObject.Position.Y - BoundRect.Height;}

will work here,but so does the case:

if(Position.X > OtherObject.Position.X) {horizontalPoint = OtherObject.Position.X + OtherObject.BoundRect.Width;}

which will cause a logical error. This can be overcome with the method I used and by changing it a bit, I have achieved what I needed. Here's the code

public void handleCollision(GameObject OtherObject)
    {   

        if(OtherObject is Stage)
        {

            if (CollidesWith(OtherObject, Position.X + Velocity.X, Position.Y))
            {
                float rightEdgeDistance = OtherObject.BoundRect.X - (Position.X + this.BoundRect.Width);
                float leftEdgeDistance = OtherObject.BoundRect.X + OtherObject.BoundRect.Width - Position.X;

                float Left_Right_SmallerDistance = Math.Min(Math.Abs(rightEdgeDistance), Math.Abs(leftEdgeDistance));

                if (Left_Right_SmallerDistance == Math.Abs(leftEdgeDistance))
                {
                    Position.X = OtherObject.BoundRect.X + OtherObject.BoundRect.Width;
                    Velocity.X = 0;
                }
                else if (Left_Right_SmallerDistance == Math.Abs(rightEdgeDistance))
                {
                    Position.X = OtherObject.BoundRect.X - this.BoundRect.Width;
                    Velocity.X = 0;
                }
            }

            if (CollidesWith(OtherObject, Position.X, Position.Y + Velocity.Y))
            {
                float TopEdgeDistance = OtherObject.BoundRect.Y - (Position.Y + this.BoundRect.Height);
                float BottomEdgeDistance = OtherObject.BoundRect.Y + OtherObject.BoundRect.Height - Position.Y;

                float Top_Bottom_SmallerDistance = Math.Min(Math.Abs(TopEdgeDistance), Math.Abs(BottomEdgeDistance));

                if (Top_Bottom_SmallerDistance == Math.Abs(BottomEdgeDistance))
                {
                    Position.Y = OtherObject.BoundRect.Y + OtherObject.BoundRect.Height;
                    Velocity.Y = 0;
                }
                else if (Top_Bottom_SmallerDistance == Math.Abs(TopEdgeDistance))
                {
                    Position.Y = OtherObject.BoundRect.Y - this.BoundRect.Height;
                    Velocity.Y = 0;
                }
            }

        }

    }

Thank you good sir, For helping me realize that I need to predict collision of the next update :)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.