craftyjs / Crafty

JavaScript Game Engine
http://craftyjs.com
MIT License
3.42k stars 559 forks source link

Gravity + Jetpack? #903

Closed ashes999 closed 9 years ago

ashes999 commented 9 years ago

I'm trying to add "jetpack"-like functionality to my player. When I add the Gravity component, I get the following (desirable) behaviours:

What I would like to figure out, is how I can press/hold the up button and fly upward. Is it enough to bind the EnterFrame event and add to the player's y position? Is there a better way to do this?

NB: if jumping gets disabled in the process, that's cool too.

mucaho commented 9 years ago

You could achieve in different ways, as you probably noticed. Here is a quick jsfiddle - benefits are you work with vertical velocity so it plays somewhat nicely with gravity. By adding vertical velocity each frame (up to a limit) you get a nice sluggishness effect.

.bind("EnterFrame", function(frameData) {
  if (this.isDown("W")) {
    this.vy = Math.max(-5, this._vy - 2.5);
  }
})

I would suggest to use the forum for discussing usages of the library and use the issues to post feature request if it's not something trivially doable with the library.

ashes999 commented 9 years ago

Noted, thanks. I'll close this issue then.

mucaho commented 9 years ago

Hold up, wait a minute :) Does it work as you intended?

ashes999 commented 9 years ago

No, not really. Thanks though.

mucaho commented 9 years ago

What's not working? I just tried, it should also work with normal jump behavior: new jsfiddle

ashes999 commented 9 years ago

It's not that it doesn't work (although with the new js fiddle, if you hold w, you just jump normally) -- it's two things:

1) I find the pre-existing Crafty components really dense. I can't understand them (especially how to extend them), especially without looking at the source. 2) The source, and examples from you, are usually the latest (dev) versions. I have to download and rebuild the local sources, instead of using the latest production version (0.6.2 as of writing).

I'm using CraftyJS for prototyping. The perception is (rightly) "well I can wire up gravity, collision, etc. in a matter of minutes" -- but changing those behaviours (eg. can't jetpack, can't figure out how to make this work) is really a hassle.

As much as you are extremely helpful and provide working examples (something very rare in GitHub culture), I can't expect you to write all my code for me. I don't know where that leaves me, because there's no other tool that gives me this much, this fast.

Like in this case; I had to again resort to using nightlies.

(end soapbox)

mucaho commented 9 years ago

Crafty has a bit of a slow (but therefor hopefully stable) release cycle. Just recently, the Motion system was developed, and due to diligent beta testing from @neothemachine we discovered bugs in the nightly builds and (hopefully) fixed them by now (see this forum discussion). What I want to say is: thank you for your patience and testing with nightly builds, this helps us fix things and provide more stable code for future users of the library.

I didn't understand crafty at all at first, but with time I started to understand the source code gradually. If there are any obscurities, feel free to ask!

Btw, I added this example to craftyjs/demos/devDemos/example_jetpack.html for future reference.

letmaik commented 9 years ago

@mucaho Just quick, in your demo code, you use if (this._y + this._h > ground._y + this._dy), in my code you used if (this._y + this._h > ground._y + this._vy). So what's doing _dy here?

mucaho commented 9 years ago

Using this._dy appeared to be a miniscule improvement over this._vy. Reason is that dy actually contains the difference of y in the current frame, whereas vy contains the planned difference of y for the next frame. According to my testing there was almost no difference, so I didn't feel like it was worth mentioning / potentially confusing you with yet another alternative. However, now that you mention it, you are better off using dy in the general case, as vy can be changed by the user (like it's the case in the jetpack example code) and the if condition probably won't work as expected.

starwed commented 9 years ago

When I was working on a little physics based prototype, I ended up creating a "Newtonian" component that allowed you to apply forces, and then rewriting "Gravity" to use that. I'll submit a PR for this at some point; it ends up being much more flexible, and should be useful for things like jetpacks.

Here is a pastebin of the coffeescript implementation of those components, but it required adding a "CalculateAcceleration" hook into the "Motion" component. (Also, that code almost definitely has some small bugs lurking somewhere, but the basic idea seems pretty sound.)

ashes999 commented 9 years ago

Is it expected that new and inexperienced users puzzle out all these details themselves?

ashes999 commented 9 years ago

@mucaho how do you stop on collisions?

I added walls around my screen, and an onHit function that sets vx and vy to zero on collision. But it seems to cause rebound, eg. if you walk left into a wall, and let go, you start automatically going right.

You can see this in my latest code. If you walk into the left/right walls, or fly above the asteroid and then hold the down key, you rebound. This is with the fourway component.

mucaho commented 9 years ago

@ashes999 reported the bug #904 Does it work if you replace Actor.collideWith with?

  collideWith: function(tag) {
    this.bind("Moved", function(evt) {
      if (this.hit(tag)) {
        this[evt.axis] = evt.oldValue;
        if (evt.axis === "y") this.vy = 0;
      }
    });
    return this;
  },
ashes999 commented 9 years ago

@mucaho yes, that fixes my problem. Thanks.

I have a comment about something. With gravity, when you're standing on the ground, your velocity is still increasing (even though you are grounded). This results in bugs where, if the ground you're standing on is thin enough, your velocity eventually reaches a high even value that you punch through it.

This is usually cited as being due to the use of euler integration. One way to mitigate it which I've used in the past, although it won't help with high-enough velocities, is to break up and apply time-frame updates at a fixed, small interval (eg. 10ms). I think you're already doing this.

Anyway, without the if (evt.axis === "y") this.vy = 0; line, your velocity continues to increase. In my sample, this means that you eventually fall through the asteroid.

Thanks for your help. Let me know if you have any questions about my comments.

mucaho commented 9 years ago

With gravity, when you're standing on the ground, your velocity is still increasing (even though you are grounded)

Gravity should correctly reset vertical velocity upon landing (you can log the vertical velocity when player lands on the platform above and it should show 0). However, the player never lands on your bottom floor. I thought you were aiming for an endless vertical jumper, so that's why I tried to preserve your code logic with if (evt.axis === "y") this.vy = 0;. EDIT: aha got it, you do .gravity("Asteroid") but your walls don't contain "Asteroid" component, so they are ignored by Gravity

ashes999 commented 9 years ago

Gravity should correctly reset vertical velocity upon landing (you can log the vertical velocity when player lands on the platform above and it should show 0).

The way Gravity appears to work is that it displaces you upward to the top of the object, but does not stop your velocity from increasing. I've seen two behaviours, depending on how I try to resolve the collisions:

You're right that I'm prototyping an endless vertical jumper. But, that if-statement you added doesn't affect that (actually, I can walk on the floor fine with it.) It actually prevents a bug where your y velocity eventually becomes ~150 and you blink through the floor.

You can clone my repo and verify this yourself (use the HEAD~1 version). If you want specific details (eg. when does it jitter? when does it phase through the floor? when does it hold stable but keep increasing?) I can try to reproduce those cases.

mucaho commented 9 years ago

Unfortunately I can't reproduce the jittering issue. Could you make a small example where it's occurring? Regarding stability, it's on the TODO list (see last paragraph of #884).

mucaho commented 9 years ago

aha @ashes999 , I think communicated a very important point poorly, I think that should solve your jittering problem

you do .gravity("Asteroid") but your walls don't contain "Asteroid" component, so they are ignored by Gravity

ashes999 commented 9 years ago

@mucaho I didn't get a chance to try and repro the jittering yet. Hopefully, tomorrow.

I don't use gravity any more. It turns out that gravity is well-suited to ground only, because when you specify .gravity('tag'), hitting that object propels you upward to the top of it. For asteroids, if you bump them from the bottom, you teleport to the top. Similarly, if you bump into a wall, you teleport to the top.

Now, I'm trying to use a simple collision instead of gravity. My latest code is here. I don't know if I can reproduce the jittering with this code, but I'll try.