diff --git a/CHANGELOG.md b/CHANGELOG.md index 854cf62e..f047f646 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,12 +10,19 @@ This project adheres to [Semantic Versioning](http://semver.org/). - The `ex.Physics.useRealisticPhysics()` physics solver has been updated to fix a bug in bounciness to be more physically accurate, this does change how physics behaves. Setting `ex.Body.bounciness = 0` will simulate the old behavior. - `ex.TransformComponent.posChanged$` has been removed, it incurs a steep performance cost - `ex.EventDispatcher` meta events 'subscribe' and 'unsubscribe' were unused and undocumented and have been removed +- `ex.TileMap` tlies are now drawn from the lower left by default to match with `ex.IsometricMap` and Tiled, but can be configured with `renderFromTopOfGraphic` to restore the previous behavior. ### Deprecated - ### Added +- Added new configurable `ex.TileMap` option for rendering from the bottom or the top of the graphic, this matches with `ex.IsometricMap` and how Tiled renders `renderFromTopOfGraphic`, by default `false` and renders from the bottom. + ```typescript + const tileMap = new ex.TileMap({ + renderFromTopOfGraphic: false + }) + ``` - Added new `ex.Future` type which is a convenient way of wrapping a native browser promise and resolving/rejecting later ```typescript const future = new ex.Future(); @@ -137,7 +144,6 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Most places where `ex.Matrix` was used have been switched to `ex.AffineMatrix` - Most places where `ex.TransformComponent` was used have been switched to `ex.Transform` - ## [0.26.0] - 2022-05-20 ## Breaking Changes diff --git a/src/engine/TileMap/TileMap.ts b/src/engine/TileMap/TileMap.ts index b64a5326..63cbeb57 100644 --- a/src/engine/TileMap/TileMap.ts +++ b/src/engine/TileMap/TileMap.ts @@ -42,6 +42,11 @@ export interface TileMapOptions { * The number of tile rows, or the number of tiles high */ rows: number; + + /** + * Optionally render from the top of the graphic, by default tiles are rendered from the bottom + */ + renderFromTopOfGraphic?: boolean; } /** @@ -66,6 +71,8 @@ export class TileMap extends Entity { public readonly rows: number; public readonly columns: number; + public renderFromTopOfGraphic = false; + private _collidersDirty = true; public flagCollidersDirty() { this._collidersDirty = true; @@ -181,6 +188,7 @@ export class TileMap extends Entity { this._transform.pos = options.pos ?? Vector.Zero; this._oldPos = this._transform.pos; + this.renderFromTopOfGraphic = options.renderFromTopOfGraphic ?? this.renderFromTopOfGraphic; this.tileWidth = options.tileWidth; this.tileHeight = options.tileHeight; this.rows = options.rows; @@ -400,7 +408,8 @@ export class TileMap extends Entity { if (hasGraphicsTick(graphic)) { graphic?.tick(delta, this._token); } - graphic.draw(ctx, x * this.tileWidth, y * this.tileHeight); + const offsetY = this.renderFromTopOfGraphic ? 0 : (graphic.height - this.tileHeight); + graphic.draw(ctx, x * this.tileWidth, y * this.tileHeight - offsetY); } } } diff --git a/src/spec/TileMapSpec.ts b/src/spec/TileMapSpec.ts index 67ee0521..011188dd 100644 --- a/src/spec/TileMapSpec.ts +++ b/src/spec/TileMapSpec.ts @@ -196,6 +196,59 @@ describe('A TileMap', () => { await expectAsync(TestUtils.flushWebGLCanvasTo2D(engine.canvas)).toEqualImage('src/spec/images/TileMapSpec/TileMap.png'); }); + it('should draw from the bottom', async () => { + const tm = new ex.TileMap({ + pos: ex.vec(30, 30), + tileWidth: 64, + tileHeight: 48, + rows: 3, + columns: 7 + }); + const tileGraphic = new ex.Rectangle({ + color: ex.Color.Red, + strokeColor: ex.Color.Black, + width: 64, + height: 64 + }); + tm.tiles.forEach(function (cell: ex.Tile) { + cell.solid = true; + cell.addGraphic(tileGraphic); + }); + tm._initialize(engine); + + drawWithTransform(engine.graphicsContext, tm, 100); + engine.graphicsContext.flush(); + + await expectAsync(TestUtils.flushWebGLCanvasTo2D(engine.canvas)).toEqualImage('src/spec/images/TileMapSpec/tilemap-from-bottom.png'); + }); + + it('should draw from the top', async () => { + const tm = new ex.TileMap({ + pos: ex.vec(30, 30), + tileWidth: 64, + tileHeight: 48, + rows: 3, + columns: 7, + renderFromTopOfGraphic: true + }); + const tileGraphic = new ex.Rectangle({ + color: ex.Color.Red, + strokeColor: ex.Color.Black, + width: 64, + height: 64 + }); + tm.tiles.forEach(function (cell: ex.Tile) { + cell.solid = true; + cell.addGraphic(tileGraphic); + }); + tm._initialize(engine); + + drawWithTransform(engine.graphicsContext, tm, 100); + engine.graphicsContext.flush(); + + await expectAsync(TestUtils.flushWebGLCanvasTo2D(engine.canvas)).toEqualImage('src/spec/images/TileMapSpec/tilemap-from-top.png'); + }); + it('should handle offscreen culling correctly with negative coords', async () => { await texture.load(); const tm = new ex.TileMap({ diff --git a/src/spec/images/TileMapSpec/tilemap-from-bottom.png b/src/spec/images/TileMapSpec/tilemap-from-bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..39924053bccd83c98ae5e2c22bbb0a13fe7e032e GIT binary patch literal 5126 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYU^v0S#=yWJ(BCS>z`(#*9OUlAuGN?o_HZX925XD^BSW9L(yNxXP=pGq&6^m zd^nuFgo(>Xlwm@GECZ8*+yl7>)8!vr-~XRM=+6hKl?_b6pt6y@f#Jkg_8I5-6Q(n= zDIDZqKaoqr092rsGH^6hG*mal*EPid|Hq7O+;#Z`JNt%MHVJeWJYsyuSof2$?(c7Y zbmL;`8-D&_D3eS;cR>gH4)#$GjmFSul7r;p;gg3Nc@*OB-H(f39%NHK z?|kMx`OW?B_xJz5{r!G_|M{ibCnH4y92$>vr!f5d_V3%SKlKOx$S3?~zw!P5$KQEL z3-&ks;$E@8!RJqK2?whP2RDOz`(#*9OUlAuVG{q*zs|JAvF{QLdA{_nrv zA3s)py1$QqZ37e6pZyMO6Y}5Zum3N1_wfIJj2-9eKXWJix0ipg@BhC4;@p3}zW;B| zuyaS%{76RDf1wNxP3xQ4SFo>Ow`gF9DEP_O!!n^!)uM@s>j^7^N(5sA0|yAPOn-mC zgF!N)Nm*loLmY!clPE--!DCd*Xo!s_n9=+~-Qq%l`NZ_^+pk~$^VppI$G=}c8NUCV z{qJkY@9%%VzyJU3@At=#H}7jZ6YjvsdWlKWVc*{Ud;iEEDEWS~pq}xQxB`e+aQgfI ze1=JX|4FQAV3KBKP`S~}z5@(@JZ1m!^}T%qi~Sxqtpx`{7#TRPF)A<={bhXinfXR) z1Ea@>!`VxixO_wzCM3u*Fe%7Akb5v){=xPA{~3h-e2`k%z!VHB8`&EePJCsbah^Y6 zIwPCHLH_jZ-`}+KzG3-#&?W$KN;)( z{^mzFE~dWW=O2bL$pmy4bg=JWAN9~^42>o^NG={ed8m;`A^zU|xcKEkHsv$kubS_W*~+Jq!&j+6-WE#t9ET$x3i4FnbrUu!@}EX7Jd+1X9l^ zz%Z(1G{i;|%xHcYEiQ&$X_>nq`-XQ#Pf0<_+S+yt4PHLh6Wpn+Zsq>>%p>MG!_I%aAAX7=hQDZU*biaNpUz`(%Z>FVdQ&MBb@ E0A55CaR2}S literal 0 HcmV?d00001