diff --git a/dev/main.tsx b/dev/main.tsx
index dfe9569..cd918f3 100644
--- a/dev/main.tsx
+++ b/dev/main.tsx
@@ -16,6 +16,7 @@ import { FileIcon } from "#/dev/icons/File.tsx";
import { LetterXIcon } from "#/dev/icons/LetterX.tsx";
import { LetterYIcon } from "#/dev/icons/LetterY.tsx";
import { ZoomScanIcon } from "#/dev/icons/ZoomScan.tsx";
+import { type Animation, spriteMap } from "#/engine/Animation.ts";
import type { Entity } from "#/engine/Entity.ts";
import {
currentScene,
@@ -106,6 +107,7 @@ const DevTools: Component<{ gameContainer: HTMLElement }> = ({
instances[entityRefs.findIndex((ref) => ref.item.isActive)];
setActiveEntity(instance);
+ const sprite = spriteMap.get(instance.animation.spriteSrc)!;
const processProxy = sceneProcessMap.get(currentScene.data.name)!;
const ctx = viewport.ctx;
@@ -114,12 +116,20 @@ const DevTools: Component<{ gameContainer: HTMLElement }> = ({
ctx.save();
ctx.lineWidth = 1;
- ctx.strokeStyle = "#ee459e";
+ ctx.strokeStyle = "#6572f5";
viewport.ctx.strokeRect(
- instance.position.x + 0.5,
- instance.position.y + 0.5,
- 16 - 1,
- 16 - 1,
+ instance.position.x - 0.5,
+ instance.position.y - 0.5,
+ sprite.image.width / sprite.xFrames + 1,
+ sprite.image.height / sprite.yFrames + 1,
+ );
+
+ ctx.fillStyle = "#ee459e";
+ viewport.ctx.fillRect(
+ instance.position.x - 1,
+ instance.position.y - 1,
+ 2,
+ 2,
);
ctx.restore();
};
@@ -182,7 +192,7 @@ const DevTools: Component<{ gameContainer: HTMLElement }> = ({
-
+
diff --git a/lib/Animation.ts b/engine/Animation.ts
similarity index 64%
rename from lib/Animation.ts
rename to engine/Animation.ts
index 39d35d4..a614192 100644
--- a/lib/Animation.ts
+++ b/engine/Animation.ts
@@ -1,14 +1,25 @@
import { drawSprite, type Sprite } from "#/lib/Sprite.ts";
+export const spriteMap = new Map();
+
export type Animation = {
+ spriteSrc: string;
xIndex: number;
yIndex: number;
frameTime: number;
frameDelta: number;
};
-export function createAnimation(yIndex: number, frameTime: number): Animation {
+export function createAnimation(
+ sprite: Sprite,
+ frameTime: number,
+ yIndex: number,
+): Animation {
+ const spriteSrc = sprite.image.src;
+ spriteMap.set(spriteSrc, sprite);
+
return {
+ spriteSrc,
xIndex: 0,
yIndex,
frameTime,
@@ -16,19 +27,21 @@ export function createAnimation(yIndex: number, frameTime: number): Animation {
};
}
-export function animateSprite(
+export function playAnimation(
ctx: CanvasRenderingContext2D,
- sprite: Sprite,
animation: Animation,
x: number,
y: number,
delta: number,
): void {
+ const sprite = spriteMap.get(animation.spriteSrc)!;
+
if ((animation.frameDelta += delta) > animation.frameTime) {
animation.frameDelta = 0;
if ((animation.xIndex += 1) >= sprite.xFrames) {
animation.xIndex = 0;
}
}
+
drawSprite(ctx, sprite, animation.xIndex, animation.yIndex, x, y);
}
diff --git a/engine/Scene.ts b/engine/Scene.ts
index 9a43f62..f2cec9f 100644
--- a/engine/Scene.ts
+++ b/engine/Scene.ts
@@ -66,12 +66,16 @@ export const [loadScene, sceneCache] = useWithAsyncCache(
});
}
- const instance: Entity = structuredClone({
- ...entityMap.get(entity.name)!,
- ...entity,
- });
-
- entityInstanceMap.get(entity.name)!.instances.push(instance);
+ try {
+ entityInstanceMap.get(entity.name)!.instances.push(
+ structuredClone({
+ ...entityMap.get(entity.name)!,
+ ...entity,
+ }),
+ );
+ } catch (error) {
+ console.error(entity.name, error);
+ }
}),
);
},
diff --git a/entities/barEntity/entity.ts b/entities/barEntity/entity.ts
index ce4cfb2..0fdede8 100644
--- a/entities/barEntity/entity.ts
+++ b/entities/barEntity/entity.ts
@@ -1,18 +1,17 @@
+import { createAnimation, playAnimation } from "#/engine/Animation.ts";
import { useEntity } from "#/engine/Entity.ts";
-import { animateSprite, createAnimation } from "#/lib/Animation.ts";
import { fromSrc, type Sprite } from "#/lib/Sprite.ts";
const idle: Sprite = await fromSrc("/assets/hero/idle.png", 5, 4);
const { animate, process } = useEntity(import.meta, {
- animation: createAnimation(0, 200),
+ animation: createAnimation(idle, 200, 0),
stuff: 1,
});
animate((entity, ctx, delta) => {
- animateSprite(
+ playAnimation(
ctx,
- idle,
entity.animation,
entity.position.x,
entity.position.y,
diff --git a/entities/fooEntity/entity.ts b/entities/fooEntity/entity.ts
index 269aa27..2c11f9b 100644
--- a/entities/fooEntity/entity.ts
+++ b/entities/fooEntity/entity.ts
@@ -1,18 +1,17 @@
import charIdle from "#/assets/char-idle.png";
+import { createAnimation, playAnimation } from "#/engine/Animation.ts";
import { useEntity } from "#/engine/Entity.ts";
-import { animateSprite, createAnimation } from "#/lib/Animation.ts";
import { fromSrc, type Sprite } from "#/lib/Sprite.ts";
const idle: Sprite = await fromSrc(charIdle, 2, 4);
const { animate, process } = useEntity(import.meta, {
- animation: createAnimation(2, 500),
+ animation: createAnimation(idle, 500, 2),
});
animate((entity, ctx, delta) => {
- animateSprite(
+ playAnimation(
ctx,
- idle,
entity.animation,
entity.position.x,
entity.position.y,
diff --git a/entities/portal/entity.ts b/entities/portal/entity.ts
new file mode 100644
index 0000000..f862c3b
--- /dev/null
+++ b/entities/portal/entity.ts
@@ -0,0 +1,19 @@
+import { createAnimation, playAnimation } from "#/engine/Animation.ts";
+import { useEntity } from "#/engine/Entity.ts";
+import { fromSrc, type Sprite } from "#/lib/Sprite.ts";
+
+const sprite: Sprite = await fromSrc("./assets/portal.png", 3, 1);
+
+const { animate } = useEntity(import.meta, {
+ animation: createAnimation(sprite, 400, 0),
+});
+
+animate((entity, ctx, delta) => {
+ playAnimation(
+ ctx,
+ entity.animation,
+ entity.position.x,
+ entity.position.y,
+ delta,
+ );
+});
diff --git a/entities/testEntity/entity.ts b/entities/testEntity/entity.ts
index c5a3730..f5d2a37 100644
--- a/entities/testEntity/entity.ts
+++ b/entities/testEntity/entity.ts
@@ -1,12 +1,12 @@
import charIdle from "#/assets/char-idle.png";
import charWalk from "#/assets/char-walk.png";
-import { useEntity } from "#/engine/Entity.ts";
-import { viewport } from "#/engine/Viewport.ts";
import {
type Animation,
- animateSprite,
createAnimation,
-} from "#/lib/Animation.ts";
+ playAnimation,
+} from "#/engine/Animation.ts";
+import { useEntity } from "#/engine/Entity.ts";
+import { viewport } from "#/engine/Viewport.ts";
import { loadImage } from "#/lib/loadImage.ts";
import { createSprite } from "#/lib/Sprite.ts";
@@ -14,28 +14,28 @@ const sprites = {
idle: createSprite(await loadImage(charIdle), 2, 4),
walk: createSprite(await loadImage(charWalk), 4, 4),
};
+const animations = {
+ idle: createAnimation(sprites.idle, 500, 2),
+ walk: createAnimation(sprites.walk, 250, 2),
+} as const;
const { animate, process } = useEntity<{
+ animation: Animation;
animations: {
idle: Animation;
walk: Animation;
};
- currentAnimation: "idle" | "walk";
stuff: number;
}>(import.meta, {
- animations: {
- idle: createAnimation(2, 500),
- walk: createAnimation(2, 250),
- },
- currentAnimation: "idle",
+ animations,
+ animation: animations.idle,
stuff: 1,
});
animate((entity, ctx, delta) => {
- animateSprite(
+ playAnimation(
ctx,
- sprites[entity.currentAnimation],
- entity.animations[entity.currentAnimation],
+ entity.animation,
entity.position.x,
entity.position.y,
delta,
@@ -46,9 +46,9 @@ process((entity, delta) => {
entity.position.x += entity.stuff * 0.5;
if (entity.position.x > viewport.canvas.width - 16) {
entity.stuff = -1;
- entity.currentAnimation = "walk";
+ entity.animation = entity.animations.walk;
} else if (entity.position.x < 0) {
entity.stuff = 1;
- entity.currentAnimation = "idle";
+ entity.animation = entity.animations.idle;
}
});
diff --git a/scenes/testScene/data.json b/scenes/testScene/data.json
index b93e95a..fce5cdf 100644
--- a/scenes/testScene/data.json
+++ b/scenes/testScene/data.json
@@ -4,14 +4,14 @@
"height": 48,
"entities": [
{
- "name": "testEntity",
+ "name": "fooEntity",
"position": {
"x": 0,
"y": 0
}
},
{
- "name": "fooEntity",
+ "name": "portal",
"position": {
"x": 16,
"y": 16