This is a huge release v0.29.0, we have a lot of big features that bring us closer to v1!
Join the community on discord!
There are breaking changes in this release, please review the migration guide from v0.28.x to v0.29.0
Features
-
Added new
ex.Tilemap.getOnScreenTiles()
method to help users access onscreen tiles for logic or other concerns. -
Added
ex.FontSource
resource typeconst fontSource = new ex.FontSource('/my-font.ttf', 'My Font') loader.addResource(fontSource) game.start(loader).then(() => { const font = fontSource.toFont() // returns ex.Font })
Font options can be defined either at the source or at the
toFont()
call. If defined in both,toFont(options)
will
override the options in theFontSource
.const fontSource = new ex.FontSource('/my-font.ttf', 'My Font', { filtering: ex.ImageFiltering.Pixel, size: 16, // set a default size }) const font = fontSource.toFont({ // override just the size size: 20, })
-
Added fullscreen after load feature! You can optionally provide a
fullscreenContainer
with a string id or an instance of theHTMLElement
new ex.Loader({ fullscreenAfterLoad: true, fullscreenContainer: document.getElementById('container') });
-
Added 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)
-
Experimental
ex.coroutine
for running code that changes over time, useful for modeling complex animation code. Coroutines return a promise when they are complete. You can think of eachyield
as a frame.- The result of a yield is the current elapsed time
- You can yield a number in milliseconds and it will wait that long before resuming
- You can yield a promise and it will wait until it resolves before resuming
const completePromise = coroutine(engine, function * () { let elapsed = 0; elapsed = yield 200; // frame 1 wait 200 ms before resuming elapsed = yield fetch('./some/data.json'); // frame 2 elapsed = yield; // frame 3 });
-
Added additional options in rayCast options
ignoreCollisionGroupAll: boolean
will ignore testing against anything with theCollisionGroup.All
which is the default for allfilter: (hit: RayCastHit) => boolean
will allow people to do arbitrary filtering on raycast results, this runs very last after all other collision group/collision mask decisions have been made
-
Added additional data
side
andlastContact
toonCollisionEnd
andcollisionend
events -
Added configuration option to
ex.PhysicsConfig
to configure composite collider onCollisionStart/End behavior -
Added configuration option to
ex.TileMap({ meshingLookBehind: Infinity })
which allows users to configure how far the TileMap looks behind for matching colliders (default is 10). -
Added Arcade Collision Solver bias to help mitigate seams in geometry that can cause problems for certain games.
ex.ContactSolveBias.None
No bias, current default behavior collisions are solved in the default distance orderex.ContactSolveBias.VerticalFirst
Vertical collisions are solved first (useful for platformers with up/down gravity)ex.ContactSolveBias.HorizontalFirst
Horizontal collisions are solved first (useful for games with left/right predominant forces)const engine = new ex.Engine({ ... physics: { solver: ex.SolverStrategy.Realistic, arcade: { contactSolveBias: ex.ContactSolveBias.VerticalFirst }, } })
-
Added Graphics
opacity
on the Actor constructornew ex.Actor({opacity: .5})
-
Added Graphics pixel
offset
on the Actor constructornew ex.Actor({offset: ex.vec(-15, -15)})
-
Added new
new ex.Engine({uvPadding: .25})
option to allow users using texture atlases in their sprite sheets to configure this to avoid texture bleed. This can happen if you're sampling from images meant for pixel art -
Added new antialias settings for pixel art! This allows for smooth subpixel rendering of pixel art without shimmer/fat-pixel artifacts.
- Use
new ex.Engine({pixelArt: true})
to opt in to all the right defaults to make this work!
- Use
-
Added new antialias configuration options to deeply configure how Excalibur does any antialiasing, or you can provide
antialiasing: true
/antialiasing: false
to use the old defaults.- Example;
const game = new ex.Engine({ antialiasing: { pixelArtSampler: false, filtering: ex.ImageFiltering.Pixel, nativeContextAntialiasing: false, canvasImageRendering: 'pixelated' } })
-
Added new
lineHeight
property onSpriteFont
andFont
to manually adjust the line height when rendering text. -
Added missing dual of
ex.GraphicsComponent.add()
, you can nowex.GraphicsComponent.remove(name)
; -
Added additional options to
ex.Animation.fromSpriteSheetCoordinates()
you can now pass any validex.GraphicOptions
to influence the sprite per frameconst 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 });
-
Added additional options to
ex.SpriteSheet.getSprite(..., options)
. You can pass any validex.GraphicOptions
to modify a copy of the sprite from the spritesheet.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 });
-
New simplified way to query entities
ex.World.query([MyComponentA, MyComponentB])
-
New way to query for tags on entities
ex.World.queryTags(['A', 'B'])
-
Systems can be added as a constructor to a world, if they are the world will construct and pass a world instance to them
world.add(MySystem); ... class MySystem extends System { query: Query; constructor(world: World) { super() this.query = world.query([MyComponent]); } update }
-
Added
RayCastHit
as part of every raycast not just the physics world query!- Additionally added the ray distance and the contact normal for the surface
-
Added the ability to log a message once to all log levels
debugOnce
infoOnce
warnOnce
errorOnce
fatalOnce
-
Added ability to load additional images into
ex.Material
s!const noise = new ex.ImageSource('./noise.avif'); loader.addResource(noise); var waterMaterial = game.graphicsContext.createMaterial({ name: 'water', fragmentSource: waterFrag, color: ex.Color.fromRGB(55, 0, 200, .6), images: { u_noise: noise } });
-
Scene Transition & Loader API, this gives you the ability to have first class support for individual scene resource loading and scene transitions.
-
Add or remove scenes by constructor
-
Add loaders by constructor
-
New
ex.DefaultLoader
type that allows for easier custom loader creation -
New
ex.Transition
type for building custom transitions -
New scene lifecycle to allow scene specific resource loading
onTransition(direction: "in" | "out") {...}
onPreLoad(loader: DefaultLoader) {...}
-
New async
goToScene()
API that allows overriding loaders/transitions between scenes -
Scenes now can have
async onInitialize
andasync onActivate
! -
New scenes director API that allows upfront definition of scenes/transitions/loaders
-
Example:
Defining scenes upfrontconst 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! } }) // Specify the boot loader & first scene transition from loader game.start('scene1', { inTransition: new ex.FadeInOut({duration: 500, direction: 'in', color: ex.Color.ExcaliburBlue}) loader: boot, });
- Scene specific input API so that you can add input handlers that only fire when a scene is active!
class SceneWithInput extends ex.Scene { onInitialize(engine: ex.Engine<any>): void { this.input.pointers.on('down', () => { console.log('pointer down from scene1'); }); } } class OtherSceneWithInput extends ex.Scene { onInitialize(engine: ex.Engine<any>): void { this.input.pointers.on('down', () => { console.log('pointer down from scene2'); }); } }
-
Breaking Changes
-
ex.Engine.goToScene
's second argument now takesGoToOptions
instead of just scene activation data{ /** * Optionally supply scene activation data passed to Scene.onActivate */ sceneActivationData?: TActivationData, /** * Optionally supply destination scene "in" transition, this will override any previously defined transition */ destinationIn?: Transition, /** * Optionally supply source scene "out" transition, this will override any previously defined transition */ sourceOut?: Transition, /** * Optionally supply a different loader for the destination scene, this will override any previously defined loader */ loader?: DefaultLoader }
-
ex.Physics
static is marked as deprecated, configuring these setting will move to theex.Engine({...})
constructorconst engine = new ex.Engine({ ... physics: { solver: ex.SolverStrategy.Realistic, gravity: ex.vec(0, 20), arcade: { contactSolveBias: ex.ContactSolveBias.VerticalFirst }, } })
-
Changed the
Font
default base align toTop
this is more in line with user expectations. This does change the default rendering to the top left corner of the font instead of the bottom left. -
Remove confusing Graphics Layering from
ex.GraphicsComponent
, recommend we use theex.GraphicsGroup
to manage this behavior- Update
ex.GraphicsGroup
to be consistent and useoffset
instead ofpos
for graphics relative positioning
- Update
-
ECS implementation has been updated to remove the "stringly" typed nature of components & systems
- For average users of Excalibur folks shouldn't notice any difference
- For folks leveraging the ECS, Systems/Components no longer have type parameters based on strings. The type itself is used to track changes.
class MySystem extends System<'ex.component'>
becomesclass MySystem extends System
class MyComponent extends Component<'ex.component'>
becomesclass MyComponent extends Component
ex.System.update(elapsedMs: number)
is only passed an elapsed time
-
Prevent people from inadvertently overriding
update()
inex.Scene
andex.Actor
. This method can still be overridden with the//@ts-ignore
pragma -
ex.SpriteSheet.getSprite(...)
will now throw on invalid sprite coordinates, this is likely always an error and a warning is inappropriate. This also has the side benefit that you will always get a definite type out of the method.
Fixed
- Performance improvement in
ex.TileMap
finding onscreen tiles is now BLAZINGLY FAST thanks to a suggestion from Kristen Maeyvn in the Discord.- TileMaps no longer need a quad tree, we can calculate the onscreen tiles with math by converting the screen into tilemap space 😎
- Fixed bug where
ex.TileMap.getTileByPoint()
did not take into account the rotation/scale of the tilemap. - Fixes issue where mis-matched coordinate planes on parent/children caused bizarre issues. Now children are forced to inherit their parent's coordinate plane, it will always be the coordinate plane of the top most parent.
- Fixed issue with Log ScreenAppender utility where it was not positioned correctly, you can now deeply configure it!
export interface ScreenAppenderOptions { engine: Engine; /** * Optionally set the width of the overlay canvas */ width?: number; /** * Optionally set the height of the overlay canvas */ height?: number; /** * Adjust the text offset from the left side of the screen */ xPos?: number; /** * Provide a text color */ color?: Color; /** * Optionally set the CSS zindex of the overlay canvas */ zIndex?: number; }
- Fixed errant warning about resolution when using
pixelRatio
on low res games to upscale - Fixes an issue where a collider that was part of a contact that was deleted did not fire a collision end event, this was unexpected
- Fixes an issue where you may want to have composite colliders behave as constituent colliders for the purposes of start/end collision events. A new property is added to physics config, the current behavior is the default which is
'together'
, this means the whole composite collider is treated as 1 collider for onCollisionStart/onCollisionEnd. Now you can configure aseparate
which will fire onCollisionStart/onCollisionEnd for every separate collider included in the composite (useful if you are building levels or things with gaps that you need to disambiguate). You can also configure this on a per composite level to mix and matchCompositeCollider.compositeStrategy
- Fixed issue where particles would have an errant draw if using a particle sprite
- Fixed issue where a null/undefined graphics group member graphic would cause a crash, now logs a warning.
- Fixed issue where Actor built in components could not be extended because of the way the Actor based type was built.
- Actors now use instance properties for built-ins instead of getters
- With the ECS refactor you can now subtype built-in
Components
and.get(Builtin)
will return the correct subtype.
class MyBodyComponent extends ex.BodyComponent {} class MyActor extends ex.Actor { constructor() { super({}) this.removeComponent(ex.BodyComponent); this.addComponent(new MyBodyComponent()) } } const myActor = new MyActor(); const myBody = myActor.get(ex.BodyComponent); // Returns the new MyBodyComponent subtype!
- Fixed issue with
snapToPixel
where theex.Camera
was not snapping correctly - Fixed issue where using CSS transforms on the canvas confused Excalibur pointers
- Fixed issue with *AndFill suffixed [[DisplayModes]]s where content area offset was not accounted for in world space
- Fixed issue where
ex.Sound.getTotalPlaybackDuration()
would crash if not loaded, now logs friendly warning - Fixed issue where an empty constructor on
new ex.Label()
would crash
Raw Changes
New Features
- feat: Excalibur Scene Transitions & Loaders by @eonarheim in #2790
- feat: Docs vNext (Docusaurus Migration) by @kamranayub in #2793
- feat: Allow additional images in Materials by @eonarheim in #2892
- feat: Raycast improvements with contact normal by @eonarheim in #2899
- feat: Implement SpriteSheet getSprite options by @eonarheim in #2901
- feat: Scene input API by @eonarheim in #2889
- feat: Implement Font lineHeight by @eonarheim in #2905
- feat: Pixel Art Sampler & Antialiasing Improvements by @eonarheim in #2739
- feat: Implement Contact Bias Solver order for seams + Deprecate ex.Physics Static by @eonarheim in #2926
- feat: ex.FontSource resource type by @mattjennings in #2934
- feat: Debug Draw Static Helpers by @eonarheim in #2929
- Added fullscreen request to Loader button event, and modified Loader config parameters to accomodate by @jyoung4242 in #2907
New Refreshed Doc Site! https://excaliburjs.com
- Docs cleanup work part one: 00-welcome, 00-your-first-game, 01-installation, 03-bundlers, and 03-actors by @cdelstad in #2890
- docs: Fix symbol linking + Pointer docs improvements by @kamranayub in #2928
- refactor(showcase): update retroski demo url by @mathieuher in #2930
Breaking Changes
- refactor: Graphics Component simplification by @eonarheim in #2893
- refactor: New ECS simplification by @eonarheim in #2900
- change: Pointers will use graphics bounds by default by @eonarheim in #2897
Fixes
-
fix: Add friendly error if canvasElementId missing by @jyoung4242 in #2885
-
fix: Issue with pointer events on screen elements by @eonarheim in #2902
-
fix: Empty label constructor by @eonarheim in #2904
-
fix: Removed redundant playbackend event from playback resume by @jyoung4242 in #2906
-
fix: Allow CSS transforms on canvas by @eonarheim in #2910
-
fix: [#2896] Allow component subtypes to work on extend Actors by @eonarheim in #2924
-
fix: [#2799] Change default Font Base Align to Top by @eonarheim in #2925
-
fix: Windows tests crash due to memory constraints by @eonarheim in #2927
-
fix: Fire collisionend when collider deleted + Composite collision start/end strategy by @eonarheim in #2931
-
fix: Scene transition bugs + Coroutines by @eonarheim in #2932
-
fix: ScreenAppender Logger by @eonarheim in #2935
-
fix: [#2933] Children inherit their coordinate plane by @eonarheim in #2936
-
chore: Update dependency @types/node to v20.11.14 by @renovate in #2909
-
chore: Update dependency @types/react-color to v3.0.11 by @renovate in #2912
-
chore: Update dependency @types/node to v20.11.15 by @renovate in #2915
-
chore: Update dependency css-loader to v6.10.0 by @renovate in #2914
-
chore: Update dependency @types/react to v18.2.51 by @renovate in #2911
-
chore: Update dependency karma-webpack to v5.0.1 by @renovate in #2919
-
chore: Update dependency core-js to v3.35.1 by @renovate in #2913
-
chore: Update dependency @types/node to v20.11.16 by @renovate in #2920
-
chore: Update dependency docusaurus-plugin-typedoc-api to v4.1.0 by @renovate in #2916
-
chore: Update dependency eslint to v8.56.0 by @renovate in #2917
-
chore: Update dependency eslint-plugin-jsdoc to v46.10.1 by @renovate in #2921
-
chore: Update dependency eslint-config-prettier to v9.1.0 by @renovate in #2918
New Contributors
- @jyoung4242 made their first contribution in #2885
- @cdelstad made their first contribution in #2890
- @mathieuher made their first contribution in #2930
Additional Thanks
Many many thanks to everyone who has helped with this release, find bugs, and improve the documentation especially to those on the Discord! In no particular order:
- @mahbarahona for making the new create-excalibur CLI utility https://github.com/excaliburjs/create-excalibur !!!
- @jyoung4242 (mookie) for building new features into our loader and exploring WFC
- @kamranayub for all the hard work on the docsite!
- kristen.maeyvn (discord) for suggesting a HUGE performance improvement that was implemented in TileMaps
- @mattjennings for spending many many hours on discord with me debugging and improving collisions/physics!
- @drewconley for being a trusted sounding board for this release!
- @cdelstad for meeting IRL and chatting about the future of Excalibur!
- @Hugo-Dz for collaborating on SpriteFusion with Excalibur!
- And many many more people on the Discord that have made the community friendly and wonderful 🥰
Full Changelog: v0.28.6...v0.29.0