-
-
Notifications
You must be signed in to change notification settings - Fork 195
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
update test
- Loading branch information
Showing
6 changed files
with
404 additions
and
43 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,237 @@ | ||
--- | ||
title: Scene Transitions 🧪 | ||
slug: /transitions | ||
section: Fundamentals | ||
--- | ||
|
||
import TransitionExample from '!!raw-loader!./examples/scene-transitions.ts'; | ||
import CrossFadeTransitionExample from '!!raw-loader!./examples/scene-crossfade.ts'; | ||
|
||
```twoslash include ex | ||
/// <reference path="../src/engine/excalibur.d.ts" /> | ||
class MyScene extends ex.Scene {} | ||
class MyOtherScene extends ex.Scene {} | ||
``` | ||
|
||
:::warning | ||
|
||
This is currently an alpha feature, and will be released in the next version! API might change/fluctuate until it lands in a supported version. | ||
|
||
::: | ||
|
||
🧪 `npm install [email protected]` or greater | ||
|
||
Many times in your game you'll want to smoothly go from one scene to the next, or provide a custom effect when transitioning! | ||
|
||
## Using Pre-Definited Scene Transitions | ||
|
||
It is generally recommended that you define you scenes up front, when you do you have the opportunity to also specify the in/out transitions for a scene. | ||
|
||
* `in` transitions play when the target scene has started, and will play the effect until the `duration` in milliseconds is complete | ||
* `out` transitions play before the current scene is deactivated, the transition must complete before deactivation. | ||
|
||
If no `in/out` transition is specified for a scene it will hard cut from one to the other. | ||
|
||
```ts twoslash | ||
// @include: ex | ||
// ---cut--- | ||
const game = new ex.Engine({ | ||
scenes: { | ||
scene1: { | ||
scene: MyScene, | ||
transitions: { | ||
in: new ex.FadeInOut({duration: 500, direction: 'in', color: ex.Color.Black}), | ||
out: new ex.FadeInOut({duration: 500, direction: 'out', color: ex.Color.Black}) | ||
} | ||
}, | ||
scene2: { | ||
scene: MyOtherScene, | ||
transitions: { | ||
in: new ex.FadeInOut({duration: 500, direction: 'in', color: ex.Color.Black}), | ||
out: new ex.FadeInOut({duration: 500, direction: 'out', color: ex.Color.Black}) | ||
} | ||
} | ||
} | ||
}); | ||
|
||
|
||
game.goto('scene1'); | ||
|
||
``` | ||
|
||
or using the add scene api | ||
|
||
```ts twoslash | ||
// @include: ex | ||
// ---cut--- | ||
const game = new ex.Engine(); | ||
|
||
game.add('scene1', { | ||
scene: MyScene, | ||
transitions: { | ||
in: new ex.FadeInOut({duration: 500, direction: 'in', color: ex.Color.Black}), | ||
out: new ex.FadeInOut({duration: 500, direction: 'out', color: ex.Color.Black}) | ||
} | ||
}); | ||
|
||
``` | ||
|
||
Click canvas to transition! | ||
|
||
<GameCodeBlock live | ||
title="Transition" | ||
code={TransitionExample} | ||
/> | ||
|
||
## Transition Options | ||
Transitions have a few tricks up their sleeves, you can control duration, direction, easing function, whether to hide any loaders, or block user input during the transition | ||
|
||
```ts twoslash | ||
// @include: ex | ||
// ---cut--- | ||
const transition = new ex.Transition({ | ||
/** | ||
* Transition duration in milliseconds | ||
*/ | ||
duration: 1000, | ||
|
||
/** | ||
* Optionally hides the loader during the transition | ||
* | ||
* If either the out or in transition have this set to true, then the loader will be hidden. | ||
* | ||
* Default false | ||
*/ | ||
hideLoader: false, | ||
|
||
/** | ||
* Optionally blocks user input during a transition | ||
* | ||
* Default false | ||
*/ | ||
blockInput: false, | ||
|
||
/** | ||
* Optionally specify a easing function, by default linear | ||
*/ | ||
easing: ex.EasingFunctions.Linear, | ||
/** | ||
* Optionally specify a transition direction, by default 'out' | ||
* | ||
* * For 'in' direction transitions start at 1 and complete is at 0 | ||
* * For 'out' direction transitions start at 0 and complete is at 1 | ||
*/ | ||
direction: 'out', | ||
}) | ||
``` | ||
|
||
## Overriding Transitions | ||
|
||
There are 2 ways to override pre-defined transitions | ||
|
||
* `goto('myscene', { destinationIn: ..., sourceOut: ... })` takes the highest precedence and will override any transition | ||
* Extending [[Scene.onTransition]] you can provide dynamic transitions depending on your scene's state | ||
|
||
```typescript | ||
class MyCustomScene extends ex.Scene { | ||
onTransition(direction: "in" | "out") { | ||
return new ex.FadeInOut({ | ||
direction, | ||
color: ex.Color.Violet, | ||
duration: 2000 | ||
}); | ||
} | ||
} | ||
``` | ||
|
||
## FadeInOut | ||
|
||
This transition does exactly as it sounds, you can specific a duration in milliseconds and it will fade in the specified `direction`. [[FadeInOut]] uses the color [[Color.Black]] by default. | ||
|
||
* The `direction: 'in'` direction means the transition will start fully opaque (non-transparent), then transition to fully transparent. | ||
* The `direction: 'out'` direction means the transition will start fully transparent, then transition to fully opaque (non-transparent). | ||
|
||
## CrossFade | ||
|
||
:::warning | ||
|
||
[[CrossFade]] can only be used on the `in`` transition for a scene, this is because it needs to [[Engine.screenshot|screenshot]] the previous scene in order to cross fade it. | ||
|
||
::: | ||
|
||
|
||
You can specific a duration in milliseconds and it will fade in the specified `direction`. [[CrossFade]] takes a screen shot of the previous scene and blends that into the current scene. | ||
|
||
* The `direction: 'in'` direction means the transition will start fully opaque (non-transparent), then transition to fully transparent. | ||
* The `direction: 'out'` direction means the transition will start fully transparent, then transition to fully opaque (non-transparent). | ||
|
||
Click canvas to transition! | ||
|
||
<GameCodeBlock live | ||
title="CrossFade Transition" | ||
code={CrossFadeTransitionExample} | ||
/> | ||
|
||
## Starting Scene Transition | ||
|
||
Sometimes you want a special start transition for the beginning of your game after loading. You may want to match the color, do something special, etc. | ||
|
||
This can be done on the [[Engine.start]] by providing a start transition | ||
|
||
```typescript | ||
game.start('scene1', | ||
{ | ||
inTransition: startTransition | ||
}); | ||
|
||
``` | ||
|
||
## Custom built Transitions | ||
|
||
Transitions are really an [[Entity]] with a [[TransformComponent]] and [[GraphicsComponent]] that take up the entire screen and draw on top of everything by default `z = Infinity`. | ||
|
||
To build your own custom transition, extend [[Transition]] and implement the stubbed methods | ||
|
||
For example this is CrossFade's implementation | ||
|
||
```typescript | ||
export class CrossFade extends Transition { | ||
engine: Engine; | ||
image: HTMLImageElement; | ||
screenCover: Sprite; | ||
constructor(options: TransitionOptions & CrossFadeOptions) { | ||
super(options); | ||
this.name = `CrossFade#${this.id}`; | ||
} | ||
|
||
override async onPreviousSceneDeactivate(scene: Scene<unknown>) { | ||
this.image = await scene.engine.screenshot(true); | ||
} | ||
|
||
override onInitialize(engine: Engine): void { | ||
this.engine = engine; | ||
const bounds = engine.screen.getWorldBounds(); | ||
this.transform.pos = vec(bounds.left, bounds.top); | ||
this.screenCover = ImageSource.fromHtmlImageElement(this.image).toSprite(); | ||
this.graphics.add(this.screenCover); | ||
this.transform.scale = vec(1 / engine.screen.pixelRatio, 1 / engine.screen.pixelRatio); | ||
this.graphics.opacity = this.progress; | ||
} | ||
|
||
override onStart(_progress: number): void { | ||
this.graphics.opacity = this.progress; | ||
} | ||
|
||
override onReset() { | ||
this.graphics.opacity = this.progress; | ||
} | ||
|
||
override onEnd(progress: number): void { | ||
this.graphics.opacity = progress; | ||
} | ||
|
||
override onUpdate(progress: number): void { | ||
this.graphics.opacity = progress; | ||
} | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
|
||
|
||
class MyScene extends ex.Scene { | ||
public onInitialize(): void { | ||
this.add( | ||
new ex.Actor({ | ||
pos: ex.vec(200, 200), | ||
color: ex.Color.Red, | ||
width: 100, | ||
height: 200 | ||
})) | ||
} | ||
} | ||
|
||
|
||
class MyOtherScene extends ex.Scene { | ||
public onInitialize(): void { | ||
this.add( | ||
new ex.Actor({ | ||
pos: ex.vec(200, 200), | ||
color: ex.Color.Blue, | ||
width: 200, | ||
height: 100 | ||
})) | ||
} | ||
} | ||
|
||
game.add('scene1', { | ||
scene: MyScene, | ||
transitions: { | ||
in: new ex.CrossFade({duration: 1500, blockInput: true }), | ||
} | ||
}); | ||
|
||
game.add('scene2', { | ||
scene: MyOtherScene, | ||
transitions: { | ||
in: new ex.CrossFade({duration: 1500, blockInput: true }), | ||
} | ||
}); | ||
|
||
game.input.pointers.primary.on('down', () => { | ||
game.currentSceneName === 'scene2' ? game.goto('scene1') : game.goto('scene2'); | ||
}); | ||
|
||
game.start('scene2'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
|
||
|
||
class MyScene extends ex.Scene { | ||
public onInitialize(): void { | ||
this.add( | ||
new ex.Actor({ | ||
pos: ex.vec(200, 200), | ||
color: ex.Color.Red, | ||
width: 100, | ||
height: 200 | ||
})) | ||
} | ||
} | ||
|
||
|
||
class MyOtherScene extends ex.Scene { | ||
public onInitialize(): void { | ||
this.add( | ||
new ex.Actor({ | ||
pos: ex.vec(200, 200), | ||
color: ex.Color.Blue, | ||
width: 200, | ||
height: 100 | ||
})) | ||
} | ||
} | ||
|
||
game.add('scene1', { | ||
scene: MyScene, | ||
transitions: { | ||
in: new ex.FadeInOut({duration: 500, direction: 'in', color: ex.Color.Black}), | ||
out: new ex.FadeInOut({duration: 500, direction: 'out', color: ex.Color.Black}) | ||
} | ||
}); | ||
|
||
game.add('scene2', { | ||
scene: MyOtherScene, | ||
transitions: { | ||
in: new ex.FadeInOut({duration: 500, direction: 'in', color: ex.Color.Black}), | ||
out: new ex.FadeInOut({duration: 500, direction: 'out', color: ex.Color.Black}) | ||
} | ||
}); | ||
|
||
game.input.pointers.primary.on('down', () => { | ||
game.currentSceneName === 'scene2' ? game.goto('scene1') : game.goto('scene2'); | ||
}); | ||
|
||
game.start('scene2'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.