From b83b596b0f79fa1d5b95c462d3fa7171ff221a19 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sun, 11 Nov 2018 00:56:22 +0100 Subject: [PATCH] Add support for simple periodic sprite animations --- dist/resources/entity/square.json | 23 +++++++++++++++++ dist/resources/sprite/entity/square.png | Bin 808 -> 528 bytes src/controller/entitycontext.ts | 33 ++++++++++++++++++++---- src/controller/gamecontext.ts | 7 +++-- src/model/data/entity.ts | 10 +++++++ src/view/entity.ts | 33 ++++++++++++++---------- src/view/util/image.ts | 6 +++-- 7 files changed, 90 insertions(+), 22 deletions(-) create mode 100644 dist/resources/entity/square.json diff --git a/dist/resources/entity/square.json b/dist/resources/entity/square.json new file mode 100644 index 0000000..643cfb6 --- /dev/null +++ b/dist/resources/entity/square.json @@ -0,0 +1,23 @@ +{ + "sprite": "square", + "collision": [ + { + "type": "polygon", + "vertices": [ + [-0.46875, -0.46875], + [-0.46875, 0.46875], + [0.46875, 0.46875], + [0.46875, -0.46875] + ] + } + ], + "frames": 4, + "animation": { + "sequence": [ + [500, 0], + [500, 1], + [500, 2], + [500, 3] + ] + } +} diff --git a/dist/resources/sprite/entity/square.png b/dist/resources/sprite/entity/square.png index cdc6981bc225c3926688e64d1c612284f47b4e72..9fcb9f9d13e9ec3f390fd4aee86a9e397c080d26 100644 GIT binary patch delta 517 zcmV+g0{Z=^29N|HiBL{Q4GJ0x0000DNk~Le0000W0001h2m}BC0Kjjfk&z)6e*wZt zL_t(&-tC!PY63A3fIoK=WrgiyL0f21+ADaQ94MYGYqh>4}(X6zqj z=5g8il4UcQF9gnv7O*=f-vB%^qQadVD`&}sDKnXL;!=OLqRtIP{futC&#g1&TB$zQ$INJ zgHj|}hx>&qE=8Jm0Q9&KTZm?4>wN|S)pY^TV<3?2MLdI{K=&inz2;VM;CKyya{nkT z%2177w1#{D$d=+xH&q2F;2#34C^hnfLIjBXpxOPxU-g4`k35qK*93#_00000NkvXX Hu0mjf0g2ZU delta 799 zcmV+)1K|9S1gHifiBL{Q4GJ0x0000DNk~Le0001h0000W2nGNE0NNsOC6OT)e*)S` zL_t(|+U=X&isCRBhF?vSl0rQc`~jun+=6#A7dLm~H4K7aZJ~!PG&E`J9_+M?J9XD( zO}C8SbCN=YHzp6CQ4@f8YDORJh4P;>-)Ww<+bw+GM_t#b>)J}%cs#~U(>8!)~{OQVPbHe|uM>5g230f3gf=7$S-y#BtoV=kwWW{w+A?kWym5-{bZ5^{Q{X z-D0s=U^bg!KA*Ska=C=>`>3i4m&@hh`KnM}YrM^#lw)AY;hY~|mJ@B5fer&z642!a4X5MZ;}V7*>jf6Z+Ln9XKb ztyVC`psFe>VT>_w&cPV_(x;Sqc%If8d7eWlg`y~6jJYYejr?15Wz}_!vMgbYK~WS? zN+Hj4XsscHu$JEnFrUv61ObYouquG-x}7D~M%mR`w~Jm?6{@Pb`4MNU?03Gy#0yzgi^QJ6>WKjGG5r8^134%p&d-;9q}a_#67n zBJLD`AOQ#xfFJ<~5`Z892oiuG0SFR+AOW6&(P;E7vq*qD={lwKe;u;^ghNaJlPt>s zpqIbHn=BGwK!jn4Ii;%taL#)<7T;9?nsGJbbQ}kqb1SE_KF@V4fRwT~ z0lKnmsEmMeh&b> zU5)ycN9O a + s[0], 0); + else + this.totalTime = 0; + } - public render() { + public render(time: number) { this.renderer.setTranslation(this.pos); - this.view.render(); + this.getSprite(time).render(); } public getTranslation(): vec2 { @@ -48,4 +57,18 @@ export class EntityContext implements CollidableGroup { public interact() { alert(`You've interacted with ${this.name}!`); } + + private getSprite(time: number): SpriteView { + time %= this.totalTime; + + if (this.animation) { + for (const [len, sprite] of this.animation.sequence) { + time -= len; + if (time < 0) + return this.sprites[sprite]; + } + } + + return this.sprites[0]; + } } diff --git a/src/controller/gamecontext.ts b/src/controller/gamecontext.ts index 9c25c6b..771affa 100644 --- a/src/controller/gamecontext.ts +++ b/src/controller/gamecontext.ts @@ -19,19 +19,22 @@ export class GameContext implements CollidableGroup { const map = this.loadMap(renderer, 'test'); const loadPlayer = EntityContext.load(renderer, 'green_circle'); const loadEntity = EntityContext.load(renderer, 'red_circle'); + const loadEntity2 = EntityContext.load(renderer, 'square'); const [mapView, mapCollision] = await map; const player = await loadPlayer; const entity = await loadEntity; + const entity2 = await loadEntity2; vec2.set(player.pos, 6, 6); vec2.set(entity.pos, 3, 3); + vec2.set(entity2.pos, 3, 8); return new GameContext( renderer, mapView, player, - [entity], + [entity, entity2], mapCollision, ); } @@ -168,7 +171,7 @@ export class GameContext implements CollidableGroup { this.mapView.render(); for (const r of [...this.entities, this.player]) - r.render(); + r.render(time); window.requestAnimationFrame(this.render); } diff --git a/src/model/data/entity.ts b/src/model/data/entity.ts index 3474a38..f52c130 100644 --- a/src/model/data/entity.ts +++ b/src/model/data/entity.ts @@ -1,19 +1,29 @@ import { Collision } from './collision'; +export interface EntityAnimation { + readonly sequence: ReadonlyArray<[number, number]>; +} + export interface EntityDataInput { readonly sprite: string; readonly anchor?: [number, number]; readonly collision?: Collision[]; + readonly frames?: number; + readonly animation?: EntityAnimation; } export class EntityData { public readonly sprite: string; public readonly anchor: [number, number]; public readonly collision: Collision[]; + public readonly frames: number; + public readonly animation?: EntityAnimation; constructor(input: EntityDataInput) { this.sprite = input.sprite; this.anchor = input.anchor || [0.5, 0.5]; this.collision = input.collision || []; + this.frames = input.frames || 1; + this.animation = input.animation; } } diff --git a/src/view/entity.ts b/src/view/entity.ts index b59bf92..7600826 100644 --- a/src/view/entity.ts +++ b/src/view/entity.ts @@ -8,21 +8,28 @@ import { vec2 } from 'gl-matrix'; export async function loadEntity( r: Renderer, data: EntityData, -): Promise { +): Promise { const tile = await loadImage(`resources/sprite/entity/${data.sprite}.png`); - const [texture, size, coords] = mkTexture(r, tile); - const offset = vec2.mul(vec2.create(), data.anchor, size); - r.snapToGrid(offset, offset); + const sprites: SpriteView[] = []; - const anchorCoords: SpriteCoords = [ - coords[0] - offset[0], - coords[1] - offset[1], - coords[2] - offset[0], - coords[3] - offset[1], - ]; + for (let frame = 0; frame < data.frames; frame++) { + const [texture, size, coords] = mkTexture(r, tile, frame, data.frames); - const builder = new SpriteViewBuilder(r, texture); - builder.addSprite(anchorCoords, [0, 0, 1, 1]); - return builder.build(); + const offset = vec2.mul(vec2.create(), data.anchor, size); + r.snapToGrid(offset, offset); + + const anchorCoords: SpriteCoords = [ + coords[0] - offset[0], + coords[1] - offset[1], + coords[2] - offset[0], + coords[3] - offset[1], + ]; + + const builder = new SpriteViewBuilder(r, texture); + builder.addSprite(anchorCoords, [0, 0, 1, 1]); + sprites.push(builder.build()); + } + + return sprites; } diff --git a/src/view/util/image.ts b/src/view/util/image.ts index e01246c..7baeccc 100644 --- a/src/view/util/image.ts +++ b/src/view/util/image.ts @@ -14,13 +14,15 @@ export function loadImage(url: string): Promise { export function mkTexture( r: Renderer, src: HTMLCanvasElement|HTMLImageElement, + frame: number = 0, + total: number = 1, ): [WebGLTexture, [number, number], SpriteCoords] { const gl = r.getContext(); const texture = gl.createTexture(); if (!texture) throw new Error('unable to create texture'); - const w = src.width, h = src.height; + const w = src.width, h = src.height / total; const w2 = nextPowerOf2(w), h2 = nextPowerOf2(h); const canvas = document.createElement('canvas'); @@ -28,7 +30,7 @@ export function mkTexture( canvas.height = h2; const ctx = canvas.getContext('2d') as CanvasRenderingContext2D; - ctx.drawImage(src, 0, 0); + ctx.drawImage(src, 0, frame * h, w, h, 0, 0, w, h); gl.bindTexture(gl.TEXTURE_2D, texture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas);