🎮 Your friendly TypeScript 2D game engine for the web 🗡️
This is a maintenance release with only bug fixes!
components
option optional by @muhajirdev in https://github.com/excaliburjs/Excalibur/pull/3027
Full Changelog: https://github.com/excaliburjs/Excalibur/compare/v0.29.2...v0.29.3
This is a medium-ish release, with a ton of community contributions especially in the bugfix realm!
ex.ImageSource
with the new ex.ImageWrapping.Clamp
(default), ex.ImageWrapping.Repeat
, and ex.ImageWrapping.Mirror
.
const image = new ex.ImageSource('path/to/image.png', {
filtering: ex.ImageFiltering.Pixel,
wrapping: {
x: ex.ImageWrapping.Repeat,
y: ex.ImageWrapping.Repeat,
}
});
ex.TileMap
's and individual ex.Tile
'sex.IsometricMap
's and individual ex.IsometricTile
'suseAnchor
parameter to ex.GraphicsGroup
to allow users to opt out of anchor based positioning, if set to false all graphics members
will be positioned with the top left of the graphic at the actor's position.
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),
},
],
});
ex.coroutine
overloads, you need not pass engine as long as you are in an Excalibur lifecycle
const result = ex.coroutine(function* () {...});
ex.coroutine
overloads, you need not pass engine as long as you are in an Excalibur lifecycle
const result = ex.coroutine({myThis: 'cool'}, function* () {...});
ex.coroutine
timing parameter to schedule when they are updated
const result = ex.coroutine(engine, function * () {...}, { timing: 'postupdate' })
GraphicsComponent.bounds
which will report the world bounds of the graphic if applicable!ex.Vector.EQUALS_EPSILON
to configure the ex.Vector.equals(v)
thresholdconst game = new ex.Engine({
handleContextLost: (e) => {...},
handleContextRestored: (e) => {...}
})
ex.TileMap
culling did not work properly when using fixed updates lower than refresh rateex.FontSource().toFont(options)
ex.Loader
start button position when using CSS transformsex.Loader
DisplayMode.FillContainer
DisplayMode.FitContainer
DisplayMode.FitContainerAndFill
DisplayMode.FitContainerAndZoom
ex.ParticleEmitter
z-index did not propagate to particlestransform.scale = v
and transform.scale.setTo(x, y)
ex.coroutine
TypeScript type to include yielding undefined
Color.toHex()
produced invalid strings if the channel values are negative or fractional, or if the alpha channel was different than 1ex.Loader
viewport/resolution internal configurationFull Changelog: https://github.com/excaliburjs/Excalibur/compare/v0.29.1...v0.29.2
Small release to fix transition bug!
Full Changelog: https://github.com/excaliburjs/Excalibur/compare/v0.29.0...v0.29.1
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
Added new ex.Tilemap.getOnScreenTiles()
method to help users access onscreen tiles for logic or other concerns.
Added ex.FontSource
resource type
const 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 the FontSource
.
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 the HTMLElement
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 each yield
as a frame.
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 the CollisionGroup.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 madeAdded additional data side
and lastContact
to onCollisionEnd
and collisionend
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 constructor new ex.Actor({opacity: .5})
Added Graphics pixel offset
on the Actor constructor new 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.
new ex.Engine({pixelArt: true})
to opt in to all the right defaults to make this work!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.
const game = new ex.Engine({
antialiasing: {
pixelArtSampler: false,
filtering: ex.ImageFiltering.Pixel,
nativeContextAntialiasing: false,
canvasImageRendering: 'pixelated'
}
})
Added new lineHeight
property on SpriteFont
and Font
to manually adjust the line height when rendering text.
Added missing dual of ex.GraphicsComponent.add()
, you can now ex.GraphicsComponent.remove(name)
;
Added additional options to ex.Animation.fromSpriteSheetCoordinates()
you can now pass any valid ex.GraphicOptions
to influence the sprite per frame
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
});
Added additional options to ex.SpriteSheet.getSprite(..., options)
. You can pass any valid ex.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!
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
and async onActivate
!
New scenes director API that allows upfront definition of scenes/transitions/loaders
Example: Defining scenes upfront
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!
}
})
// 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,
});
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');
});
}
}
ex.Engine.goToScene
's second argument now takes GoToOptions
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 the ex.Engine({...})
constructor
const 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 to Top
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 the ex.GraphicsGroup
to manage this behavior
ex.GraphicsGroup
to be consistent and use offset
instead of pos
for graphics relative positioningECS implementation has been updated to remove the "stringly" typed nature of components & systems
class MySystem extends System<'ex.component'>
becomes class MySystem extends System
class MyComponent extends Component<'ex.component'>
becomes class MyComponent extends Component
ex.System.update(elapsedMs: number)
is only passed an elapsed timePrevent people from inadvertently overriding update()
in ex.Scene
and ex.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.
ex.TileMap
finding onscreen tiles is now BLAZINGLY FAST thanks to a suggestion from Kristen Maeyvn in the Discord.
ex.TileMap.getTileByPoint()
did not take into account the rotation/scale of the tilemap.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;
}
pixelRatio
on low res games to upscale'together'
, this means the whole composite collider is treated as 1 collider for onCollisionStart/onCollisionEnd. Now you can configure a separate
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 match CompositeCollider.compositeStrategy
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!
snapToPixel
where the ex.Camera
was not snapping correctlyex.Sound.getTotalPlaybackDuration()
would crash if not loaded, now logs friendly warningnew ex.Label()
would crashfix: Add friendly error if canvasElementId missing by @jyoung4242 in https://github.com/excaliburjs/Excalibur/pull/2885
fix: Issue with pointer events on screen elements by @eonarheim in https://github.com/excaliburjs/Excalibur/pull/2902
fix: Empty label constructor by @eonarheim in https://github.com/excaliburjs/Excalibur/pull/2904
fix: Removed redundant playbackend event from playback resume by @jyoung4242 in https://github.com/excaliburjs/Excalibur/pull/2906
fix: Allow CSS transforms on canvas by @eonarheim in https://github.com/excaliburjs/Excalibur/pull/2910
fix: [#2896] Allow component subtypes to work on extend Actors by @eonarheim in https://github.com/excaliburjs/Excalibur/pull/2924
fix: [#2799] Change default Font Base Align to Top by @eonarheim in https://github.com/excaliburjs/Excalibur/pull/2925
fix: Windows tests crash due to memory constraints by @eonarheim in https://github.com/excaliburjs/Excalibur/pull/2927
fix: Fire collisionend when collider deleted + Composite collision start/end strategy by @eonarheim in https://github.com/excaliburjs/Excalibur/pull/2931
fix: Scene transition bugs + Coroutines by @eonarheim in https://github.com/excaliburjs/Excalibur/pull/2932
fix: ScreenAppender Logger by @eonarheim in https://github.com/excaliburjs/Excalibur/pull/2935
fix: [#2933] Children inherit their coordinate plane by @eonarheim in https://github.com/excaliburjs/Excalibur/pull/2936
chore: Update dependency node to v20 by @renovate in https://github.com/excaliburjs/Excalibur/pull/2831
chore: Update babel monorepo by @renovate in https://github.com/excaliburjs/Excalibur/pull/2908
chore: Update dependency @types/node to v20.11.14 by @renovate in https://github.com/excaliburjs/Excalibur/pull/2909
chore: Update dependency @types/react-color to v3.0.11 by @renovate in https://github.com/excaliburjs/Excalibur/pull/2912
chore: Update dependency @types/node to v20.11.15 by @renovate in https://github.com/excaliburjs/Excalibur/pull/2915
chore: Update dependency css-loader to v6.10.0 by @renovate in https://github.com/excaliburjs/Excalibur/pull/2914
chore: Update dependency @types/react to v18.2.51 by @renovate in https://github.com/excaliburjs/Excalibur/pull/2911
chore: Update dependency karma-webpack to v5.0.1 by @renovate in https://github.com/excaliburjs/Excalibur/pull/2919
chore: Update dependency core-js to v3.35.1 by @renovate in https://github.com/excaliburjs/Excalibur/pull/2913
chore: Update dependency @types/node to v20.11.16 by @renovate in https://github.com/excaliburjs/Excalibur/pull/2920
chore: Update dependency docusaurus-plugin-typedoc-api to v4.1.0 by @renovate in https://github.com/excaliburjs/Excalibur/pull/2916
chore: Update dependency eslint to v8.56.0 by @renovate in https://github.com/excaliburjs/Excalibur/pull/2917
chore: Update dependency eslint-plugin-jsdoc to v46.10.1 by @renovate in https://github.com/excaliburjs/Excalibur/pull/2921
chore: Update dependency eslint-config-prettier to v9.1.0 by @renovate in https://github.com/excaliburjs/Excalibur/pull/2918
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:
Full Changelog: https://github.com/excaliburjs/Excalibur/compare/v0.28.6...v0.29.0
Bugfix release! This release is slightly different cut from the branch release/v0.28.x instead of main, this is because features that are not ready to ship are in the mainline currently and will be shipped as part of v0.29.0
Also big thanks to @jyoung4242! This was his first contribution to Excalibur!
Full Changelog: https://github.com/excaliburjs/Excalibur/compare/v0.28.6...v0.28.7
Another small release! A few bug fixes and some small features. Thanks @mattjennings for being MVP and testing all these bugs!
ex.IsometricTile.data
this brings it into feature parity with normal ex.Tile.data
Actor.graphics.onPreTransformDraw
with the corresponding event .on('pretransformdraw')
Actor.graphics.onPostTransformDraw
with the corresponding event .on('posttransformdraw')
ex.Animation
ex.Animation.currentFrameTimeLeft
will return the current time in milliseconds left in the currentex.Animation.goToFrame(frameNumber: number, duration?: number)
now accepts an optional duration for the target frameex.Animation.speed
can set the speed multiplier on an animation 1 = 1x speed, 2 = 2x speed.Full Changelog: https://github.com/excaliburjs/Excalibur/compare/v0.28.5...v0.28.6
Another small release! A few bug fixes, QOL improvements, and fixes supporting the new Tiled plugin!
Full Changelog: https://github.com/excaliburjs/Excalibur/compare/v0.28.4...v0.28.5
Another small release! A few bug fixes and some enhancements to materials!
https://github.com/excaliburjs/Excalibur/assets/612071/ade92ec3-caff-4835-8410-74f4ebbbe421
2 new uniforms
u_screen_texture
- This is the texture of the screen right before the material draw callu_time_ms
- This is the milliseconds since page navigation (performance.now()
under the hood)2 new attribute/varyings
a_screenuv
- The vertex attribute corresponding to the screen uv relative to the current graphicv_screenuv
- The fragment varying corresponding to the screen uv relative to the current graphicFinally there is a new convenience api for updating shader values in materials. .update(shader => {...})
game.input.pointers.primary.on('move', evt => {
heartActor.pos = evt.worldPos;
swirlMaterial.update(shader => {
shader.trySetUniformFloatVector('iMouse', evt.worldPos);
});
});
Full Changelog: https://github.com/excaliburjs/Excalibur/compare/v0.28.3...v0.28.4
Small release, fixed a couple bugs!
Full Changelog: https://github.com/excaliburjs/Excalibur/compare/v0.28.2...v0.28.3
This was mostly a bug fix release, lots of small fixes! No breaking changes. Notably there are some performance improvements to TileMap by utilizing a QuadTree to help offscreen culling.
ex.Engine.version
to report the current excalibur version build string by @eonarheim in https://github.com/excaliburjs/Excalibur/pull/2822
Full Changelog: https://github.com/excaliburjs/Excalibur/compare/v0.28.0...v0.28.2