Skip to content

Commit

Permalink
fix: Transition bug when camera is zoomed (#2942)
Browse files Browse the repository at this point in the history
- Fixed issue where start transition did not work properly if deferred
- Fixed issue where transitions did not cover the whole screen if camera was zoomed
  • Loading branch information
eonarheim authored Feb 23, 2024
1 parent 1b8475c commit 907b7e7
Show file tree
Hide file tree
Showing 10 changed files with 71 additions and 23 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).

### Fixed

-
- Fixed issue where start transition did not work properly if deferred
- Fixed issue where transitions did not cover the whole screen if camera was zoomed

### Updates

Expand Down
14 changes: 13 additions & 1 deletion sandbox/tests/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,15 @@ scene1.add(new ex.Label({
text: 'Scene 1',
z: 99
}))
scene1.onInitialize = () => {
scene1.camera.pos = ex.vec(200, 200);
scene1.camera.zoom = 2;
}
var scene2 = new ex.Scene();
scene2.onInitialize = () => {
scene2.camera.pos = ex.vec(200, 200);
scene2.camera.zoom = 2;
}
scene2.add(new ex.Label({
pos: ex.vec(100, 100),
color: ex.Color.Violet,
Expand Down Expand Up @@ -130,7 +138,11 @@ gameWithTransitions.input.keyboard.on('press', evt => {
gameWithTransitions.input.pointers.primary.on('down', () => {
gameWithTransitions.goto('scene1');
});
var startTransition = new ex.FadeInOut({duration: 500, direction: 'in', color: ex.Color.ExcaliburBlue});
var startTransition = new ex.FadeInOut({
duration: 3500,
direction: 'in',
color: ex.Color.Black
});
// startTransition.events.on('kill', () => {
// console.log(game.currentScene.entities);
// console.log('killed!');
Expand Down
3 changes: 1 addition & 2 deletions src/engine/Director/CrossFade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ export class CrossFade extends Transition {

override onInitialize(engine: Engine): void {
this.engine = engine;
const bounds = engine.screen.getWorldBounds();
this.transform.pos = vec(bounds.left, bounds.top);
this.transform.pos = engine.screen.unsafeArea.topLeft;
this.screenCover = ImageSource.fromHtmlImageElement(this.image).toSprite();
this.graphics.add(this.screenCover);
this.transform.scale = vec(1 / engine.screen.pixelRatio, 1 / engine.screen.pixelRatio);
Expand Down
8 changes: 4 additions & 4 deletions src/engine/Director/DefaultLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ export class DefaultLoader implements Loadable<Loadable<any>[]> {
*/
public onInitialize(engine: Engine) {
this.engine = engine;
this.canvas.width = this.engine.screen.canvasWidth;
this.canvas.height = this.engine.screen.canvasHeight;
this.canvas.width = this.engine.screen.resolution.width;
this.canvas.height = this.engine.screen.resolution.height;
}

/**
Expand Down Expand Up @@ -165,10 +165,10 @@ export class DefaultLoader implements Loadable<Loadable<any>[]> {
const seconds = this._totalTimeMs / 1000;

ctx.fillStyle = Color.Black.toRGBA();
ctx.fillRect(0, 0, this.engine.screen.drawWidth, this.engine.screen.drawHeight);
ctx.fillRect(0, 0, this.engine.screen.resolution.width, this.engine.screen.resolution.height);

ctx.save();
ctx.translate(this.engine.screen.center.x, this.engine.screen.center.y);
ctx.translate(this.engine.screen.resolution.width / 2, this.engine.screen.resolution.height / 2);
const speed = seconds * 10;
ctx.strokeStyle = 'white';
ctx.lineWidth = 10;
Expand Down
18 changes: 15 additions & 3 deletions src/engine/Director/Director.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ export class Director<TKnownScenes extends string = any> {
public events = new EventEmitter<DirectorEvents>();
private _logger = Logger.getInstance();
private _deferredGoto: string;
private _deferredTransition: Transition;
private _initialized = false;

/**
Expand Down Expand Up @@ -176,8 +177,13 @@ export class Director<TKnownScenes extends string = any> {
this._initialized = true;
if (this._deferredGoto) {
const deferredScene = this._deferredGoto;
const deferredTransition = this._deferredTransition;
this._deferredGoto = null;
this._deferredTransition = null;
await this.swapScene(deferredScene);
if (deferredTransition) {
await this.playTransition(deferredTransition);
}
} else {
await this.swapScene('root');
}
Expand Down Expand Up @@ -217,9 +223,10 @@ export class Director<TKnownScenes extends string = any> {
// Fire and forget promise for the initial scene
if (maybeStartTransition) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.swapScene(this.startScene);
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.playTransition(maybeStartTransition);
this.swapScene(this.startScene).then(() => {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.playTransition(maybeStartTransition);
});
} else {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.swapScene(this.startScene);
Expand Down Expand Up @@ -476,6 +483,11 @@ export class Director<TKnownScenes extends string = any> {
* @param transition
*/
async playTransition(transition: Transition) {
if (!this.isInitialized) {
this._deferredTransition = transition;
return;
}

if (transition) {
this.currentTransition = transition;
const currentScene = this._engine.currentScene;
Expand Down
8 changes: 3 additions & 5 deletions src/engine/Director/FadeInOut.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Engine } from '../Engine';
import { Color } from '../Color';
import { vec } from '../Math/vector';
import { Rectangle } from '../Graphics';
import { Transition, TransitionOptions } from './Transition';

Expand All @@ -22,11 +21,10 @@ export class FadeInOut extends Transition {
}

public onInitialize(engine: Engine): void {
const bounds = engine.screen.getWorldBounds();
this.transform.pos = vec(bounds.left, bounds.top);
this.transform.pos = engine.screen.unsafeArea.topLeft;
this.screenCover = new Rectangle({
width: bounds.width,
height: bounds.height,
width: engine.screen.resolution.width,
height: engine.screen.resolution.height,
color: this.color
});
this.graphics.add(this.screenCover);
Expand Down
9 changes: 5 additions & 4 deletions src/engine/Director/Transition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,13 +195,14 @@ export class Transition extends Entity {
this.onReset();
}

play(engine: Engine) {
play(engine: Engine, targetScene?: Scene) {
if (this.started) {
this._logger.warn(`Attempted to play a transition ${this.name} that is already playing`);
return Promise.resolve();
this.reset();
this._logger.warn(`Attempted to play a transition ${this.name} that is already playing, reset transition.`);
}

engine.add(this);
const currentScene = targetScene ?? engine.currentScene;
currentScene.add(this);
const self = this;
return coroutine(engine, function * () {
while (!self.complete) {
Expand Down
24 changes: 23 additions & 1 deletion src/engine/Screen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -806,6 +806,16 @@ export class Screen {
return this._contentArea;
}

/**
* Returns the unsafe area in screen space, this is the full screen and some space may not be onscreen.
*/
public get unsafeArea(): BoundingBox {
return this._unsafeArea;
}

private _contentArea: BoundingBox = new BoundingBox();
private _unsafeArea: BoundingBox = new BoundingBox();

private _computeFit() {
document.body.style.margin = '0px';
document.body.style.overflow = 'hidden';
Expand All @@ -825,9 +835,9 @@ export class Screen {
height: adjustedHeight
};
this._contentArea = BoundingBox.fromDimension(this.resolution.width, this.resolution.height, Vector.Zero);
this._unsafeArea = BoundingBox.fromDimension(this.resolution.width, this.resolution.height, Vector.Zero);
}

private _contentArea: BoundingBox = new BoundingBox();
private _computeFitScreenAndFill() {
document.body.style.margin = '0px';
document.body.style.overflow = 'hidden';
Expand Down Expand Up @@ -866,6 +876,12 @@ export class Screen {
right: this._contentResolution.width,
bottom: this.resolution.height - clip
});
this._unsafeArea = new BoundingBox({
top: -clip,
left: 0,
right: this._contentResolution.width,
bottom: this.resolution.height + clip
});
} else {
this.resolution = {
width: vh * this._contentResolution.height / vh * vw / vh,
Expand All @@ -878,6 +894,12 @@ export class Screen {
right: this.resolution.width - clip,
bottom: this._contentResolution.height
});
this._unsafeArea = new BoundingBox({
top: 0,
left: -clip,
right: this.resolution.width + clip,
bottom: this._contentResolution.height
});
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/spec/DirectorSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ describe('A Director', () => {

it('can configure start, non deferred', async () => {
const engine = TestUtils.engine();
const clock = engine.clock as ex.TestClock;
const scene1 = new ex.Scene();
const scene2 = new ex.Scene();
const sut = new ex.Director(engine, {
Expand All @@ -62,10 +63,12 @@ describe('A Director', () => {
loader
});
await engine.load(loader);
await TestUtils.flushMicrotasks(clock, 5);

expect(sut.currentTransition).toBe(fadeIn);
expect(sut.currentSceneName).toBe('scene1');
expect(sut.currentScene).toBe(scene1);
engine.dispose();
});

it('can configure start deferred', async () => {
Expand Down
4 changes: 2 additions & 2 deletions src/spec/FadeInOutSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ describe('A FadeInOut transition', () => {
it('can fade out', async () => {
const engine = TestUtils.engine({ backgroundColor: ex.Color.ExcaliburBlue });
const clock = engine.clock as ex.TestClock;
TestUtils.runToReady(engine);
await TestUtils.runToReady(engine);
engine.add(new ex.Actor({
pos: ex.vec(20, 20),
width: 100,
Expand All @@ -75,7 +75,7 @@ describe('A FadeInOut transition', () => {
}));
engine.addScene('newScene', scene);

const goto = engine.goto('newScene', { sourceOut: sut });
const goto = engine.goToScene('newScene', { sourceOut: sut });
await TestUtils.flushMicrotasks(clock, 3);
clock.step(900);
await Promise.resolve();
Expand Down

0 comments on commit 907b7e7

Please sign in to comment.