Skip to content

Commit

Permalink
feat: Add alias fixed update timestep (#3239)
Browse files Browse the repository at this point in the history
  • Loading branch information
eonarheim authored Oct 9, 2024
1 parent 9c08c95 commit 53a06ce
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 7 deletions.
4 changes: 2 additions & 2 deletions src/engine/Camera.ts
Original file line number Diff line number Diff line change
Expand Up @@ -797,8 +797,8 @@ export class Camera implements CanUpdate, CanInitialize {

// interpolation if fixed update is on
// must happen on the draw, because more draws are potentially happening than updates
if (this._engine.fixedUpdateFps) {
const blend = this._engine.currentFrameLagMs / (1000 / this._engine.fixedUpdateFps);
if (this._engine.fixedUpdateTimestep) {
const blend = this._engine.currentFrameLagMs / this._engine.fixedUpdateTimestep;
const interpolatedPos = this.pos.scale(blend).add(this._oldPos.scale(1.0 - blend));
interpolatedPos.clone(this.drawPos);
this.updateTransform(interpolatedPos);
Expand Down
44 changes: 41 additions & 3 deletions src/engine/Engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,21 @@ export interface EngineOptions<TKnownScenes extends string = any> {
*/
maxFps?: number;

/**
* Optionally configure a fixed update timestep in milliseconds, this can be desireable if you need the physics simulation to be very stable. When
* set the update step and physics will use the same elapsed time for each tick even if the graphical framerate drops. In order for the
* simulation to be correct, excalibur will run multiple updates in a row (at the configured update elapsed) to catch up, for example
* there could be X updates and 1 draw each clock step.
*
* **NOTE:** This does come at a potential perf cost because each catch-up update will need to be run if the fixed rate is greater than
* the current instantaneous framerate, or perf gain if the fixed rate is less than the current framerate.
*
* By default is unset and updates will use the current instantaneous framerate with 1 update and 1 draw each clock step.
*
* **WARN:** `fixedUpdateTimestep` takes precedence over `fixedUpdateFps` use whichever is most convenient.
*/
fixedUpdateTimestep?: number;

/**
* Optionally configure a fixed update fps, this can be desireable if you need the physics simulation to be very stable. When set
* the update step and physics will use the same elapsed time for each tick even if the graphical framerate drops. In order for the
Expand All @@ -298,6 +313,8 @@ export interface EngineOptions<TKnownScenes extends string = any> {
* the current instantaneous framerate, or perf gain if the fixed rate is less than the current framerate.
*
* By default is unset and updates will use the current instantaneous framerate with 1 update and 1 draw each clock step.
*
* **WARN:** `fixedUpdateTimestep` takes precedence over `fixedUpdateFps` use whichever is most convenient.
*/
fixedUpdateFps?: number;

Expand All @@ -306,6 +323,7 @@ export interface EngineOptions<TKnownScenes extends string = any> {
*
* Excalibur will automatically sort draw calls by z and priority into renderer batches for maximal draw performance,
* this can disrupt a specific desired painter order.
*
*/
useDrawSorting?: boolean;

Expand Down Expand Up @@ -455,8 +473,25 @@ export class Engine<TKnownScenes extends string = any> implements CanInitialize,
* the current instantaneous framerate, or perf gain if the fixed rate is less than the current framerate.
*
* By default is unset and updates will use the current instantaneous framerate with 1 update and 1 draw each clock step.
*
* **WARN:** `fixedUpdateTimestep` takes precedence over `fixedUpdateFps` use whichever is most convenient.
*/
public fixedUpdateFps?: number;
public readonly fixedUpdateFps?: number;

/**
* Optionally configure a fixed update timestep in milliseconds, this can be desireable if you need the physics simulation to be very stable. When
* set the update step and physics will use the same elapsed time for each tick even if the graphical framerate drops. In order for the
* simulation to be correct, excalibur will run multiple updates in a row (at the configured update elapsed) to catch up, for example
* there could be X updates and 1 draw each clock step.
*
* **NOTE:** This does come at a potential perf cost because each catch-up update will need to be run if the fixed rate is greater than
* the current instantaneous framerate, or perf gain if the fixed rate is less than the current framerate.
*
* By default is unset and updates will use the current instantaneous framerate with 1 update and 1 draw each clock step.
*
* **WARN:** `fixedUpdateTimestep` takes precedence over `fixedUpdateFps` use whichever is most convenient.
*/
public readonly fixedUpdateTimestep?: number;

/**
* Direct access to the excalibur clock
Expand Down Expand Up @@ -975,7 +1010,10 @@ O|===|* >________________>\n\
}

this.maxFps = options.maxFps ?? this.maxFps;

this.fixedUpdateTimestep = options.fixedUpdateTimestep ?? this.fixedUpdateTimestep;
this.fixedUpdateFps = options.fixedUpdateFps ?? this.fixedUpdateFps;
this.fixedUpdateTimestep = this.fixedUpdateTimestep || 1000 / this.fixedUpdateFps;

this.clock = new StandardClock({
maxFps: this.maxFps,
Expand Down Expand Up @@ -1740,8 +1778,8 @@ O|===|* >________________>\n\
GraphicsDiagnostics.clear();

const beforeUpdate = this.clock.now();
const fixedTimestepMs = 1000 / this.fixedUpdateFps;
if (this.fixedUpdateFps) {
const fixedTimestepMs = this.fixedUpdateTimestep;
if (this.fixedUpdateTimestep) {
this._lagMs += elapsedMs;
while (this._lagMs >= fixedTimestepMs) {
this._update(fixedTimestepMs);
Expand Down
4 changes: 2 additions & 2 deletions src/engine/Graphics/GraphicsSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,9 +258,9 @@ export class GraphicsSystem extends System {
if (transform) {
let tx = transform.get();
if (optionalBody) {
if (this._engine.fixedUpdateFps && optionalBody.__oldTransformCaptured && optionalBody.enableFixedUpdateInterpolate) {
if (this._engine.fixedUpdateTimestep && optionalBody.__oldTransformCaptured && optionalBody.enableFixedUpdateInterpolate) {
// Interpolate graphics if needed
const blend = this._engine.currentFrameLagMs / (1000 / this._engine.fixedUpdateFps);
const blend = this._engine.currentFrameLagMs / this._engine.fixedUpdateTimestep;
tx = blendTransform(optionalBody.oldTransform, transform.get(), blend, this._targetInterpolationTransform);
}
}
Expand Down

0 comments on commit 53a06ce

Please sign in to comment.