-
-
Notifications
You must be signed in to change notification settings - Fork 191
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Tiling Sprites and Animations (#3309)
https://github.com/user-attachments/assets/1c957f33-2088-4a77-981d-78e1db2e3d83 Added convenience types `ex.TiledSprite` and `ex.TiledAnimation` for Tiling Sprites and Animations ```typescript const tiledGroundSprite = new ex.TiledSprite({ image: groundImage, width: game.screen.width, height: 200, wrapping: { x: ex.ImageWrapping.Repeat, y: ex.ImageWrapping.Clamp } }); const tilingAnimation = new ex.TiledAnimation({ animation: cardAnimation, sourceView: {x: 20, y: 20}, width: 200, height: 200, wrapping: ex.ImageWrapping.Repeat }); ```
- Loading branch information
Showing
22 changed files
with
665 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<!doctype html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
<meta http-equiv="X-UA-Compatible" content="ie=edge" /> | ||
<title>Tiling</title> | ||
</head> | ||
<body> | ||
<script src="../../lib/excalibur.js"></script> | ||
<script src="index.js"></script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
var game = new ex.Engine({ | ||
width: 800, | ||
height: 800, | ||
displayMode: ex.DisplayMode.FitScreenAndFill, | ||
pixelArt: true, | ||
pixelRatio: 2 | ||
}); | ||
|
||
var cards = new ex.ImageSource('./kenny-cards.png'); | ||
var cardSpriteSheet = ex.SpriteSheet.fromImageSource({ | ||
image: cards, | ||
grid: { | ||
rows: 4, | ||
columns: 14, | ||
spriteWidth: 42, | ||
spriteHeight: 60 | ||
}, | ||
spacing: { | ||
originOffset: { x: 11, y: 2 }, | ||
margin: { x: 23, y: 5 } | ||
} | ||
}); | ||
|
||
cardSpriteSheet.sprites.forEach((s) => (s.scale = ex.vec(2, 2))); | ||
var cardAnimation = ex.Animation.fromSpriteSheet(cardSpriteSheet, ex.range(0, 14 * 4), 200); | ||
|
||
var groundImage = new ex.ImageSource('./ground.png'); | ||
var desertImage = new ex.ImageSource('./desert.png'); | ||
var loader = new ex.Loader([cards, groundImage, desertImage]); | ||
var groundSprite = groundImage.toSprite(); | ||
|
||
// var tiledGroundSprite = new ex.TiledSprite({ | ||
// image: groundImage, | ||
// width: game.screen.width, | ||
// height: 200, | ||
// wrapping: { | ||
// x: ex.ImageWrapping.Repeat, | ||
// y: ex.ImageWrapping.Clamp | ||
// } | ||
// }); | ||
var tiledGroundSprite = ex.TiledSprite.fromSprite(groundSprite, { | ||
width: game.screen.width, | ||
height: 200, | ||
wrapping: { | ||
x: ex.ImageWrapping.Repeat, | ||
y: ex.ImageWrapping.Clamp | ||
} | ||
}); | ||
|
||
var tilingAnimation = new ex.TiledAnimation({ | ||
animation: cardAnimation, | ||
sourceView: { x: 20, y: 20 }, | ||
width: 200, | ||
height: 200, | ||
wrapping: ex.ImageWrapping.Repeat | ||
}); | ||
|
||
// tilingAnimation.sourceView = {x: 0, y: 0}; | ||
|
||
game.start(loader).then(() => { | ||
var cardActor = new ex.Actor({ | ||
pos: ex.vec(400, 400) | ||
}); | ||
cardActor.graphics.use(tilingAnimation); | ||
game.add(cardActor); | ||
|
||
var actor = new ex.Actor({ | ||
pos: ex.vec(game.screen.unsafeArea.left, 700), | ||
anchor: ex.vec(0, 0) | ||
}); | ||
actor.graphics.use(tiledGroundSprite); | ||
game.add(actor); | ||
|
||
game.input.pointers.primary.on('wheel', (ev) => { | ||
game.currentScene.camera.zoom += ev.deltaY / 1000; | ||
game.currentScene.camera.zoom = ex.clamp(game.currentScene.camera.zoom, 0.05, 100); | ||
tiledGroundSprite.width = game.screen.width; | ||
// game.screen.center // TODO this doesn't seem right when the screen is narrow in Fit&Fill | ||
actor.pos.x = game.screen.unsafeArea.left; // TODO unsafe area doesn't update on camera zoom | ||
}); | ||
}); |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
import { ImageFiltering } from './Filtering'; | ||
import { ImageWrapConfiguration } from './ImageSource'; | ||
import { SourceView, Sprite } from './Sprite'; | ||
import { ImageWrapping } from './Wrapping'; | ||
import { Animation, AnimationOptions } from './Animation'; | ||
import { GraphicOptions } from './Graphic'; | ||
import { TiledSprite } from './TiledSprite'; | ||
import { watch } from '../Util/Watch'; | ||
import { Future } from '../Util/Future'; | ||
|
||
export interface TiledAnimationOptions { | ||
/** | ||
* Animation to tile | ||
*/ | ||
animation: Animation; | ||
/** | ||
* Optionally override source view on frame graphics | ||
*/ | ||
sourceView?: Partial<SourceView>; | ||
/** | ||
* Optionally override filtering options | ||
*/ | ||
filtering?: ImageFiltering; | ||
/** | ||
* Default wrapping is Repeat for TiledAnimation | ||
*/ | ||
wrapping?: ImageWrapConfiguration | ImageWrapping; | ||
/** | ||
* Total width in pixels for the tiling to take place | ||
*/ | ||
width: number; | ||
/** | ||
* Total height in pixels for the tiling to take place | ||
*/ | ||
height: number; | ||
} | ||
|
||
export class TiledAnimation extends Animation { | ||
private _ready = new Future<void>(); | ||
public ready = this._ready.promise; | ||
private _tiledWidth: number = 0; | ||
private _tiledHeight: number = 0; | ||
private _sourceView: Partial<SourceView> = {}; | ||
constructor(options: GraphicOptions & Omit<AnimationOptions, 'frames'> & TiledAnimationOptions) { | ||
super({ | ||
...options, | ||
frames: options.animation.frames.slice(), | ||
strategy: options.animation.strategy, | ||
frameDuration: options.animation.frameDuration, | ||
speed: options.animation.speed, | ||
reverse: options.animation.isReversed | ||
}); | ||
this._sourceView = { ...options.sourceView }; | ||
this._tiledWidth = options.width; | ||
this._tiledHeight = options.height; | ||
|
||
const promises: Promise<void>[] = []; | ||
for (let i = 0; i < this.frames.length; i++) { | ||
const graphic = this.frames[i].graphic; | ||
if (graphic && graphic instanceof Sprite) { | ||
const tiledSprite = new TiledSprite({ | ||
image: graphic.image, | ||
width: options.width, | ||
height: options.height, | ||
sourceView: { ...graphic.sourceView }, | ||
wrapping: options.wrapping, | ||
filtering: options.filtering | ||
}); | ||
this.frames[i].graphic = tiledSprite; | ||
|
||
// There is a new calc'd sourceView when ready | ||
tiledSprite.ready.then(() => { | ||
tiledSprite.sourceView = { ...tiledSprite.sourceView, ...this._sourceView }; | ||
}); | ||
promises.push(tiledSprite.ready); | ||
} | ||
} | ||
Promise.allSettled(promises).then(() => this._ready.resolve()); | ||
} | ||
|
||
public static fromAnimation(animation: Animation, options?: Omit<TiledAnimationOptions, 'animation'>): TiledAnimation { | ||
return new TiledAnimation({ | ||
width: animation.width, | ||
height: animation.height, | ||
...options, | ||
animation | ||
}); | ||
} | ||
|
||
private _updateSourceView() { | ||
for (let i = 0; i < this.frames.length; i++) { | ||
const graphic = this.frames[i].graphic; | ||
if (graphic && graphic instanceof Sprite) { | ||
graphic.sourceView = { ...graphic.sourceView, ...this._sourceView }; | ||
} | ||
} | ||
} | ||
|
||
get sourceView(): Partial<SourceView> { | ||
return watch(this._sourceView, () => this._updateSourceView()); | ||
} | ||
|
||
set sourceView(sourceView: Partial<SourceView>) { | ||
this._sourceView = watch(sourceView, () => this._updateSourceView()); | ||
this._updateSourceView(); | ||
} | ||
|
||
private _updateWidthHeight() { | ||
for (let i = 0; i < this.frames.length; i++) { | ||
const graphic = this.frames[i].graphic; | ||
if (graphic && graphic instanceof Sprite) { | ||
graphic.sourceView.height = this._tiledHeight || graphic.height; | ||
graphic.destSize.height = this._tiledHeight || graphic.height; | ||
graphic.sourceView.width = this._tiledWidth || graphic.width; | ||
graphic.destSize.width = this._tiledWidth || graphic.width; | ||
} | ||
} | ||
} | ||
|
||
get width() { | ||
return this._tiledWidth; | ||
} | ||
|
||
get height() { | ||
return this._tiledHeight; | ||
} | ||
|
||
override set width(width: number) { | ||
this._tiledWidth = width; | ||
this._updateWidthHeight(); | ||
} | ||
|
||
override set height(height: number) { | ||
this._tiledHeight = height; | ||
this._updateWidthHeight(); | ||
} | ||
} |
Oops, something went wrong.