From c504f283613ff11a890aed5bb4d2c462603d7cf3 Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Mon, 5 Jan 2026 08:45:46 -0600 Subject: [PATCH] docs: composite explainer + format (#3652) --- .../01-Custom Graphics/code/sample1.ts | 14 +-- .../01-Custom Graphics/code/sample2.ts | 22 ++--- .../01-Custom Graphics/code/sample3.ts | 24 ++--- .../01-Custom Graphics/code/sample4.ts | 36 ++++---- .../01-Custom Graphics/code/sample5.ts | 36 ++++---- .../01-colliders-and-shapes.mdx | 41 +++++++++ .../05-uscasee2 tilemap and triggers.mdx | 43 ++++++++- .../03-Colliders Primer/code/usecase1.ts | 64 +++++++------- .../03-Colliders Primer/code/usecase2.ts | 51 +++++------ .../03-Colliders Primer/code/usecase3.ts | 88 +++++++++---------- 10 files changed, 251 insertions(+), 168 deletions(-) diff --git a/site/docs/00-tutorials/How-to's/01-Custom Graphics/code/sample1.ts b/site/docs/00-tutorials/How-to's/01-Custom Graphics/code/sample1.ts index 549e69c5..73535557 100644 --- a/site/docs/00-tutorials/How-to's/01-Custom Graphics/code/sample1.ts +++ b/site/docs/00-tutorials/How-to's/01-Custom Graphics/code/sample1.ts @@ -18,18 +18,18 @@ export class HealthBar extends ex.Actor { Engine Setup *******************************/ const game = new ex.Engine({ - canvasElementId: 'preview-canvas', - displayMode: ex.DisplayMode.Fixed, - width: 500, - height: 500, - pixelArt: true + canvasElementId: 'preview-canvas', + displayMode: ex.DisplayMode.Fixed, + width: 500, + height: 500, + pixelArt: true }); /****************************** Setting up the parent actor *******************************/ const player = new ex.Actor({ x: 300, y: 200, width: 32, height: 32, color: ex.Color.Red }); -player.addChild(new HealthBar(ex.vec(0, -24),ex.vec(36, 6), 100)); +player.addChild(new HealthBar(ex.vec(0, -24), ex.vec(36, 6), 100)); game.add(player); game.start(); @@ -38,4 +38,4 @@ game.start(); make it easier to see *******************************/ game.currentScene.camera.strategy.lockToActor(player); -game.currentScene.camera.zoom = 2.5; \ No newline at end of file +game.currentScene.camera.zoom = 2.5; diff --git a/site/docs/00-tutorials/How-to's/01-Custom Graphics/code/sample2.ts b/site/docs/00-tutorials/How-to's/01-Custom Graphics/code/sample2.ts index 56910bf8..a64057e4 100644 --- a/site/docs/00-tutorials/How-to's/01-Custom Graphics/code/sample2.ts +++ b/site/docs/00-tutorials/How-to's/01-Custom Graphics/code/sample2.ts @@ -16,7 +16,7 @@ class HealthBarGraphic extends ex.Graphic { ctx: CanvasRenderingContext2D | null; // dirty flag to trigger a redraw so we don't redraw every frame - dirtyFlag:boolean = true; + dirtyFlag: boolean = true; constructor() { super({ @@ -28,13 +28,13 @@ class HealthBarGraphic extends ex.Graphic { this.cnv = document.createElement("canvas"); this.cnv.width = this.width * this.drawScale; this.cnv.height = this.height * this.drawScale; - this.ctx = this.cnv.getContext("2d"); + this.ctx = this.cnv.getContext("2d"); } - clone(): HealthBarGraphic{ + clone(): HealthBarGraphic { return new HealthBarGraphic(); } - + protected _drawImage( ex: ex.ExcaliburGraphicsContext, x: number, @@ -63,18 +63,18 @@ export class HealthBar extends ex.Actor { Engine Setup *******************************/ const game = new ex.Engine({ - canvasElementId: 'preview-canvas', - displayMode: ex.DisplayMode.Fixed, - width: 500, - height: 500, - pixelArt: true + canvasElementId: 'preview-canvas', + displayMode: ex.DisplayMode.Fixed, + width: 500, + height: 500, + pixelArt: true }); /****************************** Setting up the parent actor *******************************/ const player = new ex.Actor({ x: 300, y: 200, width: 32, height: 32, color: ex.Color.Red }); -player.addChild(new HealthBar(ex.vec(0, -24),ex.vec(36, 6), 100)); +player.addChild(new HealthBar(ex.vec(0, -24), ex.vec(36, 6), 100)); game.add(player); game.start(); @@ -83,4 +83,4 @@ game.start(); make it easier to see *******************************/ game.currentScene.camera.strategy.lockToActor(player); -game.currentScene.camera.zoom = 2.5; \ No newline at end of file +game.currentScene.camera.zoom = 2.5; diff --git a/site/docs/00-tutorials/How-to's/01-Custom Graphics/code/sample3.ts b/site/docs/00-tutorials/How-to's/01-Custom Graphics/code/sample3.ts index 66924212..87a3eea1 100644 --- a/site/docs/00-tutorials/How-to's/01-Custom Graphics/code/sample3.ts +++ b/site/docs/00-tutorials/How-to's/01-Custom Graphics/code/sample3.ts @@ -16,7 +16,7 @@ class HealthBarGraphic extends ex.Graphic { ctx: CanvasRenderingContext2D | null; // dirty flag to trigger a redraw so we don't redraw every frame - dirtyFlag:boolean = true; + dirtyFlag: boolean = true; constructor() { super({ @@ -28,13 +28,13 @@ class HealthBarGraphic extends ex.Graphic { this.cnv = document.createElement("canvas"); this.cnv.width = this.width * this.drawScale; this.cnv.height = this.height * this.drawScale; - this.ctx = this.cnv.getContext("2d"); + this.ctx = this.cnv.getContext("2d"); } - clone(): HealthBarGraphic{ + clone(): HealthBarGraphic { return new HealthBarGraphic(); } - + protected _drawImage(ex: ex.ExcaliburGraphicsContext, x: number, y: number): void { /* Dirty Flag is used to tell the graphic something's changed @@ -58,7 +58,7 @@ class HealthBarGraphic extends ex.Graphic { const fillWidth = (this.width - border); const fillHeight = this.height - border; // stay inside border ctx.fillStyle = this.safeColor.toString(); - + ctx.fillRect( inset, // x inset, // y @@ -102,18 +102,18 @@ export class HealthBar extends ex.Actor { Engine Setup *******************************/ const game = new ex.Engine({ - canvasElementId: 'preview-canvas', - displayMode: ex.DisplayMode.Fixed, - width: 500, - height: 500, - pixelArt: true + canvasElementId: 'preview-canvas', + displayMode: ex.DisplayMode.Fixed, + width: 500, + height: 500, + pixelArt: true }); /****************************** Setting up the parent actor *******************************/ const player = new ex.Actor({ x: 300, y: 200, width: 32, height: 32, color: ex.Color.Red }); -player.addChild(new HealthBar(ex.vec(0, -24),ex.vec(36, 6), 100)); +player.addChild(new HealthBar(ex.vec(0, -24), ex.vec(36, 6), 100)); game.add(player); game.start(); @@ -122,4 +122,4 @@ game.start(); make it easier to see *******************************/ game.currentScene.camera.strategy.lockToActor(player); -game.currentScene.camera.zoom = 2.5; \ No newline at end of file +game.currentScene.camera.zoom = 2.5; diff --git a/site/docs/00-tutorials/How-to's/01-Custom Graphics/code/sample4.ts b/site/docs/00-tutorials/How-to's/01-Custom Graphics/code/sample4.ts index 39bc015e..f60e6553 100644 --- a/site/docs/00-tutorials/How-to's/01-Custom Graphics/code/sample4.ts +++ b/site/docs/00-tutorials/How-to's/01-Custom Graphics/code/sample4.ts @@ -16,7 +16,7 @@ class HealthBarGraphic extends ex.Graphic { ctx: CanvasRenderingContext2D | null; // dirty flag to trigger a redraw so we don't redraw every frame - dirtyFlag:boolean = true; + dirtyFlag: boolean = true; percent: number = 1.0; constructor() { @@ -29,18 +29,18 @@ class HealthBarGraphic extends ex.Graphic { this.cnv = document.createElement("canvas"); this.cnv.width = this.width * this.drawScale; this.cnv.height = this.height * this.drawScale; - this.ctx = this.cnv.getContext("2d"); + this.ctx = this.cnv.getContext("2d"); } updatePercent(percentfill: number) { - this.percent = percentfill; - this.dirtyFlag = true; -} + this.percent = percentfill; + this.dirtyFlag = true; + } - clone(): HealthBarGraphic{ + clone(): HealthBarGraphic { return new HealthBarGraphic(); } - + protected _drawImage(ex: ex.ExcaliburGraphicsContext, x: number, y: number): void { /* Dirty Flag is used to tell the graphic something's changed @@ -63,7 +63,7 @@ class HealthBarGraphic extends ex.Graphic { const inset = border / 2; // or border if you prefer full padding const fillWidth = (this.width - border) * this.percent; const fillHeight = this.height - border; // stay inside border - + // === Changing colors === if (this.percent > 0.5) { ctx.fillStyle = this.safeColor.toString(); @@ -72,7 +72,7 @@ class HealthBarGraphic extends ex.Graphic { } else { ctx.fillStyle = this.warningColor.toString(); } - + ctx.fillRect( inset, // x inset, // y @@ -121,7 +121,7 @@ export class HealthBar extends ex.Actor { this.currentHealth -= damageAmount; //renew health as a part of the demo - if(this.currentHealth < 0) this.currentHealth = this.maxHealth; + if (this.currentHealth < 0) this.currentHealth = this.maxHealth; let percent = this.currentHealth / this.maxHealth; (this.graphics.current as HealthBarGraphic).updatePercent(percent); @@ -132,18 +132,18 @@ export class HealthBar extends ex.Actor { Engine Setup *******************************/ const game = new ex.Engine({ - canvasElementId: 'preview-canvas', - displayMode: ex.DisplayMode.Fixed, - width: 500, - height: 500, - pixelArt: true + canvasElementId: 'preview-canvas', + displayMode: ex.DisplayMode.Fixed, + width: 500, + height: 500, + pixelArt: true }); /****************************** Setting up the parent actor *******************************/ const player = new ex.Actor({ x: 300, y: 200, width: 32, height: 32, color: ex.Color.Red }); -player.addChild(new HealthBar(ex.vec(0, -24),ex.vec(36, 6), 100)); +player.addChild(new HealthBar(ex.vec(0, -24), ex.vec(36, 6), 100)); game.add(player); game.start(); @@ -152,7 +152,7 @@ game.start(); *******************************/ game.input.keyboard.on('press', (e: ex.KeyEvent) => { if (e.key === ex.Keys.Enter) { - (player.children[0] as HealthBar).takeDamage(10); + (player.children[0] as HealthBar).takeDamage(10); } }) @@ -161,4 +161,4 @@ game.input.keyboard.on('press', (e: ex.KeyEvent) => { make it easier to see *******************************/ game.currentScene.camera.strategy.lockToActor(player); -game.currentScene.camera.zoom = 2.5; \ No newline at end of file +game.currentScene.camera.zoom = 2.5; diff --git a/site/docs/00-tutorials/How-to's/01-Custom Graphics/code/sample5.ts b/site/docs/00-tutorials/How-to's/01-Custom Graphics/code/sample5.ts index 90818e2a..8686cf25 100644 --- a/site/docs/00-tutorials/How-to's/01-Custom Graphics/code/sample5.ts +++ b/site/docs/00-tutorials/How-to's/01-Custom Graphics/code/sample5.ts @@ -16,7 +16,7 @@ class HealthBarGraphic extends ex.Graphic { ctx: CanvasRenderingContext2D | null; // dirty flag to trigger a redraw so we don't redraw every frame - dirtyFlag:boolean = true; + dirtyFlag: boolean = true; percent: number = 1.0; newpercent: number = 1.0; changeRate: number = 0.01; @@ -31,19 +31,19 @@ class HealthBarGraphic extends ex.Graphic { this.cnv = document.createElement("canvas"); this.cnv.width = this.width * this.drawScale; this.cnv.height = this.height * this.drawScale; - this.ctx = this.cnv.getContext("2d"); + this.ctx = this.cnv.getContext("2d"); } updatePercent(percentfill: number) { - this.newpercent = percentfill; - if(this.newpercent === this.percent) return; - this.dirtyFlag = true; -} + this.newpercent = percentfill; + if (this.newpercent === this.percent) return; + this.dirtyFlag = true; + } - clone(): HealthBarGraphic{ + clone(): HealthBarGraphic { return new HealthBarGraphic(); } - + protected _drawImage(ex: ex.ExcaliburGraphicsContext, x: number, y: number): void { if (this.dirtyFlag && this.ctx) { const ctx = this.ctx; @@ -128,11 +128,11 @@ export class HealthBar extends ex.Actor { this.graphics.use(new HealthBarGraphic()); } - takeDamage(damageAmount: number){ + takeDamage(damageAmount: number) { this.currentHealth -= damageAmount; //renew health as a part of the demo - if(this.currentHealth < 0) this.currentHealth = this.maxHealth; + if (this.currentHealth < 0) this.currentHealth = this.maxHealth; let percent = this.currentHealth / this.maxHealth; (this.graphics.current as HealthBarGraphic).updatePercent(percent); @@ -143,18 +143,18 @@ export class HealthBar extends ex.Actor { Engine Setup *******************************/ const game = new ex.Engine({ - canvasElementId: 'preview-canvas', - displayMode: ex.DisplayMode.Fixed, - width: 500, - height: 500, - pixelArt: true + canvasElementId: 'preview-canvas', + displayMode: ex.DisplayMode.Fixed, + width: 500, + height: 500, + pixelArt: true }); /****************************** Setting up the parent actor *******************************/ const player = new ex.Actor({ x: 300, y: 200, width: 32, height: 32, color: ex.Color.Red }); -player.addChild(new HealthBar(ex.vec(0, -24),ex.vec(36, 6), 100)); +player.addChild(new HealthBar(ex.vec(0, -24), ex.vec(36, 6), 100)); game.add(player); game.start(); @@ -163,7 +163,7 @@ game.start(); *******************************/ game.input.keyboard.on('press', (e: ex.KeyEvent) => { if (e.key === ex.Keys.Enter) { - (player.children[0] as HealthBar).takeDamage(10); + (player.children[0] as HealthBar).takeDamage(10); } }) @@ -172,4 +172,4 @@ game.input.keyboard.on('press', (e: ex.KeyEvent) => { make it easier to see *******************************/ game.currentScene.camera.strategy.lockToActor(player); -game.currentScene.camera.zoom = 2.5; \ No newline at end of file +game.currentScene.camera.zoom = 2.5; diff --git a/site/docs/00-tutorials/How-to's/03-Colliders Primer/01-colliders-and-shapes.mdx b/site/docs/00-tutorials/How-to's/03-Colliders Primer/01-colliders-and-shapes.mdx index 99c72095..f5daf679 100644 --- a/site/docs/00-tutorials/How-to's/03-Colliders Primer/01-colliders-and-shapes.mdx +++ b/site/docs/00-tutorials/How-to's/03-Colliders Primer/01-colliders-and-shapes.mdx @@ -76,6 +76,47 @@ Excalibur provides several shapes to cover typical game needs: Combines multiple shapes into a single collider. Allows complex actors to have different interaction zones, like a player with a hitbox and a separate trigger zone for area effects. Useful for drawing keep-in or keep-out boundaries for an area. +:::tip + +A composite collider is a way of treating multiple pieces of geometry as one collider, this means there is a choice about how to resolve collisions. + +### Composite Collision Resolution Strategy + +1. 'together' - Treat all geometry as part of the same body, meaning only 1 contact is possible for the composite, and only 1 event is generated. +2. 'separate' - Treat geometry as if it were part of separate bodies, a contact is possible for every piece of geometry, an event is generated for every contact. + +Set the default for all composites as engine config + +```typescript +const game = new ex.Engine({ + ... + physics: { + colliders: { + compositeStrategy: 'separate' + } + } +}); + +``` + +Or per composite collider, this will override any default config + +```typescript +const tm = new ex.TileMap({...}); +// TileMaps only have CompositeColliders +const comp = tm.collider.get() as ex.CompositeCollider; +comp.compositeStrategy = 'separate'; + +``` + +### Rule of Thumb + +* If your composite is connected and has no large gaps, use 'together' +* If your composite is a level like a platformer with large gaps, use 'separate' + +::: + + ### Adding a Shape to an Actor Let's discuss the two ways this can happen. diff --git a/site/docs/00-tutorials/How-to's/03-Colliders Primer/05-uscasee2 tilemap and triggers.mdx b/site/docs/00-tutorials/How-to's/03-Colliders Primer/05-uscasee2 tilemap and triggers.mdx index 2ac3d129..20249aea 100644 --- a/site/docs/00-tutorials/How-to's/03-Colliders Primer/05-uscasee2 tilemap and triggers.mdx +++ b/site/docs/00-tutorials/How-to's/03-Colliders Primer/05-uscasee2 tilemap and triggers.mdx @@ -11,7 +11,48 @@ import title from './images/usecasetwotitle.png'; ## Problem Statement -We have a player, and we want it to not pass through the outer tiles of the tilemap. We will use the solid property on the tiles to add a collider. Also, we want the player to step on a trigger to show a prize. +We have a player, and we want it to not pass through the outer tiles of the tilemap. We will use the solid property on the tiles to add a collider. +Also, we want the player to step on a trigger to show a prize. + +:::tip + +`TileMap`s use something called a "composite collider" to represent their collision geometry. A composite collider is a way of treating multiple pieces of geometry as one collider. + +### Composite Collision Resolution Strategy + +1. 'together' - Treat all geometry as part of the same body, meaning only 1 contact is possible for the composite, and only 1 event is generated. +2. 'separate' - Treat geometry as if it were part of separate bodies, a contact is possible for every piece of geometry, an event is generated for every contact. + +Set the default for all composites as engine config + +```typescript +const game = new ex.Engine({ + ... + physics: { + colliders: { + compositeStrategy: 'separate' + } + } +}); + +``` + +Or per composite + +```typescript +const tm = new ex.TileMap({...}); +// TileMaps only have CompositeColliders +const comp = tm.collider.get() as ex.CompositeCollider; +comp.compositeStrategy = 'separate'; + +``` + +### Rule of Thumb + +* If your composite is connected and has no large gaps, use 'together' +* If your composite is a level like a platformer with large gaps, use 'separate' + +::: Demonstrates: diff --git a/site/docs/00-tutorials/How-to's/03-Colliders Primer/code/usecase1.ts b/site/docs/00-tutorials/How-to's/03-Colliders Primer/code/usecase1.ts index 6c5b559f..1c35d846 100644 --- a/site/docs/00-tutorials/How-to's/03-Colliders Primer/code/usecase1.ts +++ b/site/docs/00-tutorials/How-to's/03-Colliders Primer/code/usecase1.ts @@ -4,9 +4,9 @@ import * as ex from 'excalibur'; Player Setup *******************************/ -class MyPlayer extends ex.Actor{ +class MyPlayer extends ex.Actor { speed: number = 100; - constructor(){ + constructor() { super({ x: 300, y: 200, @@ -18,14 +18,14 @@ class MyPlayer extends ex.Actor{ onCollisionStart(self: ex.Collider, other: ex.Collider, side: ex.Side, contact: ex.CollisionContact): void { //match blocks velocity if they collide with player - if(other.owner instanceof MyBlock){ + if (other.owner instanceof MyBlock) { this.speed = other.owner.speed; } } onCollisionEnd(self: ex.Collider, other: ex.Collider, side: ex.Side, contact: ex.CollisionContact): void { //match blocks velocity if they collide with player - if(other.owner instanceof MyBlock){ + if (other.owner instanceof MyBlock) { this.speed = 100 } } @@ -52,11 +52,11 @@ class MyPlayer extends ex.Actor{ /******************************* Block Setup *******************************/ -class MyBlock extends ex.Actor{ +class MyBlock extends ex.Actor { speed: number = 10; isMoving: boolean = false; moveDirection: ex.Vector = ex.Vector.Zero; - constructor(){ + constructor() { super({ x: 200, y: 200, @@ -64,15 +64,15 @@ class MyBlock extends ex.Actor{ height: 32, color: ex.Color.Blue, collisionType: ex.CollisionType.Passive - + }); } // collider start onCollisionStart(self: ex.Collider, other: ex.Collider, side: ex.Side, contact: ex.CollisionContact): void { - if(!this.isMoving && other.owner instanceof MyPlayer){ + if (!this.isMoving && other.owner instanceof MyPlayer) { this.isMoving = true; - switch(side){ + switch (side) { case ex.Side.None: this.moveDirection = ex.Vector.Zero; break; @@ -93,20 +93,20 @@ class MyBlock extends ex.Actor{ break; } } - + } onCollisionEnd(self: ex.Collider, other: ex.Collider, side: ex.Side, contact: ex.CollisionContact): void { - if(this.isMoving && other.owner instanceof MyPlayer){ + if (this.isMoving && other.owner instanceof MyPlayer) { this.isMoving = false; this.moveDirection = ex.Vector.Zero; } } - + onPreUpdate(engine: ex.Engine, elapsed: number): void { - if(this.isMoving){ + if (this.isMoving) { this.vel = this.moveDirection; } else { this.vel = ex.Vector.Zero; @@ -117,20 +117,20 @@ class MyBlock extends ex.Actor{ //******************************* // Room Setup //*******************************/ -class MyRoom extends ex.Actor{ - - TopWall = ex.Shape.Box(420,10, ex.Vector.Zero, ex.vec(-210, -210)); - BottomWall = ex.Shape.Box(420,10, ex.Vector.Zero, ex.vec(-210, 200)); - LeftWall = ex.Shape.Box(10,420, ex.Vector.Zero, ex.vec(-210, -210)); - RightWall = ex.Shape.Box(10,420, ex.Vector.Zero, ex.vec(200, -210)); - - background = new ex.Rectangle({ - width: 400, - height: 400, - color: ex.Color.White - }); - - constructor(){ +class MyRoom extends ex.Actor { + + TopWall = ex.Shape.Box(420, 10, ex.Vector.Zero, ex.vec(-210, -210)); + BottomWall = ex.Shape.Box(420, 10, ex.Vector.Zero, ex.vec(-210, 200)); + LeftWall = ex.Shape.Box(10, 420, ex.Vector.Zero, ex.vec(-210, -210)); + RightWall = ex.Shape.Box(10, 420, ex.Vector.Zero, ex.vec(200, -210)); + + background = new ex.Rectangle({ + width: 400, + height: 400, + color: ex.Color.White + }); + + constructor() { super({ x: 250, y: 250, @@ -151,11 +151,11 @@ class MyRoom extends ex.Actor{ Engine Setup *******************************/ const game = new ex.Engine({ - canvasElementId: 'preview-canvas', - displayMode: ex.DisplayMode.Fixed, - width: 500, - height: 500, - pixelArt: true + canvasElementId: 'preview-canvas', + displayMode: ex.DisplayMode.Fixed, + width: 500, + height: 500, + pixelArt: true }); /****************************** diff --git a/site/docs/00-tutorials/How-to's/03-Colliders Primer/code/usecase2.ts b/site/docs/00-tutorials/How-to's/03-Colliders Primer/code/usecase2.ts index b6acfc1e..eb46abc1 100644 --- a/site/docs/00-tutorials/How-to's/03-Colliders Primer/code/usecase2.ts +++ b/site/docs/00-tutorials/How-to's/03-Colliders Primer/code/usecase2.ts @@ -4,9 +4,9 @@ import * as ex from 'excalibur'; Player Setup *******************************/ -class MyPlayer extends ex.Actor{ +class MyPlayer extends ex.Actor { speed: number = 100; - constructor(){ + constructor() { super({ x: 250, y: 250, @@ -18,7 +18,7 @@ class MyPlayer extends ex.Actor{ } onCollisionStart(self: ex.Collider, other: ex.Collider, side: ex.Side, contact: ex.CollisionContact): void { - if(other.owner instanceof MyPrize){ + if (other.owner instanceof MyPrize) { other.owner.kill(); } } @@ -56,9 +56,9 @@ const myTileMap = new ex.TileMap({ }) const setupTileMap = () => { - let whiteBorder = new ex.Rectangle({width: 30,height: 30,color: ex.Color.Transparent, strokeColor: ex.Color.White, lineWidth: 2}); - let whiteTile = new ex.Rectangle({width: 32, height: 32, color: ex.Color.White}); - let greyTile = new ex.GraphicsGroup({members: [new ex.Rectangle({width: 32, height: 32, color: ex.Color.Gray}), {graphic: whiteBorder, offset: ex.vec(1, 1)}]}); + let whiteBorder = new ex.Rectangle({ width: 30, height: 30, color: ex.Color.Transparent, strokeColor: ex.Color.White, lineWidth: 2 }); + let whiteTile = new ex.Rectangle({ width: 32, height: 32, color: ex.Color.White }); + let greyTile = new ex.GraphicsGroup({ members: [new ex.Rectangle({ width: 32, height: 32, color: ex.Color.Gray }), { graphic: whiteBorder, offset: ex.vec(1, 1) }] }); const isTopEdge = (index: number, width: number) => index < width; const isBottomEdge = (index: number, width: number, totalTiles: number) => index >= totalTiles - width; @@ -75,34 +75,30 @@ const setupTileMap = () => { } let tileIndex = 0; - for(const tile of myTileMap.tiles){ - if(isEdgeTile(tileIndex, myTileMap.columns, myTileMap.tiles.length)){ + for (const tile of myTileMap.tiles) { + if (isEdgeTile(tileIndex, myTileMap.columns, myTileMap.tiles.length)) { tile.solid = true; tile.addGraphic(greyTile); - }else{ + } else { tile.addGraphic(whiteTile); } tileIndex++; } - - let CC:ex.CompositeCollider = myTileMap.getCompositeCollider(); - CC.compositeStrategy = 'separate'; - } setupTileMap(); /****************************** Button Setup *******************************/ -class MyButton extends ex.Actor{ - constructor(){ +class MyButton extends ex.Actor { + constructor() { super({ x: 96, y: 96, radius: 12, color: ex.Color.Black, z: 10, - + }) } } @@ -118,7 +114,7 @@ let trigger = new ex.Trigger({ action: () => { let isPrize = game.currentScene.entities.find(e => e instanceof MyPrize); - if(!isPrize){ + if (!isPrize) { game.currentScene.add(new MyPrize()); } }, @@ -128,8 +124,8 @@ let trigger = new ex.Trigger({ Prize Setup *******************************/ -class MyPrize extends ex.Actor{ - constructor(){ +class MyPrize extends ex.Actor { + constructor() { super({ x: 300, y: 300, @@ -146,11 +142,16 @@ class MyPrize extends ex.Actor{ Engine Setup *******************************/ const game = new ex.Engine({ - canvasElementId: 'preview-canvas', - displayMode: ex.DisplayMode.Fixed, - width: 500, - height: 500, - pixelArt: true + canvasElementId: 'preview-canvas', + displayMode: ex.DisplayMode.Fixed, + width: 500, + height: 500, + pixelArt: true, + physics: { + colliders: { + compositeStrategy: 'separate' + } + } }); /****************************** @@ -162,4 +163,4 @@ game.add(myTileMap); game.add(new MyButton()); game.add(trigger); game.currentScene.camera.strategy.lockToActor(player); -game.start(); \ No newline at end of file +game.start(); diff --git a/site/docs/00-tutorials/How-to's/03-Colliders Primer/code/usecase3.ts b/site/docs/00-tutorials/How-to's/03-Colliders Primer/code/usecase3.ts index 70b87094..fc05920f 100644 --- a/site/docs/00-tutorials/How-to's/03-Colliders Primer/code/usecase3.ts +++ b/site/docs/00-tutorials/How-to's/03-Colliders Primer/code/usecase3.ts @@ -4,8 +4,8 @@ import * as ex from 'excalibur'; /****************************** Custom Attack Action *******************************/ -class AttackAction implements ex.Action{ - angVelocity:number ; +class AttackAction implements ex.Action { + angVelocity: number; id: number = ex.nextActionId(); owner: ex.Actor; parent: ex.Actor; @@ -15,11 +15,11 @@ class AttackAction implements ex.Action{ private elapsedTime = 0; constructor( - actor: ex.Actor, // the sword / child - parent: ex.Actor, - radius: number = 32, - angVelocity: number = Math.PI * 4, // rad/sec - duration: number = 0.75 // seconds + actor: ex.Actor, // the sword / child + parent: ex.Actor, + radius: number = 32, + angVelocity: number = Math.PI * 4, // rad/sec + duration: number = 0.75 // seconds ) { this.owner = actor; this.parent = parent; @@ -36,14 +36,14 @@ class AttackAction implements ex.Action{ Math.cos(this.angle) * this.radius, Math.sin(this.angle) * this.radius ); - this.owner.rotation = this.angle+Math.PI/2; + this.owner.rotation = this.angle + Math.PI / 2; this.owner.pos = offset; } isComplete(entity: ex.Entity): boolean { return this.elapsedTime >= this.duration; } - + reset(): void { this.elapsedTime = 0; this.angle = 0; @@ -52,7 +52,7 @@ class AttackAction implements ex.Action{ stop(): void { this.reset(); } - + } /****************************** @@ -65,8 +65,8 @@ const weaponColliderGroup = new ex.CollisionGroup('weaponCollider', 0b0100, 0b00 /****************************** Weapon Setup *******************************/ -class MyWeapon extends ex.Actor{ - constructor(){ +class MyWeapon extends ex.Actor { + constructor() { super({ x: 0, y: -36, @@ -81,12 +81,12 @@ class MyWeapon extends ex.Actor{ onInitialize(engine: ex.Engine): void { this.actions.runAction(new AttackAction(this, this.parent as MyPlayer)); - this.on('actioncomplete', (a:ex.ActionCompleteEvent) => { - if(a.action instanceof AttackAction) { - setTimeout(() => { - this.kill(); - }, 500) - } + this.on('actioncomplete', (a: ex.ActionCompleteEvent) => { + if (a.action instanceof AttackAction) { + setTimeout(() => { + this.kill(); + }, 500) + } }); } } @@ -94,10 +94,10 @@ class MyWeapon extends ex.Actor{ /****************************** Player Setup *******************************/ -class MyPlayer extends ex.Actor{ - +class MyPlayer extends ex.Actor { + speed: number = 100; - constructor(){ + constructor() { super({ x: 250, y: 250, @@ -112,15 +112,15 @@ class MyPlayer extends ex.Actor{ onInitialize(engine: ex.Engine): void { engine.input.keyboard.on('press', (e) => { if (e.key === ex.Keys.Enter) { - if(this.children.length > 0) return; + if (this.children.length > 0) return; this.addChild(new MyWeapon()); } }); } onCollisionStart(self: ex.Collider, other: ex.Collider, side: ex.Side, contact: ex.CollisionContact): void { - if(other.owner instanceof MyEnemy){ - this.actions.runAction(new ex.Blink(this,150,150,10)); + if (other.owner instanceof MyEnemy) { + this.actions.runAction(new ex.Blink(this, 150, 150, 10)); } } @@ -150,8 +150,8 @@ let player = new MyPlayer(); Enemy Setup *******************************/ -class EnemyDamageBox extends ex.Actor{ - constructor(x: number, y: number){ +class EnemyDamageBox extends ex.Actor { + constructor(x: number, y: number) { super({ x: x, y: y, @@ -163,18 +163,18 @@ class EnemyDamageBox extends ex.Actor{ }); } onCollisionStart(self: ex.Collider, other: ex.Collider, side: ex.Side, contact: ex.CollisionContact): void { - if((this.parent as MyEnemy).isDamaged) return; + if ((this.parent as MyEnemy).isDamaged) return; (this.parent as MyEnemy).isDamaged = true; - let parallelAction = new ex.ParallelActions([new ex.Blink(this.parent,150,150,10), new ex.Flash(this.parent, ex.Color.White, 3000)]); - if(other.owner instanceof MyWeapon){ + let parallelAction = new ex.ParallelActions([new ex.Blink(this.parent, 150, 150, 10), new ex.Flash(this.parent, ex.Color.White, 3000)]); + if (other.owner instanceof MyWeapon) { (this.parent as MyEnemy).actions.runAction(parallelAction); } } } -class MyEnemy extends ex.Actor{ +class MyEnemy extends ex.Actor { isDamaged: boolean = false; - constructor(x: number, y: number){ + constructor(x: number, y: number) { super({ x: x, y: y, @@ -182,14 +182,14 @@ class MyEnemy extends ex.Actor{ color: ex.Color.Red, z: 10, }); - this.addChild(new EnemyDamageBox(24,0)); - this.addChild(new EnemyDamageBox(-24,0)); - this.addChild(new EnemyDamageBox(0,24)); - this.addChild(new EnemyDamageBox(0,-24)); + this.addChild(new EnemyDamageBox(24, 0)); + this.addChild(new EnemyDamageBox(-24, 0)); + this.addChild(new EnemyDamageBox(0, 24)); + this.addChild(new EnemyDamageBox(0, -24)); } onInitialize(engine: ex.Engine): void { - this.on('actioncomplete', (a:ex.ActionCompleteEvent) => { - if(a.action instanceof ex.ParallelActions) { + this.on('actioncomplete', (a: ex.ActionCompleteEvent) => { + if (a.action instanceof ex.ParallelActions) { this.isDamaged = false; } }); @@ -200,11 +200,11 @@ class MyEnemy extends ex.Actor{ Engine Setup *******************************/ const game = new ex.Engine({ - canvasElementId: 'preview-canvas', - displayMode: ex.DisplayMode.Fixed, - width: 500, - height: 500, - pixelArt: true + canvasElementId: 'preview-canvas', + displayMode: ex.DisplayMode.Fixed, + width: 500, + height: 500, + pixelArt: true }); /****************************** @@ -212,5 +212,5 @@ const game = new ex.Engine({ *******************************/ game.add(player); -game.add(new MyEnemy(100,100)); -game.start(); \ No newline at end of file +game.add(new MyEnemy(100, 100)); +game.start(); -- 2.51.2