Skip to content

Commit

Permalink
feat: Add Slide Transition (#3248)
Browse files Browse the repository at this point in the history
https://github.com/user-attachments/assets/59b5fd99-205e-43fe-8338-49e2dd4b234b

## Changes:
- Added a method to force graphics on screen `ex.GraphicsComponent.forceOnScreen`
- Added new `ex.Slide` scene transition, which can slide a screen shot of the current screen: `up`, `down`, `left`, or `right`. Optionally you can add an `ex.EasingFunction`, by default `ex.EasingFunctions.Linear`
  ```typescript
  game.goToScene('otherScene', {
    destinationIn: new ex.Slide({
      duration: 1000,
      easingFunction: ex.EasingFunctions.EaseInOutCubic,
      slideDirection: 'up'
    })
  });
  ```
- Fixed issue where `ex.Engine.screenshot()` images may not yet be loaded in time for use in `ex.Transition`s
- Fixed issue where there would be an incorrect background color for 1 frame when transitioning to a new scene
  • Loading branch information
eonarheim authored Oct 23, 2024
1 parent 76122ce commit 6ff8778
Show file tree
Hide file tree
Showing 37 changed files with 798 additions and 230 deletions.
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,17 @@ This project adheres to [Semantic Versioning](http://semver.org/).

### Added

- Added a method to force graphics on screen `ex.GraphicsComponent.forceOnScreen`
- Added new `ex.Slide` scene transition, which can slide a screen shot of the current screen: `up`, `down`, `left`, or `right`. Optionally you can add an `ex.EasingFunction`, by default `ex.EasingFunctions.Linear`
```typescript
game.goToScene('otherScene', {
destinationIn: new ex.Slide({
duration: 1000,
easingFunction: ex.EasingFunctions.EaseInOutCubic,
slideDirection: 'up'
})
});
```
- Added inline SVG image support `ex.ImageSource.fromSvgString('<svg>...</svg>')`, note images produced this way still must be loaded.
- Added ability to optionally specify sprite options in the `.toSprite(options:? SpriteOptions)`
- The `ex.Engine` constructor had a new `enableCanvasContextMenu` arg that can be used to enable the right click context menu, by default the context menu is disabled which is what most games seem to want.
Expand Down Expand Up @@ -107,6 +118,8 @@ are doing mtv adjustments during precollision.

### Fixed

- Fixed issue where `ex.Engine.screenshot()` images may not yet be loaded in time for use in `ex.Transition`s
- Fixed issue where there would be an incorrect background color for 1 frame when transitioning to a new scene
- Fixed issue where `blockInput: true` on scene transition only blocked input events, not accessors like `wasHeld(...)` etc.
- Fixed issue where users could not easily define a custom `RendererPlugin` because the type was not exposed
- Fixed issue where `ex.Fade` sometimes would not complete depending on the elapsed time
Expand Down Expand Up @@ -163,6 +176,7 @@ are doing mtv adjustments during precollision.
### Changed

- Applied increased TS strictness:
* Director API subtree
* Resource API subtree
* Graphics API subtree
* TileMap API subtree
Expand Down
31 changes: 28 additions & 3 deletions karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,15 @@ const SlowSpecsReporter = function(baseReporterDecorator) {
return b.time - a.time;
})
for (const spec of slowSpecs.slice(0, 20)) {
this.write(spec.message);
let color = '\u001b[32m'; // green
let timeSeconds = spec.time/1000;
if (timeSeconds >= 0.5) {
color = '\u001b[33m'; // yellow
}
if (timeSeconds >= 1.0) {
color = '\u001b[31m'; // red
}
this.write(`${color}${(timeSeconds).toFixed(2)} Seconds:\u001b[0m ${spec.name}\n`);
}
slowSpecs.length = 0;
};
Expand All @@ -52,6 +60,22 @@ const timingReporter = {
'reporter:jasmine-slow': ['type', SlowSpecsReporter], // 1.
}

const TimeoutSpecsReporter = function(baseReporterDecorator, logger, emitter) {
baseReporterDecorator(this);
const reporter = this;

emitter.on('browser_info', (browser, data) => {
if (!data || data.type !== 'Jasmine Timeout Reporter') {
return
}
reporter.write(`\n\u001b[31m${data.type.toUpperCase()}:\u001b[0m ${data.specName}\n`);
});
}
TimeoutSpecsReporter.$inject = ['baseReporterDecorator', 'logger', 'emitter'];
const timeoutReporter = {
'reporter:jasmine-timeout': ['type', TimeoutSpecsReporter]
}

module.exports = (config) => {
config.set({
singleRun: true,
Expand All @@ -64,7 +88,8 @@ module.exports = (config) => {
require('karma-spec-reporter'),
require('karma-jasmine-order-reporter'),
seedReporter,
timingReporter
timingReporter,
timeoutReporter
],
client: {
// Excalibur logs / console logs suppressed when captureConsole = false;
Expand Down Expand Up @@ -155,7 +180,7 @@ module.exports = (config) => {
// i. e.
stats: 'normal'
},
reporters: ['jasmine-order', 'progress', /*'spec'*/, 'coverage-istanbul','jasmine-seed', 'jasmine-slow'],
reporters: ['jasmine-order', 'progress', /*'spec'*/, 'coverage-istanbul','jasmine-seed', 'jasmine-slow', 'jasmine-timeout'],
coverageReporter: {
reporters: [
{ type: 'html', dir: 'coverage/' },
Expand Down
51 changes: 45 additions & 6 deletions sandbox/src/game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -858,7 +858,7 @@ player.on('pointerwheel', () => {
});

var newScene = new ex.Scene();
newScene.backgroundColor = ex.Color.Yellow;
newScene.backgroundColor = ex.Color.ExcaliburBlue;
newScene.add(new ex.Label({ text: 'MAH LABEL!', x: 200, y: 100 }));
newScene.on('activate', (evt?: ex.ActivateEvent) => {
console.log('activate newScene');
Expand Down Expand Up @@ -894,9 +894,43 @@ game.input.keyboard.on('down', (keyDown?: ex.KeyEvent) => {
});
game.add(a);
} else if (keyDown.key === ex.Keys.U) {
game.goToScene('label');
game.goToScene('label', {
destinationIn: new ex.Slide({
duration: 1000,
easingFunction: ex.EasingFunctions.EaseInOutCubic,
slideDirection: 'up'
})
});
} else if (keyDown.key === ex.Keys.D) {
game.goToScene('label', {
destinationIn: new ex.Slide({
duration: 1000,
easingFunction: ex.EasingFunctions.EaseInOutCubic,
slideDirection: 'down'
})
});
} else if (keyDown.key === ex.Keys.L) {
game.goToScene('label', {
destinationIn: new ex.Slide({
duration: 1000,
easingFunction: ex.EasingFunctions.EaseInOutCubic,
slideDirection: 'left'
})
});
} else if (keyDown.key === ex.Keys.R) {
game.goToScene('label', {
destinationIn: new ex.Slide({
duration: 1000,
easingFunction: ex.EasingFunctions.EaseInOutCubic,
slideDirection: 'right'
})
});
} else if (keyDown.key === ex.Keys.I) {
game.goToScene('root');
game.goToScene('root', {
destinationIn: new ex.CrossFade({
duration: 1000
})
});
}
});

Expand Down Expand Up @@ -1068,6 +1102,11 @@ game.currentScene.camera.strategy.lockToActorAxis(player, ex.Axis.X);
game.currentScene.camera.y = 200;

// Run the mainloop
game.start(boot).then(() => {
logger.info('All Resources have finished loading');
});
game
.start('root', {
inTransition: new ex.FadeInOut({ duration: 2000, direction: 'in', color: ex.Color.ExcaliburBlue }),
loader: boot
})
.then(() => {
logger.info('All Resources have finished loading');
});
6 changes: 3 additions & 3 deletions src/engine/Collision/Colliders/CircleCollider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,9 @@ export class CircleCollider extends Collider {
*/
public rayCast(ray: Ray, max: number = Infinity): RayCastHit | null {
// https://en.wikipedia.org/wiki/Intersection_(geometry)#A_line_and_a_circle
const c = this.center; //?
const dir = ray.dir; //?
const orig = ray.pos; //?
const c = this.center;
const dir = ray.dir;
const orig = ray.pos;

const u = c.sub(orig);

Expand Down
2 changes: 1 addition & 1 deletion src/engine/Collision/PhysicsConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ export const DefaultPhysicsConfig: DeepRequired<PhysicsConfig> = {
export function DeprecatedStaticToConfig(): DeepRequired<PhysicsConfig> {
return {
enabled: Physics.enabled,
gravity: Physics.gravity,
gravity: Physics.gravity.clone(),
solver: Physics.collisionResolutionStrategy,
substep: 1,
continuous: {
Expand Down
8 changes: 5 additions & 3 deletions src/engine/Director/CrossFade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ export interface CrossFadeOptions {
* Note: CrossFade only works as an "in" transition
*/
export class CrossFade extends Transition {
engine: Engine;
image: HTMLImageElement;
screenCover: Sprite;
engine!: Engine;
image!: HTMLImageElement;
screenCover!: Sprite;
constructor(options: TransitionOptions & CrossFadeOptions) {
super({ direction: 'in', ...options }); // default the correct direction
this.name = `CrossFade#${this.id}`;
Expand All @@ -34,6 +34,8 @@ export class CrossFade extends Transition {
this.transform.pos = engine.screen.unsafeArea.topLeft;
this.screenCover = ImageSource.fromHtmlImageElement(this.image).toSprite();
this.graphics.add(this.screenCover);

// This is because we preserve hidpi res on the screen shot which COULD be bigger than the logical resolution
this.transform.scale = vec(1 / engine.screen.pixelRatio, 1 / engine.screen.pixelRatio);
this.graphics.opacity = this.progress;
}
Expand Down
6 changes: 3 additions & 3 deletions src/engine/Director/DefaultLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export function isLoaderConstructor(x: any): x is LoaderConstructor {
}

export class DefaultLoader implements Loadable<Loadable<any>[]> {
public data: Loadable<any>[];
public data!: Loadable<any>[];
public events = new EventEmitter<LoaderEvents>();
public canvas: Canvas = new Canvas({
filtering: ImageFiltering.Blended,
Expand All @@ -57,7 +57,7 @@ export class DefaultLoader implements Loadable<Loadable<any>[]> {
return this._resources;
}
private _numLoaded: number = 0;
public engine: Engine;
public engine!: Engine;

/**
* @param options Optionally provide the list of resources you want to load at constructor time
Expand Down Expand Up @@ -259,6 +259,6 @@ export class DefaultLoader implements Loadable<Loadable<any>[]> {
public off(eventName: string, handler: Handler<unknown>): void;
public off(eventName: string): void;
public off<TEventName extends EventKey<LoaderEvents> | string>(eventName: TEventName, handler?: Handler<any>): void {
this.events.off(eventName, handler);
(this.events as any).off(eventName, handler);
}
}
Loading

0 comments on commit 6ff8778

Please sign in to comment.