feat: Excalibur Scene Transitions & Loaders (#2790)
This PR implements an API inspired by https://github.com/mattjennings/excalibur-router/ with permission from Matt.
The idea behind this refactoring is to add transitions between scenes and scene specific loaders!
* Add or remove scenes by constructor
* Add loaders by constructor
* New `DefaultLoader` type that allows for easier custom loader creation
* New `Transition` type for building custom transitions
* New scene lifecycle to allow scene specific resource loading
* `onTransition(direction: "in" | "out") {...}`
* `onPreLoad(loader: DefaultLoader) {...}`
* New async goto API that allows overriding loaders/transitions between scenes
* Scenes now can have `async onInitialize` and `async onActivate`!
* New scenes director API that allows upfront definition of scenes/transitions/loaders
View [this file](https://github.com/excaliburjs/Excalibur/blob/feat/async-init-ex-router/sandbox/tests/router/index.ts) for a current example of what the API looks like
Defining scenes upfront
```typescript
const game = new ex.Engine({
scenes: {
scene1: {
scene: scene1,
transitions: {
out: new ex.FadeInOut({duration: 1000, direction: 'out', color: ex.Color.Black}),
in: new ex.FadeInOut({duration: 1000, direction: 'in'})
}
},
scene2: {
scene: scene2,
loader: ex.DefaultLoader, // Constructor only option!
transitions: {
out: new ex.FadeInOut({duration: 1000, direction: 'out'}),
in: new ex.FadeInOut({duration: 1000, direction: 'in', color: ex.Color.Black })
}
},
scene3: ex.Scene // Constructor only option!
}
})
game.start('scene1',
{
inTransition: new ex.FadeInOut({duration: 500, direction: 'in', color: ex.Color.ExcaliburBlue})
loader: boot,
});
```

feat: Excalibur Scene Transitions & Loaders (#2790)
This PR implements an API inspired by https://github.com/mattjennings/excalibur-router/ with permission from Matt.
The idea behind this refactoring is to add transitions between scenes and scene specific loaders!
* Add or remove scenes by constructor
* Add loaders by constructor
* New `DefaultLoader` type that allows for easier custom loader creation
* New `Transition` type for building custom transitions
* New scene lifecycle to allow scene specific resource loading
* `onTransition(direction: "in" | "out") {...}`
* `onPreLoad(loader: DefaultLoader) {...}`
* New async goto API that allows overriding loaders/transitions between scenes
* Scenes now can have `async onInitialize` and `async onActivate`!
* New scenes director API that allows upfront definition of scenes/transitions/loaders
View [this file](https://github.com/excaliburjs/Excalibur/blob/feat/async-init-ex-router/sandbox/tests/router/index.ts) for a current example of what the API looks like
Defining scenes upfront
```typescript
const game = new ex.Engine({
scenes: {
scene1: {
scene: scene1,
transitions: {
out: new ex.FadeInOut({duration: 1000, direction: 'out', color: ex.Color.Black}),
in: new ex.FadeInOut({duration: 1000, direction: 'in'})
}
},
scene2: {
scene: scene2,
loader: ex.DefaultLoader, // Constructor only option!
transitions: {
out: new ex.FadeInOut({duration: 1000, direction: 'out'}),
in: new ex.FadeInOut({duration: 1000, direction: 'in', color: ex.Color.Black })
}
},
scene3: ex.Scene // Constructor only option!
}
})
game.start('scene1',
{
inTransition: new ex.FadeInOut({duration: 500, direction: 'in', color: ex.Color.ExcaliburBlue})
loader: boot,
});
```

feat: Debug Draw Static Helpers (#2929)
This PR adds new `ex.Debug` static for more convenient debug drawing where you might not have a graphics context accessible to you. This works by batching up all the debug draw requests and flushing them during the debug draw step.
* `ex.Debug.drawRay(ray: Ray, options?: { distance?: number, color?: Color })`
* `ex.Debug.drawBounds(boundingBox: BoundingBox, options?: { color?: Color })`
* `ex.Debug.drawCircle(center: Vector, radius: number, options?: ...)`
* `ex.Debug.drawPolygon(points: Vector[], options?: { color?: Color })`
* `ex.Debug.drawText(text: string, pos: Vector)`
* `ex.Debug.drawLine(start: Vector, end: Vector, options?: LineGraphicsOptions)`
* `ex.Debug.drawLines(points: Vector[], options?: LineGraphicsOptions)`
* `drawPoint(point: Vector, options?: PointGraphicsOptions)`
feat: Debug Draw Static Helpers (#2929)
This PR adds new `ex.Debug` static for more convenient debug drawing where you might not have a graphics context accessible to you. This works by batching up all the debug draw requests and flushing them during the debug draw step.
* `ex.Debug.drawRay(ray: Ray, options?: { distance?: number, color?: Color })`
* `ex.Debug.drawBounds(boundingBox: BoundingBox, options?: { color?: Color })`
* `ex.Debug.drawCircle(center: Vector, radius: number, options?: ...)`
* `ex.Debug.drawPolygon(points: Vector[], options?: { color?: Color })`
* `ex.Debug.drawText(text: string, pos: Vector)`
* `ex.Debug.drawLine(start: Vector, end: Vector, options?: LineGraphicsOptions)`
* `ex.Debug.drawLines(points: Vector[], options?: LineGraphicsOptions)`
* `drawPoint(point: Vector, options?: PointGraphicsOptions)`
feat: Implement SpriteSheet getSprite options (#2901)
This PR implements new `ex.SpriteSheet.getSprite(..., options)`
You can now influence sprites directly out of the sprite sheet
```typescript
const sprite = ss.getSprite(0, 0, {
flipHorizontal: true,
flipVertical: true,
width: 200,
height: 201,
opacity: .5,
scale: ex.vec(2, 2),
origin: ex.vec(0, 1),
tint: ex.Color.Red,
rotation: 4
});
```
Also the animation helper `ex.Animation.fromSpriteSheetCoordinates()` you can now pass any valid `ex.GraphicOptions` to influence the sprite per frame
```typescript
const anim = ex.Animation.fromSpriteSheetCoordinates({
spriteSheet: ss,
frameCoordinates: [
{x: 0, y: 0, duration: 100, options: { flipHorizontal: true }},
{x: 1, y: 0, duration: 100, options: { flipVertical: true }},
{x: 2, y: 0, duration: 100},
{x: 3, y: 0, duration: 100}
],
strategy: ex.AnimationStrategy.Freeze
});
```
change: GraphicsGroup no longer centers with anchors (#2966)
`useAnchor: false` The graphics members are positioned from the top left
```typescript
const graphicGroup = new ex.GraphicsGroup({
useAnchor: false,
members: [
{
graphic: heartImage.toSprite(),
offset: ex.vec(0, 0),
},
{
graphic: heartImage.toSprite(),
offset: ex.vec(0, 16),
},
{
graphic: heartImage.toSprite(),
offset: ex.vec(16, 16),
},
{
graphic: heartImage.toSprite(),
offset: ex.vec(16, 0),
},
],
});
```

`useAnchor: true` The graphics members' total combined bounds are centered by the actor's anchor (.5, .5) by default
```typescript
const graphicGroup = new ex.GraphicsGroup({
useAnchor: true,
members: [
{
graphic: heartImage.toSprite(),
offset: ex.vec(0, 0),
},
{
graphic: heartImage.toSprite(),
offset: ex.vec(0, 16),
},
{
graphic: heartImage.toSprite(),
offset: ex.vec(16, 16),
},
{
graphic: heartImage.toSprite(),
offset: ex.vec(16, 0),
},
],
});
```

change: GraphicsGroup no longer centers with anchors (#2966)
`useAnchor: false` The graphics members are positioned from the top left
```typescript
const graphicGroup = new ex.GraphicsGroup({
useAnchor: false,
members: [
{
graphic: heartImage.toSprite(),
offset: ex.vec(0, 0),
},
{
graphic: heartImage.toSprite(),
offset: ex.vec(0, 16),
},
{
graphic: heartImage.toSprite(),
offset: ex.vec(16, 16),
},
{
graphic: heartImage.toSprite(),
offset: ex.vec(16, 0),
},
],
});
```

`useAnchor: true` The graphics members' total combined bounds are centered by the actor's anchor (.5, .5) by default
```typescript
const graphicGroup = new ex.GraphicsGroup({
useAnchor: true,
members: [
{
graphic: heartImage.toSprite(),
offset: ex.vec(0, 0),
},
{
graphic: heartImage.toSprite(),
offset: ex.vec(0, 16),
},
{
graphic: heartImage.toSprite(),
offset: ex.vec(16, 16),
},
{
graphic: heartImage.toSprite(),
offset: ex.vec(16, 0),
},
],
});
```

change: GraphicsGroup no longer centers with anchors (#2966)
`useAnchor: false` The graphics members are positioned from the top left
```typescript
const graphicGroup = new ex.GraphicsGroup({
useAnchor: false,
members: [
{
graphic: heartImage.toSprite(),
offset: ex.vec(0, 0),
},
{
graphic: heartImage.toSprite(),
offset: ex.vec(0, 16),
},
{
graphic: heartImage.toSprite(),
offset: ex.vec(16, 16),
},
{
graphic: heartImage.toSprite(),
offset: ex.vec(16, 0),
},
],
});
```

`useAnchor: true` The graphics members' total combined bounds are centered by the actor's anchor (.5, .5) by default
```typescript
const graphicGroup = new ex.GraphicsGroup({
useAnchor: true,
members: [
{
graphic: heartImage.toSprite(),
offset: ex.vec(0, 0),
},
{
graphic: heartImage.toSprite(),
offset: ex.vec(0, 16),
},
{
graphic: heartImage.toSprite(),
offset: ex.vec(16, 16),
},
{
graphic: heartImage.toSprite(),
offset: ex.vec(16, 0),
},
],
});
```

feat: Excalibur Scene Transitions & Loaders (#2790)
This PR implements an API inspired by https://github.com/mattjennings/excalibur-router/ with permission from Matt.
The idea behind this refactoring is to add transitions between scenes and scene specific loaders!
* Add or remove scenes by constructor
* Add loaders by constructor
* New `DefaultLoader` type that allows for easier custom loader creation
* New `Transition` type for building custom transitions
* New scene lifecycle to allow scene specific resource loading
* `onTransition(direction: "in" | "out") {...}`
* `onPreLoad(loader: DefaultLoader) {...}`
* New async goto API that allows overriding loaders/transitions between scenes
* Scenes now can have `async onInitialize` and `async onActivate`!
* New scenes director API that allows upfront definition of scenes/transitions/loaders
View [this file](https://github.com/excaliburjs/Excalibur/blob/feat/async-init-ex-router/sandbox/tests/router/index.ts) for a current example of what the API looks like
Defining scenes upfront
```typescript
const game = new ex.Engine({
scenes: {
scene1: {
scene: scene1,
transitions: {
out: new ex.FadeInOut({duration: 1000, direction: 'out', color: ex.Color.Black}),
in: new ex.FadeInOut({duration: 1000, direction: 'in'})
}
},
scene2: {
scene: scene2,
loader: ex.DefaultLoader, // Constructor only option!
transitions: {
out: new ex.FadeInOut({duration: 1000, direction: 'out'}),
in: new ex.FadeInOut({duration: 1000, direction: 'in', color: ex.Color.Black })
}
},
scene3: ex.Scene // Constructor only option!
}
})
game.start('scene1',
{
inTransition: new ex.FadeInOut({duration: 500, direction: 'in', color: ex.Color.ExcaliburBlue})
loader: boot,
});
```

feat: Implement SpriteSheet getSprite options (#2901)
This PR implements new `ex.SpriteSheet.getSprite(..., options)`
You can now influence sprites directly out of the sprite sheet
```typescript
const sprite = ss.getSprite(0, 0, {
flipHorizontal: true,
flipVertical: true,
width: 200,
height: 201,
opacity: .5,
scale: ex.vec(2, 2),
origin: ex.vec(0, 1),
tint: ex.Color.Red,
rotation: 4
});
```
Also the animation helper `ex.Animation.fromSpriteSheetCoordinates()` you can now pass any valid `ex.GraphicOptions` to influence the sprite per frame
```typescript
const anim = ex.Animation.fromSpriteSheetCoordinates({
spriteSheet: ss,
frameCoordinates: [
{x: 0, y: 0, duration: 100, options: { flipHorizontal: true }},
{x: 1, y: 0, duration: 100, options: { flipVertical: true }},
{x: 2, y: 0, duration: 100},
{x: 3, y: 0, duration: 100}
],
strategy: ex.AnimationStrategy.Freeze
});
```