From c6bb4abac082aa11ebcb6f4bc197159b0dce59eb Mon Sep 17 00:00:00 2001 From: Schluffe Date: Thu, 5 Mar 2026 23:36:33 +0100 Subject: [PATCH] wIp --- engine/Circle.ts | 2 + engine/Component.ts | 10 +--- engine/Entity.ts | 19 +++---- engine/Rect.ts | 2 + engine/Scene.ts | 61 ++++++++--------------- engine/Segment.ts | 9 +++- engine/Sprite.ts | 49 +++++++++++------- engine/dev/main.tsx | 4 +- engine/lib/MapProxy.ts | 13 +++++ game/entities/hero/entity.ts | 80 +++++++++++------------------- game/entities/portal/entity.ts | 6 +-- game/entities/testEntity/entity.ts | 20 ++++---- 12 files changed, 131 insertions(+), 144 deletions(-) create mode 100644 engine/lib/MapProxy.ts diff --git a/engine/Circle.ts b/engine/Circle.ts index ffe16af..86edba9 100644 --- a/engine/Circle.ts +++ b/engine/Circle.ts @@ -17,10 +17,12 @@ export function strokeCircle( circle: Circle, color: string = "#fff", alpha: number = 1.0, + lineWidth: number = 1, ): void { ctx.save(); ctx.strokeStyle = color; ctx.globalAlpha = alpha; + ctx.lineWidth = lineWidth; ctx.beginPath(); ctx.arc(circle.position.x, circle.position.y, circle.radius, 0, 2 * Math.PI); ctx.stroke(); diff --git a/engine/Component.ts b/engine/Component.ts index 9f04dae..48fde7a 100644 --- a/engine/Component.ts +++ b/engine/Component.ts @@ -1,9 +1,3 @@ -export const componentMap = new Map(); +import type { Box } from "#/engine/Box.ts"; -export function defineComponent(key: string): Map { - if (!componentMap.has(key)) { - componentMap.set(key, new Map()); - } - - return componentMap.get(key)!; -} +export type Component = Record>; diff --git a/engine/Entity.ts b/engine/Entity.ts index 9694210..0d8048c 100644 --- a/engine/Entity.ts +++ b/engine/Entity.ts @@ -1,18 +1,13 @@ -export type Entity = { - name: string; - state: T; -}; +export type Process = (id: string, delta: number) => void; -export type Process = (instance: T, delta: number) => void; +export const entityProcessMap = new Map(); +export const entityBlueprintsMap = new Map>(); -export const entityMap = new Map(); -export const entityProcessMap = new Map(); - -export function defineEntity( +export function defineEntity( name: string, - state: T, -): (fn: Process) => void { - entityMap.set(name, state); + blueprints: T, +): (fn: Process) => void { + entityBlueprintsMap.set(name, new Map(Object.entries(blueprints))); return (fn) => { entityProcessMap.set(name, fn); diff --git a/engine/Rect.ts b/engine/Rect.ts index f41ecd4..3a2d1a8 100644 --- a/engine/Rect.ts +++ b/engine/Rect.ts @@ -31,10 +31,12 @@ export function strokeRect( rect: Rect, color: string = "#fff", alpha: number = 1.0, + lineWidth: number = 1, ): void { ctx.save(); ctx.strokeStyle = color; ctx.globalAlpha = alpha; + ctx.lineWidth = lineWidth; ctx.strokeRect( rect.position.x - 0.5, rect.position.y - 0.5, diff --git a/engine/Scene.ts b/engine/Scene.ts index c88bde3..24bb1a7 100644 --- a/engine/Scene.ts +++ b/engine/Scene.ts @@ -1,66 +1,43 @@ import { type Box, box } from "#/engine/Box.ts"; import { type Drawable, draw, isDrawable } from "#/engine/Drawable.ts"; -import { - entityMap, - entityProcessMap, - type Process as ProcessEntity, -} from "#/engine/Entity.ts"; +import { entityBlueprintsMap, entityProcessMap } from "#/engine/Entity.ts"; import { type Graph, getNeighbours } from "#/engine/Graph.ts"; import { useWithAsyncCache } from "#/engine/lib/cache.ts"; -import { animateSprite, type SpriteAnimation } from "#/engine/Sprite.ts"; -import { componentMap, defineComponent } from "./Component"; -import { mulberry32 } from "./lib/mulberry32"; +import type MapProxy from "#/engine/lib/MapProxy.ts"; +import { mulberry32 } from "#/engine/lib/mulberry32.ts"; +import { animateSprite, type SpritePlayback } from "#/engine/Sprite.ts"; -export type EntityData = { - name: string; - init: object; -}; +export type Process = (ctx: CanvasRenderingContext2D, delta: number) => void; +export type PreProcess = () => void; +export type PostProcess = () => void; export type SceneData = { name: string; width: number; height: number; - entities: Array; + entities: string[]; }; -export type EntityMap = Map< - string, - { - process: ProcessEntity<{ id: number }>; - instances: Array<{ id: number }>; - } ->; - -export type AnimationMap = Map>; +export type ComponentsMap = Map>; export type Scene = { data: SceneData; - entityMap: EntityMap; + components: ComponentsMap; process: Process; }; -export type Process = (ctx: CanvasRenderingContext2D, delta: number) => void; -export type PreProcess = (entityMap: EntityMap) => void; -export type PostProcess = (entityMap: EntityMap) => void; - -export type SceneChangeHandler = (to: SceneData, from: SceneData) => void; +export type SceneChange = (to: SceneData, from: SceneData) => void; +export const sceneDataMap = new Map(); export const sceneProcessMap = new Map(); +export const sceneComponentsMap = new Map(); + export const scenePreProcessMap = new Map(); export const scenePostProcessMap = new Map(); -export const sceneChangedHandlers = new Set(); - -export const sceneDataMap = new Map(); -export const sceneEntiyMap = new Map(); +export const sceneChangeSet = new Set(); export const sceneGraph: Graph = new Map(); -const sceneInstancesDrawMap = new Map< - string, - Array ->(); -const sceneInstancesAnimationMap = new Map(); - export const [loadScene, sceneCache] = useWithAsyncCache( async (name: string) => { await import(`#/game/scenes/${name}/scene.ts`); @@ -203,7 +180,7 @@ export async function setScene(name: string): Promise { ) { animateInstances.set( instance.id, - (instance as { id: number; animation: Box }) + (instance as { id: number; animation: Box }) .animation, // TODO ); } @@ -227,7 +204,7 @@ export async function setScene(name: string): Promise { currentScene.entityMap = entityInstanceMap; - for (const handler of sceneChangedHandlers.values()) { + for (const handler of sceneChangeSet.values()) { handler(data, currentScene.data); } } @@ -245,3 +222,7 @@ export function def(name: string): (key: string, value: T) => void { console.log(componentMap); }; } + +export default function (name: string): MapProxy { + return currentScene.components.get(name) as MapProxy; +} diff --git a/engine/Segment.ts b/engine/Segment.ts index a078108..7a14b78 100644 --- a/engine/Segment.ts +++ b/engine/Segment.ts @@ -61,12 +61,17 @@ export function setSegmentIntersection( export function drawSegment( segment: Segment, ctx: CanvasRenderingContext2D, - color: string, + color: string = "#fff", + alpha: number = 1.0, + lineWidth: number = 1, ): void { - ctx.lineWidth = 2; + ctx.save(); ctx.strokeStyle = color; + ctx.globalAlpha = alpha; + ctx.lineWidth = lineWidth; ctx.beginPath(); ctx.moveTo(segment[0].x, segment[0].y); ctx.lineTo(segment[1].x, segment[1].y); ctx.stroke(); + ctx.restore(); } diff --git a/engine/Sprite.ts b/engine/Sprite.ts index 91f2149..1f69a82 100644 --- a/engine/Sprite.ts +++ b/engine/Sprite.ts @@ -16,8 +16,7 @@ export type SpriteSheet = { frameHeight: number; }; -export type SpriteAnimation = { - sprite: Sprite; +export type SpritePlayback = { xIndex: number; yIndex: number; frameTime: number; @@ -26,7 +25,7 @@ export type SpriteAnimation = { // It's not possible to crate a structured clone of a HTMLImageElement and it would be a // waste of memory anyways since we use the HTMLImageElement only as reference for draw calls. -// Therefore the SpriteSheet will stored by it's image source. +// Therefore the SpriteSheet will stored and referenced by it's image source. export const spriteSheetMap = new Map(); export function createSpriteSheet( @@ -43,7 +42,23 @@ export function createSpriteSheet( }; } -export async function useSpriteSheetSrc( +export function createSprite( + src: string, + xStartFrame: number = 0, + yStartFrame: number = 0, +): Sprite { + const spriteSheet = spriteSheetMap.get(src)!; + + return { + src, + xStart: xStartFrame * spriteSheet.frameWidth, + yStart: yStartFrame * spriteSheet.frameHeight, + width: spriteSheet.frameWidth, + height: spriteSheet.frameHeight, + }; +} + +export async function defineSpriteSheet( src: string, xFrames: number, yFrames: number, @@ -56,15 +71,11 @@ export async function useSpriteSheetSrc( return src; } -export function createSpriteAnimation( - sprite: Sprite, +export function createSpritePlayback( frameTime: number, yIndex: number, -): SpriteAnimation { - sprite.yStart = yIndex * spriteSheetMap.get(sprite.src)!.frameHeight; - +): SpritePlayback { return { - sprite, xIndex: 0, yIndex, frameTime, @@ -95,14 +106,18 @@ export function drawSprite( ); } -export function animateSprite(animation: SpriteAnimation, delta: number): void { - const sheet = spriteSheetMap.get(animation.sprite.src)!; - if ((animation.frameDelta += delta) > animation.frameTime) { - animation.frameDelta = 0; - if ((animation.xIndex += 1) >= sheet.xFrames) { - animation.xIndex = 0; +export function animateSprite( + sprite: Sprite, + playback: SpritePlayback, + delta: number, +): void { + const sheet = spriteSheetMap.get(sprite.src)!; + if ((playback.frameDelta += delta) > playback.frameTime) { + playback.frameDelta = 0; + if ((playback.xIndex += 1) >= sheet.xFrames) { + playback.xIndex = 0; } } - animation.sprite.xStart = animation.xIndex * sheet.frameWidth; + sprite.xStart = playback.xIndex * sheet.frameWidth; } diff --git a/engine/dev/main.tsx b/engine/dev/main.tsx index eecfac3..66eb08e 100644 --- a/engine/dev/main.tsx +++ b/engine/dev/main.tsx @@ -11,7 +11,7 @@ import type { Entity } from "#/engine/Entity.ts"; import { currentScene, type SceneData, - sceneChangedHandlers, + sceneChangeSet, sceneGraph, setScene, } from "#/engine/Scene.ts"; @@ -86,7 +86,7 @@ const DevTools: Component<{ gameContainer: HTMLElement }> = ({ await setScene(name); }; - sceneChangedHandlers.add((data) => { + sceneChangeSet.add((data) => { setActiveEntity(null); setSceneData(data); setEntities(mapSceneEntities(data)); diff --git a/engine/lib/MapProxy.ts b/engine/lib/MapProxy.ts new file mode 100644 index 0000000..14e24eb --- /dev/null +++ b/engine/lib/MapProxy.ts @@ -0,0 +1,13 @@ +import type { Box } from "#/engine/Box.ts"; + +export default class MapProxy { + map: Map>; + + constructor() { + this.map = new Map(); + } + + get(key: K): V | undefined { + return this.map.get(key)?.value; + } +} diff --git a/game/entities/hero/entity.ts b/game/entities/hero/entity.ts index 5e1976c..e1f51c0 100644 --- a/game/entities/hero/entity.ts +++ b/game/entities/hero/entity.ts @@ -1,70 +1,50 @@ -import { type Box, box } from "#/engine/Box.ts"; -import { type Circle, createCircle } from "#/engine/Circle.ts"; -import { moveAndCollide } from "#/engine/Collision.ts"; -import { defineEntity, type Process } from "#/engine/Entity.ts"; +// import { createCircle } from "#/engine/Circle.ts"; +// import { moveAndCollide } from "#/engine/Collision.ts"; +import { defineEntity } from "#/engine/Entity.ts"; import { keyboardInput } from "#/engine/Keyboard.ts"; import { pointer } from "#/engine/Pointer.ts"; -import { def } from "#/engine/Scene.ts"; +import $ from "#/engine/Scene.ts"; import { - createSpriteAnimation, - type Sprite, - type SpriteAnimation, - useSpriteSheetSrc, + createSprite, + createSpritePlayback, + defineSpriteSheet, } from "#/engine/Sprite.ts"; import { createVector, type Vector } from "#/engine/Vector.ts"; import charIdle from "#/game/assets/char-idle.png"; -export type Hero = { - position: Vector; - velocity: Vector; - body: Circle; - sprite: Sprite; - animation: Box; -}; - const speed = 0.05; -const idleSheet = await useSpriteSheetSrc(charIdle, 2, 4); -const sprite = { - src: idleSheet, - xStart: 0, - yStart: 0, - width: 16, - height: 16, -}; +const idleSrc = await defineSpriteSheet(charIdle, 2, 4); -const state: Hero = { +const process = defineEntity("hero", { position: createVector(), + sprite: createSprite(idleSrc), + playback: createSpritePlayback(500, 2), + // body: createCircle(createVector(), 4), velocity: createVector(), - body: createCircle(createVector(), 4), - sprite, - animation: box(createSpriteAnimation(sprite, 500, 2)), -}; +}); -const $ = def("hero"); -$("position", createVector()); -$("process", (_id, _delta) => {}); +process((id, delta) => { + const position = $("position").get(id)!; + const velocity = velocityMap.get(id)!; + // const body = bodyMap.get(id); -const process = defineEntity("hero", state); - -process((entity, delta) => { if (pointer.isDown) { - entity.position.x = pointer.position.x; - entity.position.y = pointer.position.y; + position.x = pointer.position.x; + position.y = pointer.position.y; } - entity.velocity.x -= keyboardInput.arrowLeft ? speed : 0; - entity.velocity.x += keyboardInput.arrowRight ? speed : 0; - entity.velocity.y -= keyboardInput.arrowUp ? speed : 0; - entity.velocity.y += keyboardInput.arrowDown ? speed : 0; + velocity.x -= keyboardInput.arrowLeft ? speed : 0; + velocity.x += keyboardInput.arrowRight ? speed : 0; + velocity.y -= keyboardInput.arrowUp ? speed : 0; + velocity.y += keyboardInput.arrowDown ? speed : 0; - entity.position.x = Math.round(entity.velocity.x * delta + entity.position.x); - entity.position.y = Math.round(entity.velocity.y * delta + entity.position.y); + // const collision = moveAndCollide(body, velocity, delta); + // slideOnCollision(velocity, collision); + // slide(velocity, collision.normal); - entity.velocity.x = 0; - entity.velocity.y = 0; + position.x = Math.round(velocity.x * delta + position.x); + position.y = Math.round(velocity.y * delta + position.y); - moveAndCollide(entity.body, entity.velocity); - - // entity.body.position.x = entity.body.offset.x + entity.position.x; - // entity.body.position.y = entity.body.offset.y + entity.position.y; + velocity.x = 0; + velocity.y = 0; }); diff --git a/game/entities/portal/entity.ts b/game/entities/portal/entity.ts index 68bed64..9a5010f 100644 --- a/game/entities/portal/entity.ts +++ b/game/entities/portal/entity.ts @@ -1,10 +1,10 @@ import { box } from "#/engine/Box.ts"; import { defineEntity } from "#/engine/Entity.ts"; -import { createSpriteAnimation, useSpriteSheetSrc } from "#/engine/Sprite.ts"; +import { createSpritePlayback, defineSpriteSheet } from "#/engine/Sprite.ts"; import { createVector } from "#/engine/Vector.ts"; import portal from "#/game/assets/portal.png"; -const portalSheet = await useSpriteSheetSrc(portal, 3, 1); +const portalSheet = await defineSpriteSheet(portal, 3, 1); const sprite = { src: portalSheet, xStart: 0, @@ -16,5 +16,5 @@ const sprite = { defineEntity("portal", { position: createVector(64, 64), sprite, - animation: box(createSpriteAnimation(sprite, 400, 0)), + animation: box(createSpritePlayback(sprite, 400, 0)), }); diff --git a/game/entities/testEntity/entity.ts b/game/entities/testEntity/entity.ts index 5604b7a..87c431c 100644 --- a/game/entities/testEntity/entity.ts +++ b/game/entities/testEntity/entity.ts @@ -1,10 +1,10 @@ import { type Box, box } from "#/engine/Box.ts"; import { defineEntity } from "#/engine/Entity.ts"; import { - createSpriteAnimation, + createSpritePlayback, + defineSpriteSheet, type Sprite, - type SpriteAnimation, - useSpriteSheetSrc, + type SpritePlayback, } from "#/engine/Sprite.ts"; import { createVector, type Vector } from "#/engine/Vector.ts"; import { viewport } from "#/engine/Viewport.ts"; @@ -12,8 +12,8 @@ import charIdle from "#/game/assets/char-idle.png"; import charWalk from "#/game/assets/char-walk.png"; const sheets = { - idle: await useSpriteSheetSrc(charIdle, 2, 4), - walk: await useSpriteSheetSrc(charWalk, 4, 4), + idle: await defineSpriteSheet(charIdle, 2, 4), + walk: await defineSpriteSheet(charWalk, 4, 4), }; const sprites = { @@ -34,16 +34,16 @@ const sprites = { }; const animations = { - idle: createSpriteAnimation(sprites.idle, 500, 2), - walk: createSpriteAnimation(sprites.walk, 250, 2), + idle: createSpritePlayback(sprites.idle, 500, 2), + walk: createSpritePlayback(sprites.walk, 250, 2), }; const process = defineEntity<{ position: Vector; - animation: Box; + animation: Box; animations: { - idle: SpriteAnimation; - walk: SpriteAnimation; + idle: SpritePlayback; + walk: SpritePlayback; }; sprite: Sprite; sprites: {