Detecting when a player "is on the ground"

Posts that don't fit into other categories.
mrsomeone
Posts: 3
Joined: Wed Feb 28, 2018 11:24 am

Detecting when a player "is on the ground"

Postby mrsomeone » Wed Feb 28, 2018 11:44 am

Good evening,
i'm trying to detect when the Player is on the ground in my 2D sidescroller but i'm having a hard time.
I tried to add a sensor fixture to the bottom of the player's body and the ContactAdapter "sensed" it right. The problem is I never know when the contact ends :oops:

I also tried without the sensor:
listening for "persisted" and "end" contacts, I created a Rectangle in the bottom of the player and tested if the contact.getPoint() is contained in this Rectangle this way (pseudocode)

(player is a Rect)
contacts = Set
footsRect = Rectangle(PLAYER_WIDTH * 0.9, 0.1)

on persisted
if player body is involved
- footsRect.translate(playerBody.X, playerBody.Y - playerBody.HEIGHT + footsRect.HEIGHT / 2)
- if contact point contained in rect
-- add the other body to the set

on end
if player body is involved
- remove the other body from the set (no test with the rectangle)

player_is_on_ground = contacts.size() > 0



I also tried with a Map<Body, Integer> to count the contacts and only remove them when Integer <= 0 but failed...
suggestions? ;(;(


EDIT:
i tried this method also (basically the same as mine, using id instead of Body):
viewtopic.php?t=24

seems like begin is called more times than end and with different ids sometimes. it's very noticeable just by printing the set size on every contact

mrsomeone
Posts: 3
Joined: Wed Feb 28, 2018 11:24 am

Re: Detecting when a player "is on the ground"

Postby mrsomeone » Thu Mar 01, 2018 4:06 pm

some code...

Code: Select all

        world.addListener(new ContactAdapter() {
            @Override
            public boolean begin(final ContactPoint point) {
                if (point.getBody1() == getBody() || point.getBody2() == getBody()) { // seems like equals and == gives the same result
                    final Body other = getTheOther(point.getBody1(), point.getBody2());
                    if (!contacts.contains(point.getId())) {
                        System.out.println("begin " + point.getId().hashCode());
                        contacts.add(point.getId());
                    }
                }
                return super.begin(point);
            }

            @Override
            public void end(final ContactPoint point) {
                if (point.getBody1()  == getBody() || point.getBody2() == getBody()) {
                    final Body other = getTheOther(point.getBody1(), point.getBody2());
                    if (contacts.contains(point.getId())) {
                        System.out.println("end " + point.getId().hashCode());
                        contacts.remove(point.getId());
                    }
                }
                super.end(point);
            }
        });



getBody() returns the player body.
getTheOther it's just a simple check to get the other body

Code: Select all

    private Body getTheOther(final Body b1, final Body b2) {
        if (b1 == getBody()) {
            return b2;
        }
        return b1;
    }


it really seems like begin gets called more times than end..
like it gets called twice for every NEW body it gets in contact.

example:
player spawns in air and fall on the first platform, output =
begin xxxxx
begin yyyyy
player jumps
end yyyyy
player falls on the same platform
begin yyyyy
player jumps on another platform
end yyyyy
begin zzzzz
begin kkkkkk
and so on.. the contacts HashSet keeps growing

William
Site Admin
Posts: 378
Joined: Sat Feb 06, 2010 10:23 pm

Re: Detecting when a player "is on the ground"

Postby William » Fri Mar 02, 2018 8:07 am

Have you looked at the TrackingContactIds.java sample? This might answer some questions.

As to why begin seems to be called twice might be because there are multiple contacts between the two bodies. Imagine the case where a box is sitting on another.

Another thing that may not be apparent is that when you are working with curved shapes, tracking contacts is much more difficult since it's considered a NEW contact if the contact point moves far enough.

Another way you could do this might be to call the Body.isInContact(Body) method each frame.

If you can supply a full sample we can probably offer more assistance.

Thanks,
William

mrsomeone
Posts: 3
Joined: Wed Feb 28, 2018 11:24 am

Re: Detecting when a player "is on the ground"

Postby mrsomeone » Fri Mar 02, 2018 10:11 am

William wrote:Have you looked at the TrackingContactIds.java sample? This might answer some questions.

As to why begin seems to be called twice might be because there are multiple contacts between the two bodies. Imagine the case where a box is sitting on another.

Another thing that may not be apparent is that when you are working with curved shapes, tracking contacts is much more difficult since it's considered a NEW contact if the contact point moves far enough.

Another way you could do this might be to call the Body.isInContact(Body) method each frame.

If you can supply a full sample we can probably offer more assistance.

Thanks,
William

Thanks for your support. I should've provided more details:
my shapes are all rectangles. The player is a fixed rotation dynamic body 0.8x1.5 (width x height) and a platform is just a static rectangle like 10x1
I'm ok with multiple begin calls as long as there's an end to each of them but it seems that's not the case.
I already saw your example code but it doesn't seem much different from mine: you use a Map<ID, UUID> while I use a Set<ID> but the behaviour seems the same.

I solved creating a sensor Fixture called "feet" positioned to the bottom of the player body.
my "isOnGround" function became this one:

Code: Select all

return body.getContacts(true).stream().filter(c -> c.getFixture1() == feet || c.getFixture2() == feet)
                    .findAny().isPresent();


now i'm left with 2 problems:

Code: Select all

            @Override
            public void end(final ContactPoint point) {
                super.end(point);
                if (point.getBody1().equals(getBody()) || point.getBody2().equals(getBody())) {
                System.out.println("end " + getBody().getContacts(true).stream()
                        .filter(c -> c.getFixture1() == feet || c.getFixture2() == feet).filter(c -> c.getDepth() > 0)
                        .findAny().isPresent());
                }
            }

1 - the above query always returns true if ran in any of the ContactAdapter's callbacks while I would expect it to return false in "end"
but I can live with it and just run the query for every isOnGround() call

2 - sometimes the player gets permanently stuck into vertical tiled walls ç_ç no matter if I apply forces / impulses: he stays there and cannot move
here's a little picture of what i mean:
Image

William
Site Admin
Posts: 378
Joined: Sat Feb 06, 2010 10:23 pm

Re: Detecting when a player "is on the ground"

Postby William » Fri Mar 02, 2018 1:01 pm

I'm away right now, but if I remember correctly the Body.getContacts method won't be accurate at the time the contactadapter methods are called.

Generally, I wouldn't do any logic inside of the callbacks. Instead I would queue up the information and process what happened after the step completed.

William

William
Site Admin
Posts: 378
Joined: Sat Feb 06, 2010 10:23 pm

Re: Detecting when a player "is on the ground"

Postby William » Sat Mar 31, 2018 2:37 pm

Probably too late, but I updated the SimplePlatformer.java sample program to help illustrate how you might achieve this. Keep in mind that it's just a sample and you'd probably have something more structured.

William


Return to “General Discussion”

Who is online

Users browsing this forum: No registered users and 1 guest