From f605105ffc2c25722317d77f32b9bcb0329da197 Mon Sep 17 00:00:00 2001 From: Marc Flerackers Date: Sat, 30 Nov 2024 10:36:23 +0900 Subject: [PATCH] fix: Fixes live query, using comp change events (#547) --- examples/livequery.js | 35 +++++++++++++++++++++++++++++++++ src/game/events/events.ts | 4 ++-- src/game/make.ts | 17 +++++++++++++++- src/kaplay.ts | 13 ++++++++----- src/types.ts | 41 +++++++++++++++++++++++++++++++++++++-- 5 files changed, 100 insertions(+), 10 deletions(-) create mode 100644 examples/livequery.js diff --git a/examples/livequery.js b/examples/livequery.js new file mode 100644 index 00000000..4097e6be --- /dev/null +++ b/examples/livequery.js @@ -0,0 +1,35 @@ +kaplay(); + +loadSprite("ghosty", "/sprites/ghosty.png"); + +const q = get("color", { liveUpdate: true }); + +loop(5, () => { + if (q.length < 10) { + const x = rand(0, width()); + const y = rand(0, height()); + + const ghost = add([ + sprite("ghosty"), + pos(x, y), + area(), + timer(), + color(RED), + "ghost" + ]); + ghost.wait(5, () => { + ghost.unuse("color"); + ghost.wait(5, () => { + ghost.use(color(RED)); + }) + }) + } +}); + +onClick("ghost", (ghost) => { + ghost.destroy(); +}); + +loop(1, () => { + debug.log(`There are ${q.length} touchable ghosts`); +}); \ No newline at end of file diff --git a/src/game/events/events.ts b/src/game/events/events.ts index 580bd773..718add0a 100644 --- a/src/game/events/events.ts +++ b/src/game/events/events.ts @@ -92,13 +92,13 @@ export const onDestroy = overload2((action: (obj: GameObj) => void) => { export const onUse = overload2((action: (obj: GameObj, id: string) => void) => { return _k.game.events.on("use", action); -}, (tag: Tag, action: (obj: GameObj, id: string) => void) => { +}, (tag: Tag, action: (obj: GameObj) => void) => { return on("use", tag, action); }); export const onUnuse = overload2((action: (obj: GameObj, id: string) => void) => { return _k.game.events.on("unuse", action); -}, (tag: Tag, action: (obj: GameObj, id: string) => void) => { +}, (tag: Tag, action: (obj: GameObj) => void) => { return on("unuse", tag, action); }); diff --git a/src/game/make.ts b/src/game/make.ts index 890afa53..e3e071b8 100644 --- a/src/game/make.ts +++ b/src/game/make.ts @@ -384,7 +384,6 @@ export function make(comps: CompList = []): GameObj { const events: KEventController[] = []; - // TODO: handle when object add / remove tags // TODO: clean up when obj destroyed events.push(_k.k.onAdd((obj) => { if (isChild(obj) && checkTagsOrComps(obj, t)) { @@ -392,7 +391,23 @@ export function make(comps: CompList = []): GameObj { } })); events.push(_k.k.onDestroy((obj) => { + if (checkTagsOrComps(obj, t)) { + const idx = list.findIndex((o) => o.id === obj.id); + if (idx !== -1) { + list.splice(idx, 1); + } + } + })); + events.push(_k.k.onUse((obj) => { if (isChild(obj) && checkTagsOrComps(obj, t)) { + const idx = list.findIndex((o) => o.id === obj.id); + if (idx == -1) { + list.push(obj); + } + } + })); + events.push(_k.k.onUnuse((obj, id) => { + if (isChild(obj) && !checkTagsOrComps(obj, t)) { const idx = list.findIndex((o) => o.id === obj.id); if (idx !== -1) { list.splice(idx, 1); diff --git a/src/kaplay.ts b/src/kaplay.ts index f02d1bbe..2dac4d58 100644 --- a/src/kaplay.ts +++ b/src/kaplay.ts @@ -262,6 +262,8 @@ import { onCollideEnd, onCollideUpdate, onDestroy, + onUse, + onUnuse, onDraw, onError, onFixedUpdate, @@ -349,8 +351,7 @@ const kaplay = < >( gopt: KAPLAYOpt = {}, ): TPlugins extends [undefined] ? KAPLAYCtx - : KAPLAYCtx & MergePlugins => -{ + : KAPLAYCtx & MergePlugins => { if (_k.k) { console.warn( "KAPLAY already initialized, you are calling kaplay() multiple times, it may lead bugs!", @@ -972,7 +973,7 @@ const kaplay = < // TODO: this should only run once app.run( - () => {}, + () => { }, () => { frameStart(); @@ -1035,7 +1036,7 @@ const kaplay = < // clear canvas gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT - | gl.STENCIL_BUFFER_BIT, + | gl.STENCIL_BUFFER_BIT, ); // unbind everything @@ -1266,6 +1267,8 @@ const kaplay = < onDraw, onAdd, onDestroy, + onUse, + onUnuse, onClick, onCollide, onCollideUpdate, @@ -1481,7 +1484,7 @@ const kaplay = < // export everything to window if global is set if (gopt.global !== false) { for (const key in ctx) { - ( window[ key]) = ctx[key as keyof KAPLAYCtx]; + (window[key]) = ctx[key as keyof KAPLAYCtx]; } } diff --git a/src/types.ts b/src/types.ts index 8cdfbe6f..75aaf75a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -311,6 +311,10 @@ export interface KAPLAYCtx< * // Recursively get all children and descendents * const allObjs = get("*", { recursive: true }); * ``` + * + * // Get a live query which updates in real-time + * const allObjs = get("*", { liveUpdate: true }); + * ``` * * @returns A list of game objects that have the tag. * @since v2000.0 @@ -338,6 +342,18 @@ export interface KAPLAYCtx< * include: "friend", * exclude: "bean", * }); // will return [bag] + * + * // get all visible friends + * query({ + * include: "friend", + * visible: true, + * }); + * + * // get all friends less than 150 pixels from bean + * bean.query({ + * include: "friend", + * distance: 150, + * }); * * ``` * @@ -1597,6 +1613,7 @@ export interface KAPLAYCtx< /** * Register an event that runs when an object is added * + * @param tag - The tag to match, only called for objects with a matching tag. * @param action - The function that runs when an object is added. * * @example @@ -1619,7 +1636,6 @@ export interface KAPLAYCtx< /** * Register an event that runs when an object with the provided tag is destroyed. * - * @param tag - The tag to listen for. * @param action - The function that runs when an object is destroyed. * * @example @@ -1646,6 +1662,7 @@ export interface KAPLAYCtx< /** * Register an event that runs when an object is destroyed. * + * @param tag - The tag to match, only called for objects with a matching tag. * @param action - The function that runs when an object is destroyed. * * @example @@ -1667,6 +1684,26 @@ export interface KAPLAYCtx< * @group Events */ onDestroy(action: (obj: GameObj) => void): KEventController; + /** + * Register an event that runs when an object starts using a component. + * + * @param action - The function that runs when an object starts using component. + * + * @returns The event controller. + * @since v3001.1 + * @group Events + */ + onUse(action: (obj: GameObj, id: string) => void): KEventController; + /** + * Register an event that runs when an object stops using a component. + * + * @param action - The function that runs when an object stops using a component. + * + * @returns The event controller. + * @since v3001.1 + * @group Events + */ + onUnuse(action: (obj: GameObj, id: string) => void): KEventController; /** * Register an event that runs when all assets finished loading. * @@ -2246,7 +2283,7 @@ export interface KAPLAYCtx< * @returns The event controller. * @since v3001.0 * @group Input - */ + */ onMousePress( btn: MouseButton | MouseButton[], action: (m: MouseButton) => void,