Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

objects on moving platform "jump" if it changes direction to downward #8

Closed
makc opened this issue Nov 14, 2012 · 17 comments
Closed

objects on moving platform "jump" if it changes direction to downward #8

makc opened this issue Nov 14, 2012 · 17 comments

Comments

@makc
Copy link

makc commented Nov 14, 2012

it could be argued that this is perfectly physical behavior, however, if I leave the hero standing on the moving platform and go for my coffee, I do not expect it to be dead after a while because he moved to the edge and fell off.

could there be some kind of hack implemented that will change the inertia of all the bodies that contact moving platform when it changes direction?

@alamboley
Copy link
Member

Hi, I've not be able to reproduce this behavior. This is my GameState :

override public function initialize():void {
super.initialize();

var box2d:Box2D = new Box2D("box2D");
box2d.visible = true;
add(box2d);

var hero:Hero = new Hero("hero", {width:40, height:70, view:"PatchSpriteArt.swf"});
add(hero);

add(new Platform("platform", {x:stage.stageWidth/2, width:stage.stageWidth, y:stage.stageHeight}));

add(new MovingPlatform("movingPlatform", {oneWay:true, x:300, y:280, width:200}));

}

The moving platform doesn't change other objects velocity. However the Hero has some functions where a moving platform has an impact :

protected function getSlopeBasedMoveAngle():b2Vec2
{
return Box2DPhysicsObject.Rotateb2Vec2(new b2Vec2(acceleration, 0), _combinedGroundAngle);
}

protected function updateCombinedGroundAngle():void
{
_combinedGroundAngle = 0;

if (_groundContacts.length == 0)
    return;

for each (var contact:b2Fixture in _groundContacts)
    var angle:Number = contact.GetBody().GetAngle();

var turn:Number = 45 * Math.PI / 180;
angle = angle % turn;
_combinedGroundAngle += angle;
_combinedGroundAngle /= _groundContacts.length;

}

@makc
Copy link
Author

makc commented Nov 14, 2012

I will try to give you simple game state code to reproduce this tomorrow (other pc here). However, this is fairly easy to understand. Imagine yourself in the elevator that accelerates downwards faster than 1g - the floor will basically say goodbye to your feet (and then the roof will hit your head). Now when moving platform reverses its direction and goes down - it does so instantly (basically infinite acceleration is applied, which sure is > 1g), so you see hero detached from platform for a short time. If the platform moves diagonally, this is enough for him to "jump" along the platform to one of edges and eventually fall off.

@gsynuh
Copy link
Member

gsynuh commented Nov 14, 2012

Hi makc,

Physics engines in the case of a platformer, are almost TOO close to reality in fact, like your example shows. So the laws must be broken when he is standing on a platform in one way or another to avoid this, I think that if "moving platforms" are part of the engine, then this behavior should also be taken care of inside of it.

either this or maybe your gravity isn't high enough? but I haven't tested this as moving platforms are not yet implemented in nape apparently (and I would love to be able to contribute to this so this discussion may help).

My main idea for your problem would be to "disable" the vertical velocity of the player (making it equal to the platform's at every update) only when he's in contact with it - that would involve using the onGround boolean I guess - and also this shouldn't prevent the player from voluntarily jumping off of the platform (because onGround would stop being true) or moving sideways on it by himself so long as the platforms friction is not equal to 0 i guess.
does that sound doable?

@alamboley
Copy link
Member

Thanks for the explanation, I understand this physics problem :)
The physics is right, but most of the game don't need to be as complex. I think we can get around this issue adding to the MovingPlatform's passengers its y velocity.

For example, the Treadmill (which extends MovingPlatform) update function looks like this :

override public function update(timeDelta:Number):void {

super.update(timeDelta);

if (enableTreadmill) {

    for each (var passengers:b2Body in _passengers) {

        if (startingDirection == "right")
            passengers.GetUserData().x += speedTread;
        else
            passengers.GetUserData().x -= speedTread;
    }
}

_updateAnimation();

}

In our case, it would be something close to (I didn't test it) :

var newVelocity:b2Vec2 = passengers.GetLinearVelocity();
newVelocity.Add(new b2Vec2(0, _body.GetLinearVelocity().y));
passengers.SetLinearVelocity(newVelocity);

@makc
Copy link
Author

makc commented Nov 15, 2012

Here is a test state to reproduce this:

package {

  import com.citrusengine.physics.box2d.Box2D;
  import com.citrusengine.objects.platformer.box2d.MovingPlatform;
  import com.citrusengine.objects.platformer.box2d.Hero;
  import com.citrusengine.core.StarlingState;

  public class TestState extends StarlingState {
    override public function initialize (  ) : void {
      super.initialize();

      var physics : Box2D  = new Box2D("box2d");
      physics.visible = true;
      add(physics);


      add(new MovingPlatform("moving", {x:200, y:600, width:140, height:95, startX:220, startY:600, endX:500, endY:170}))


      add(new Hero("hero", {x:300, y:450, width:60, height:135}))

    }
  }
}

Hero should land on very edge of the platform, and then fall off when it reverses the direction. The swf is set to 60fps, btw.

As @gsynuh I would like to see this fixed in MovingPlatform itself ;) There is a number of ways you could do that, so just pick one that works and easy to hack in.

@gsynuh
Copy link
Member

gsynuh commented Nov 15, 2012

Even though this might not be the best solution, it would be good to reset the passengers velocity to the new platform's velocity when it suddenly changes direction.
That way, next frame will not make the passengers "jump", but already be "in phase" with the new direction/velocity of the moving platform. For this, something as simple as :

                    for each (var passengers:b2Body in _passengers)
                    {
                        passengers.SetLinearVelocity(velocity);
                    }

does it, in the update loop of MovingPlatform, after line 161 ( _forward = !_forward; ) .

I wish I knew how to attach a commit to a comment here, it would be easier to suggest larger changes.

@alamboley
Copy link
Member

Hey, I've been able to reproduce the "bug".

I changed the class as @gsynuh said, it works great the first time, but the second time it falls :

else {

        //Destination is very close. Switch the travelling direction
        _forward = !_forward;

        for each (var passengers:b2Body in _passengers)
            passengers.SetLinearVelocity(velocity);
    }
}

_body.SetLinearVelocity(velocity);

I also tried to add this to the end of the update function, it was a bit better but not a success :

var passengersVelocity:b2Vec2;
for each (var passengers:b2Body in _passengers) {

    if (velocity.y > 0) {

        passengersVelocity = passengers.GetLinearVelocity();
        passengersVelocity.Add(velocity);
        passengers.SetLinearVelocity(passengersVelocity);
    }

}

@makc
Copy link
Author

makc commented Nov 15, 2012

sorry I can't be much of help since I work with swc here. but why do you put it inside else {...} clause when the platform itself set velocity to its _body outside? apparently there is a number of cases where it is altered between lines 135 and 163, and passengers velocity change should be in sync with all of those cases, no?

@gsynuh
Copy link
Member

gsynuh commented Nov 15, 2012

your second try is much more logical than mine in fact.

Combining the two solutions, even if it sounds crazy, works nicely, I have been running the state for 4 minutes now, it doesn't look like it has moved at all. because I believe the conservation of momentum when it changes direction is still an issue unless

for each (var passengers:b2Body in _passengers)
            passengers.SetLinearVelocity(velocity);
    }

is still applied when _forward changes I guess.

@gsynuh
Copy link
Member

gsynuh commented Nov 15, 2012

@makc the "else" is not a problem, as this is the special case when the moving platform changes direction. being a special case it should be handled differently.
in every other case what @alamboley did is perfect. I do think "resetting" the "momentum" of the passengers when changing direction is a logical thing to keep as this is where it all fails.

By the way, it's been running for a very long time now, still not out of the platform :D maybe we have a solution !

@makc
Copy link
Author

makc commented Nov 15, 2012

"changing direction" is only a special case as far as I noticed the effect in this case. however, the actual case is "changing platform velocity instantly" - i.e. if the platform could double its speed in the same direction. I did not aim to understand what lines 135-163 do, but if they change the velocity - they do all participate in this "special case".

p.s.:

still not out of the platform :D maybe we have a solution !

I'm quite happy if so.

@gsynuh
Copy link
Member

gsynuh commented Nov 15, 2012

Right. For now MovingPlatform has linear speed so it may not be a solution if you create a moving platform that has easing effects on its velocity .
Lines 135 to 163 are all assuming the moving platform will have a linear speed. In another case, it should be coded differently - and maybe be coded in another extending class.

@gsynuh
Copy link
Member

gsynuh commented Nov 15, 2012

sorry I think to be exact I should've said linear movement rather than linear speed.

@alamboley
Copy link
Member

I've updated the class with @gsynuh code which combined both.
https://github.com/alamboley/Citrus-Engine/blob/master/src/com/citrusengine/objects/platformer/box2d/MovingPlatform.as

I've to admit that combine both is a bit weird. I'm not sure to fully understand why it works.
@makc the problem occurs only when the MovingPlatform change its direction, so it should be in the final else{...}, isn't it?
@gsynuh indeed, the moving platform works only with a linear movement. Its class should be extended to implement easing effect.
Thanks for your help guys!

@gsynuh
Copy link
Member

gsynuh commented Nov 15, 2012

Our hero here, has a velocity relative to the world. What should really be happening is to have its velocity relative to the platform always. - this is exactly what your code attempts to replicate.

if the platform moves up, we can jump in place, we land on the same spot
(this is not the case if we jump exactly before the moving platform starts changing direction, in this case we simply jump off because the moving platform goes away from us while we are in the air).

However, when the platform moves down, the hero lands always slightly on the left.
it's not logical at all, because at least we should land on the right .

In an attempt to hack away some weird solution, this came up as "working"

            //prevent bodies to fall if they are on a edge.
            var passengerVelocity:b2Vec2;
            for each (passenger in _passengers) {
                
                if (velocity.y > 0)
                {
                    passengerVelocity = passenger.GetLinearVelocity();
                    //passengerVelocity.Add(velocity);
                    passengerVelocity.y += velocity.y;
                    passenger.SetLinearVelocity(passengerVelocity);
                }
                
            }               

The Hero no longer drifts when jumping on the moving platform when its going down.

Let's not forget that one is no longer a passenger when one is not touching (even slightly) the platform so the fact that we are supposed to be focused on vertical velocity causing the problem is right, however the code that does make it all work feels like a bit of nonsense. here I just had a feeling that adding in the velocity.x to the passengerVelocity was wrong in the first place. I still don't get most of it though.

@gsynuh
Copy link
Member

gsynuh commented Nov 15, 2012

I don't want to go on about it for ages and spam your inboxes, but for future reference and other visitors that would come here, It's best to clarify what's happening as much as possible, also to help people that would try creating other types of platforms.

What I did right after I took a look at this issue in the first place was set the passenger's velocity to the platform's velocity on every update using

            for each (passenger in _passengers) 
                    passenger.SetLinearVelocity(velocity);            

What happened then was that it fixed the problem but your jumping height was restricted and you would move very slowly left or right, because in fact your 'initial' velocity was the platform's velocity so in order to jump as you would on the floor you needed more velocity impulse to counter the platform's forces, and the same for moving left or right.

Adding the velocity to the passengers velocity would fix the problem, but not for its passenger's x velocity because it would already be the same as the platform's velocity , and that's because of the friction !

That's why I think we only need to fix the y velocity on each frame to make the passenger "stick" to the platform on each frame, the physics engine then, thanks to the positive friction, will fix the x velocity by itself. If we would leave Add(velocity) then there would be twice as much x velocity to the hero than it's supposed to, and that's why he would drift left when jumping.

problem solved?

@alamboley
Copy link
Member

Firstly, don't worry about my inboxes. It's an amazing feeling to see people interested in this engine :)

Setting a linear velocity on each update to the passenger, is very wrong because it would prevent objects to be able to move.
Well, I was asking why we should removed the x velocity. According to Box2D Manual, friction is used to make objects slide along each other realistically. So it has an impact on the x-axis displacement. You had a good feeling ;)

I've made the update, so problem solved? ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants