From 1f382114d746494957914d83c59ed8ce0b27b9ee Mon Sep 17 00:00:00 2001 From: KeSuave Date: Wed, 20 Nov 2024 13:55:43 -0800 Subject: [PATCH] feat: fixture events --- src/lib/components/Fixture.ts | 79 ++++++++++++++++++++++++++++++++++- src/lib/plugin.ts | 11 ++++- src/lib/utils.ts | 20 ++++++++- 3 files changed, 105 insertions(+), 5 deletions(-) diff --git a/src/lib/components/Fixture.ts b/src/lib/components/Fixture.ts index a2fc871..9b8ce9d 100644 --- a/src/lib/components/Fixture.ts +++ b/src/lib/components/Fixture.ts @@ -1,8 +1,20 @@ -import type { Comp, GameObj } from "kaplay"; +import type { + Comp, + GameObj, + KAPLAYCtx, + KEventController, + MouseButton, +} from "kaplay"; import type { Fixture, FixtureDef } from "planck"; import type { KPBodyComp } from "./Body"; import type { KPShapeComp } from "./Shape"; +import { k2pVec2 } from "../utils"; + +export interface KPFixtureUserData { + gameObj: GameObj; + [key: string]: unknown; +} export interface KPFixtureDef extends Omit { userData?: Record; @@ -10,16 +22,79 @@ export interface KPFixtureDef extends Omit { export interface KPFixtureComp extends Comp { fixture: Fixture | null; + + isHovering(): boolean; + isClicked(): boolean; + onHover(action: () => void): KEventController; + onHoverUpdate(action: () => void): KEventController; + onHoverEnd(action: () => void): KEventController; + onClick(action: () => void, btn?: MouseButton): KEventController; } type FixtureThis = GameObj; -export default function fixture(def?: KPFixtureDef): KPFixtureComp { +export default function fixture( + k: KAPLAYCtx, + def?: KPFixtureDef, +): KPFixtureComp { return { id: "kpFixture", require: ["kpBody", "kpShape"], fixture: null, + isHovering() { + if (!this.fixture) return false; + + return this.fixture.testPoint(k2pVec2(k.mousePos())); + }, + isClicked() { + return k.isMousePressed() && this.isHovering(); + }, + onHover(this: FixtureThis, action: () => void) { + let hovering = false; + + return this.onUpdate(() => { + if (!hovering) { + if (this.isHovering()) { + hovering = true; + + action(); + } + } else { + hovering = this.isHovering(); + } + }); + }, + onHoverUpdate(this: FixtureThis, action: () => void) { + return this.onUpdate(() => { + if (this.isHovering()) action(); + }); + }, + onHoverEnd(this: FixtureThis, action: () => void) { + let hovering = false; + + return this.onUpdate(() => { + if (hovering) { + if (!this.isHovering()) { + hovering = false; + + action(); + } + } else { + hovering = this.isHovering(); + } + }); + }, + onClick(this: FixtureThis, action: () => void, btn = "left") { + const evt = k.onMousePress(btn, () => { + if (this.isHovering()) action(); + }); + + this.onDestroy(() => evt.cancel()); + + return evt; + }, + add(this: FixtureThis) { if (!this.body) throw new Error("kpBody is required"); diff --git a/src/lib/plugin.ts b/src/lib/plugin.ts index 443e313..d5ab987 100644 --- a/src/lib/plugin.ts +++ b/src/lib/plugin.ts @@ -51,7 +51,7 @@ import { } from "./utils"; interface KaPlanckPlugin { - // mimicked transform components + // transform components /** * Sets the position of a body. @@ -305,6 +305,13 @@ interface KaPlanckPluginOpts { /** * A plugin to allows to use PlanckJS seamlessly with KaPlay. * + * **IMPORTANT** + * + * Vec2 from KaPlay and Vec2 from PlanckJS are different, please use them accordingly. + * + * PlanckJS uses radians for angles while KaPlay uses degrees, please make use of KaPlay's + * tools to convert between them. + * * @param {KaPlanckPluginOpts} opt */ const KaPlanckPlugin = @@ -327,7 +334,7 @@ const KaPlanckPlugin = return body(bodyDef); }, kpFixture(def?: KPFixtureDef) { - return fixture(def); + return fixture(k, def); }, kpFixtures(defs: KPFixtureDef[]) { return fixtures(defs); diff --git a/src/lib/utils.ts b/src/lib/utils.ts index b4d52ab..989739a 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,7 +1,7 @@ import type { GameObj, KAPLAYCtx, Vec2 as KaVec2, RenderProps } from "kaplay"; import { Settings, Vec2, type World } from "planck"; -import { KPWorldComp } from "./components/World"; +import type { KPWorldComp } from "./components/World"; export function u2p(u: number) { return u * Settings.lengthUnitsPerMeter; @@ -54,3 +54,21 @@ export function getRenderProps(obj: GameObj): RenderProps { uniform: obj.uniform, }; } + +export function findWorldContainer(k: KAPLAYCtx): GameObj | null { + let obj: GameObj | null = null; + + const objs = k.get("*"); + + for (const currentObj of objs) { + const possibleWorldComp = currentObj.c("kpWorld") as KPWorldComp | null; + + if (possibleWorldComp) { + obj = currentObj as GameObj; + + break; + } + } + + return obj; +}