feat: animation

This commit is contained in:
cha0s 2024-06-12 14:31:12 -05:00
parent c3dc6caac1
commit 41be1f2ed3
11 changed files with 717 additions and 14 deletions

View File

@ -1,3 +1,8 @@
export default {
image: {type: 'string'},
animation: {type: 'string'},
elapsed: {type: 'float32'},
frame: {type: 'uint16'},
frames: {type: 'uint16'},
source: {type: 'string'},
speed: {type: 'float32'},
};

View File

@ -0,0 +1,22 @@
import {System} from '@/ecs/index.js';
export default class ControlMovement extends System {
static queries() {
return {
default: ['Sprite'],
};
}
tick(elapsed) {
for (const [Sprite] of this.select('default')) {
Sprite.elapsed += elapsed / Sprite.speed;
while (Sprite.elapsed > 1) {
Sprite.elapsed -= 1;
Sprite.frame = (Sprite.frame + 1) % Sprite.frames;
}
}
}
}

View File

@ -8,6 +8,7 @@ import ApplyMomentum from '@/ecs-systems/apply-momentum.js';
import CalculateAabbs from '@/ecs-systems/calculate-aabbs.js';
import FollowCamera from '@/ecs-systems/follow-camera.js';
import UpdateSpatialHash from '@/ecs-systems/update-spatial-hash.js';
import RunAnimations from '@/ecs-systems/run-animations.js';
import Ecs from '@/engine/ecs.js';
import {decode, encode} from '@/packets/index.js';
@ -19,7 +20,13 @@ const players = {
Position: {x: 368, y: 368},
VisibleAabb: {},
World: {world: 1},
Sprite: {image: '/assets/bunny.png'},
Sprite: {
animation: 'down',
frame: 0,
frames: 8,
source: '/assets/dude.json',
speed: 0.1,
},
},
};
@ -50,6 +57,7 @@ export default class Engine {
ecs.addSystem(FollowCamera);
ecs.addSystem(CalculateAabbs);
ecs.addSystem(UpdateSpatialHash);
ecs.addSystem(RunAnimations);
this.ecses = {
1: ecs,
};

View File

@ -1,4 +1,6 @@
import {Container, Sprite} from '@pixi/react';
import {Container} from '@pixi/react';
import Sprite from './sprite.jsx';
export default function Entities({entities, x, y}) {
const sprites = [];
@ -9,10 +11,8 @@ export default function Entities({entities, x, y}) {
}
sprites.push(
<Sprite
image={entity.Sprite.image}
entity={entity}
key={id}
x={entity.Position.x}
y={entity.Position.y}
/>
);
}

View File

@ -0,0 +1,34 @@
import {Assets} from '@pixi/assets';
import {Sprite as PixiSprite} from '@pixi/react';
import {useEffect, useState} from 'react';
export default function Sprite({entity}) {
const [asset, setAsset] = useState();
useEffect(() => {
const asset = Assets.get(entity.Sprite.source);
if (asset) {
setAsset(asset);
}
else {
Assets.load(entity.Sprite.source).then(setAsset);
}
}, [setAsset, entity.Sprite.source]);
if (!asset) {
return false;
}
let texture;
if (asset.textures) {
const animation = asset.animations[entity.Sprite.animation]
texture = animation[entity.Sprite.frame];
}
else {
texture = asset;
}
return (
<PixiSprite
texture={texture}
x={entity.Position.x}
y={entity.Position.y}
/>
);
}

View File

@ -1,14 +1,14 @@
import {useEffect, useState} from 'react';
import {Assets} from '@pixi/assets';
import {PixiComponent} from '@pixi/react';
import '@pixi/spritesheet'; // NECESSARY!
import {CompositeTilemap} from '@pixi/tilemap';
import {Assets} from '@pixi/assets';
const TileLayerInternal = PixiComponent('TileLayer', {
create: () => new CompositeTilemap(),
applyProps: (tilemap, {tiles: oldTiles}, props) => {
const {asset, tiles, tileSize, size, x, y} = props;
const {asset, tiles, tileset, tileSize, size, x, y} = props;
const extless = tileset.slice('/assets/'.length, -'.json'.length);
const {textures} = asset;
tilemap.position.x = x;
tilemap.position.y = y;
@ -19,7 +19,7 @@ const TileLayerInternal = PixiComponent('TileLayer', {
let i = 0;
for (let y = 0; y < size.y; ++y) {
for (let x = 0; x < size.x; ++x) {
tilemap.tile(textures[tiles[i++]], tileSize.x * x, tileSize.y * y);
tilemap.tile(textures[`${extless}/${tiles[i++]}`], tileSize.x * x, tileSize.y * y);
}
}
},
@ -44,6 +44,7 @@ export default function TileLayer(props) {
<TileLayerInternal
{...props}
asset={asset}
tileset={tileset}
/>
);
}
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 449 B

631
public/assets/dude.json Normal file
View File

@ -0,0 +1,631 @@
{
"animations": {
"up": [
"dude/0",
"dude/1",
"dude/2",
"dude/3",
"dude/4",
"dude/5",
"dude/6",
"dude/7"
],
"right": [
"dude/8",
"dude/9",
"dude/10",
"dude/11",
"dude/12",
"dude/13",
"dude/14",
"dude/15"
],
"down": [
"dude/16",
"dude/17",
"dude/18",
"dude/19",
"dude/20",
"dude/21",
"dude/22",
"dude/23"
],
"left": [
"dude/24",
"dude/25",
"dude/26",
"dude/27",
"dude/28",
"dude/29",
"dude/30",
"dude/31"
]
},
"frames": {
"dude/0": {
"frame": {
"x": 0,
"y": 0,
"w": 32,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 32
},
"sourceSize": {
"w": 32,
"h": 32
}
},
"dude/1": {
"frame": {
"x": 32,
"y": 0,
"w": 32,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 32
},
"sourceSize": {
"w": 32,
"h": 32
}
},
"dude/2": {
"frame": {
"x": 64,
"y": 0,
"w": 32,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 32
},
"sourceSize": {
"w": 32,
"h": 32
}
},
"dude/3": {
"frame": {
"x": 96,
"y": 0,
"w": 32,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 32
},
"sourceSize": {
"w": 32,
"h": 32
}
},
"dude/4": {
"frame": {
"x": 128,
"y": 0,
"w": 32,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 32
},
"sourceSize": {
"w": 32,
"h": 32
}
},
"dude/5": {
"frame": {
"x": 160,
"y": 0,
"w": 32,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 32
},
"sourceSize": {
"w": 32,
"h": 32
}
},
"dude/6": {
"frame": {
"x": 192,
"y": 0,
"w": 32,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 32
},
"sourceSize": {
"w": 32,
"h": 32
}
},
"dude/7": {
"frame": {
"x": 224,
"y": 0,
"w": 32,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 32
},
"sourceSize": {
"w": 32,
"h": 32
}
},
"dude/8": {
"frame": {
"x": 0,
"y": 32,
"w": 32,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 32
},
"sourceSize": {
"w": 32,
"h": 32
}
},
"dude/9": {
"frame": {
"x": 32,
"y": 32,
"w": 32,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 32
},
"sourceSize": {
"w": 32,
"h": 32
}
},
"dude/10": {
"frame": {
"x": 64,
"y": 32,
"w": 32,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 32
},
"sourceSize": {
"w": 32,
"h": 32
}
},
"dude/11": {
"frame": {
"x": 96,
"y": 32,
"w": 32,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 32
},
"sourceSize": {
"w": 32,
"h": 32
}
},
"dude/12": {
"frame": {
"x": 128,
"y": 32,
"w": 32,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 32
},
"sourceSize": {
"w": 32,
"h": 32
}
},
"dude/13": {
"frame": {
"x": 160,
"y": 32,
"w": 32,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 32
},
"sourceSize": {
"w": 32,
"h": 32
}
},
"dude/14": {
"frame": {
"x": 192,
"y": 32,
"w": 32,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 32
},
"sourceSize": {
"w": 32,
"h": 32
}
},
"dude/15": {
"frame": {
"x": 224,
"y": 32,
"w": 32,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 32
},
"sourceSize": {
"w": 32,
"h": 32
}
},
"dude/16": {
"frame": {
"x": 0,
"y": 64,
"w": 32,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 32
},
"sourceSize": {
"w": 32,
"h": 32
}
},
"dude/17": {
"frame": {
"x": 32,
"y": 64,
"w": 32,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 32
},
"sourceSize": {
"w": 32,
"h": 32
}
},
"dude/18": {
"frame": {
"x": 64,
"y": 64,
"w": 32,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 32
},
"sourceSize": {
"w": 32,
"h": 32
}
},
"dude/19": {
"frame": {
"x": 96,
"y": 64,
"w": 32,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 32
},
"sourceSize": {
"w": 32,
"h": 32
}
},
"dude/20": {
"frame": {
"x": 128,
"y": 64,
"w": 32,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 32
},
"sourceSize": {
"w": 32,
"h": 32
}
},
"dude/21": {
"frame": {
"x": 160,
"y": 64,
"w": 32,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 32
},
"sourceSize": {
"w": 32,
"h": 32
}
},
"dude/22": {
"frame": {
"x": 192,
"y": 64,
"w": 32,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 32
},
"sourceSize": {
"w": 32,
"h": 32
}
},
"dude/23": {
"frame": {
"x": 224,
"y": 64,
"w": 32,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 32
},
"sourceSize": {
"w": 32,
"h": 32
}
},
"dude/24": {
"frame": {
"x": 0,
"y": 96,
"w": 32,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 32
},
"sourceSize": {
"w": 32,
"h": 32
}
},
"dude/25": {
"frame": {
"x": 32,
"y": 96,
"w": 32,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 32
},
"sourceSize": {
"w": 32,
"h": 32
}
},
"dude/26": {
"frame": {
"x": 64,
"y": 96,
"w": 32,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 32
},
"sourceSize": {
"w": 32,
"h": 32
}
},
"dude/27": {
"frame": {
"x": 96,
"y": 96,
"w": 32,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 32
},
"sourceSize": {
"w": 32,
"h": 32
}
},
"dude/28": {
"frame": {
"x": 128,
"y": 96,
"w": 32,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 32
},
"sourceSize": {
"w": 32,
"h": 32
}
},
"dude/29": {
"frame": {
"x": 160,
"y": 96,
"w": 32,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 32
},
"sourceSize": {
"w": 32,
"h": 32
}
},
"dude/30": {
"frame": {
"x": 192,
"y": 96,
"w": 32,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 32
},
"sourceSize": {
"w": 32,
"h": 32
}
},
"dude/31": {
"frame": {
"x": 224,
"y": 96,
"w": 32,
"h": 32
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 32
},
"sourceSize": {
"w": 32,
"h": 32
}
}
},
"meta": {
"format": "RGBA8888",
"image": "dude.png",
"scale": 1,
"size": {
"w": 256,
"h": 128
}
}
}

BIN
public/assets/dude.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@ -21,10 +21,12 @@ const json = {
},
};
const extlessPath = join(dirname(tileset), basename(tileset, extname(tileset)));
let i = 0;
for (let y = 0; y < height; y += h) {
for (let x = 0; x < width; x += w) {
json.frames[i++] = {
json.frames[join(extlessPath, `${i++}`)] = {
frame: {x, y, w, h},
spriteSourceSize: {x: 0, y: 0, w, h},
sourceSize: {w, h},
@ -33,6 +35,6 @@ for (let y = 0; y < height; y += h) {
}
writeFileSync(
`${join(dirname(tileset), basename(tileset, extname(tileset)))}.json`,
`${extlessPath}.json`,
JSON.stringify(json),
);

File diff suppressed because one or more lines are too long