refactor: move world concepts into engine
This commit is contained in:
parent
eeedcb8e48
commit
2d15c7ab8d
52 changed files with 3298 additions and 1558 deletions
|
|
@ -1,26 +1,48 @@
|
|||
import type { Mesh } from "./Mesh"
|
||||
import type { RenderPrototype, RenderTransform } from "../render/RenderScene"
|
||||
import type { Collider } from "../world/Collider"
|
||||
|
||||
/** Definition of an **Entity** actor kind: something that lives in the world with
|
||||
* its own behavior and a live transform (mobs, and later the npc / powerups) -- as
|
||||
* opposed to a baked, static `Prop`. `State` is the per-instance runtime record the
|
||||
* behavior mutates; `World` is whatever that behavior reads (e.g. `Terrain`).
|
||||
*
|
||||
* Every field is plain data or a module function, so a definition is **imported
|
||||
* into each context** (main thread + each render worker) rather than structured-
|
||||
* cloned across the wire -- the per-kind polymorphism is code, not serialized
|
||||
* state. That's what lets a registry of these stay compatible with the worker
|
||||
* renderer (only plain instance data ever crosses; behavior is loaded per side). */
|
||||
export type Entity<State, World> = {
|
||||
/** Stable tag for the kind (also the texture key today). The registry's order,
|
||||
* not this string, is what becomes the id packed into the mob SAB. */
|
||||
name: string
|
||||
/** Build the canonical local-space mesh once; every instance shares it, differing
|
||||
* only by its per-frame model matrix. */
|
||||
build: (mesh: Mesh) => void
|
||||
/** Advance one instance by `dt` seconds. */
|
||||
/** Open actor behavior and representation. Concrete definitions are ordinary game
|
||||
* objects referenced directly by instances. */
|
||||
export type ActorDefinition<State, World> = {
|
||||
prototype: RenderPrototype
|
||||
update: (state: State, dt: number, world: World) => void
|
||||
/** Local bounding radius (pre-scale) for the per-frame cull AABB. */
|
||||
boundingRadius: number
|
||||
/** Local body height (pre-scale) for the top of the stand-on collider. */
|
||||
bodyHeight: number
|
||||
transform: (state: State) => RenderTransform
|
||||
collider?: (state: State, world: World) => Collider | null
|
||||
}
|
||||
|
||||
/** Type-erased live actor. `create` captures concrete state safely, allowing one
|
||||
* level to hold unrelated actor state types without a content union. */
|
||||
export type Actor<World> = {
|
||||
readonly definition: object
|
||||
readonly prototype: RenderPrototype
|
||||
readonly updateState: (dt: number, world: World) => void
|
||||
readonly readTransform: () => RenderTransform
|
||||
readonly readCollider: (world: World) => Collider | null
|
||||
}
|
||||
|
||||
export namespace Actor {
|
||||
export function create<State, World>(
|
||||
definition: ActorDefinition<State, World>,
|
||||
state: State,
|
||||
): Actor<World> {
|
||||
return {
|
||||
definition,
|
||||
prototype: definition.prototype,
|
||||
updateState: (dt, world) => definition.update(state, dt, world),
|
||||
readTransform: () => definition.transform(state),
|
||||
readCollider: (world) => definition.collider?.(state, world) ?? null,
|
||||
}
|
||||
}
|
||||
|
||||
export function update<World>(actor: Actor<World>, dt: number, world: World): void {
|
||||
actor.updateState(dt, world)
|
||||
}
|
||||
|
||||
export function transform<World>(actor: Actor<World>): RenderTransform {
|
||||
return actor.readTransform()
|
||||
}
|
||||
|
||||
export function collider<World>(actor: Actor<World>, world: World): Collider | null {
|
||||
return actor.readCollider(world)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue