excaliburjs / Excalibur

🎮 Your friendly TypeScript 2D game engine for the web 🗡️
https://excaliburjs.com
BSD 2-Clause "Simplified" License
1.82k stars 188 forks source link

lockToActor jitter on large velocities — Camera.update() should be called `onPostUpdate` #1815

Closed BinarySpike closed 2 years ago

BinarySpike commented 3 years ago

The camera update, and thus strategies, are being applied prior to the actor updates. This means that the camera position is computed using a stale position. When large velocity values are used, this causes strategies like lockToActor to flicker.

The issue can easily be reproduced in the template-ts-webpack by adding:

// player.ts
vel: vec(400, 400), // between lines 7 and 8

// index.ts
this.levelOne.camera.strategy.lockToActor(this.player); // after line 22

A shim (fix) can be used by doing something like:

this.levelOne.onPostUpdate = (engine, delta) => {
  this.levelOne.camera.update(engine, delta);
}

Reviewing src/engine/Scene.ts uncovers lines 341 - 343

if (this.camera) {
      this.camera.update(engine, delta);
    }

and lines (later) 361 - 364

for (i = 0, len = this.actors.length; i < len; i++) {
      this._bodies[i] = this.actors[i].body;
    }

I believe, placing the camera update after the actor update (probably make it last) is what is logistically expected. Most game engines I've seen put the camera update on the pre-draw step.

I'd be willing to submit a PR if someone can confirm my resolution—I just started learning this today.

Edit After thinking about this for a couple days, I feel like a Camera Actor would be a better solution. The scene graph could then look like: Scene->Camera->Actors. This has the added benefit of allowing multiple cameras like a HUD camera versus a level camera.

eonarheim commented 3 years ago

@BinarySpike It's worth a shot moving the camera update to be last thing or nearly the last thing in scene update, I'm not sure I remember a reason why the current setup has it first on update. Your reasoning makes sense to me 👍

On the draw side, it is important to have it first when drawing things in world space because it sets up the camera transform for the following draw calls.

BinarySpike commented 3 years ago

I've since dived in with some more actors and I can see where someone might want to know about the camera transformation before they perform an update. However, I can't think of a situation where that would appropriate design.

Maybe an order PreUpdate->Update->Camera Update->PostUpdate would give you that ability. Then again, the documentation says PostUpdate is the preferred override.

I saw in Kraken Unchained that the hearts were a static 'HUD'-like element. They are being drawn directly on the scene. This has the disadvantage of not being an actor, and implementing an actor to draw from a Scene draw is non-trivial as the actor has to be manually bootstrapped (Initialized & updated).

I need to learn more about excalibur's ECC and I'll get a PR put together in my personal time.

Thanks for your response!

github-actions[bot] commented 3 years ago

This issue hasn't had any recent activity lately and is being marked as stale automatically.