@combos-fun/plugin-renderer-3d-event 0.0.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,27 @@
1
+ # @combos-fun/plugin-renderer-3d-event
2
+
3
+ @combos-fun/plugin-renderer-3d-event — part of the Combos Fun engine monorepo.
4
+
5
+ Keywords: `3d`, `pointer`, `touch`, `tap`, `raycast`, `input`, `event`.
6
+
7
+ ## Documentation
8
+
9
+ - Per-package agent / developer notes: [`agent-skill.md`](./agent-skill.md)
10
+ - Machine-readable manifest: [`combos-plugin.json`](./combos-plugin.json)
11
+ (validated against `schemas/combos-plugin.schema.json` in the repo
12
+ root)
13
+ - Entry skill (single source of truth for AI agents working with this
14
+ monorepo): `skills/combos-engine-development/SKILL.md`
15
+
16
+ When consumed from npm, the manifest and agent notes are exposed as
17
+ stable subpaths:
18
+
19
+ ```ts
20
+ import manifest from '@combos-fun/plugin-renderer-3d-event/plugin-manifest';
21
+ // or fetch the markdown directly:
22
+ // require.resolve('@combos-fun/plugin-renderer-3d-event/agent-skill')
23
+ ```
24
+
25
+ ## License
26
+
27
+ Internal workspace package, part of the Combos Fun engine monorepo.
package/agent-skill.md ADDED
@@ -0,0 +1,79 @@
1
+ # `@combos-fun/plugin-renderer-3d-event` — Agent notes
2
+
3
+ Pointer / touch input for the 3D pipeline via Three.js `Raycaster`. Pointer events on the WebGL canvas are converted to NDC, raycast against tagged meshes, and forwarded as `tap` / `touchstart` / `touchmove` / `touchend` on the `Event3D` Component.
4
+
5
+ There is **no keyboard** API here (same as 2D `plugin-renderer-event`). `surface.input` in host templates is a design hint, not a runtime switch.
6
+
7
+ ## When to read
8
+
9
+ Read for any 3D input task: tap a mesh, drag, pick a model, or wire UI-like 3D objects.
10
+
11
+ ## Public API
12
+
13
+ ```ts
14
+ import {
15
+ Event3DSystem,
16
+ Event3D,
17
+ type Event3DParams,
18
+ type Event3DSystemParams,
19
+ } from '@combos-fun/plugin-renderer-3d-event';
20
+ ```
21
+
22
+ `componentName = 'Event3D'`, `systemName = 'Event3DSystem'`. Use `game.getSystem(Event3DSystem)`, not a string.
23
+
24
+ Hit testing uses the GameObject's mesh (every 3D renderer tags `Object3D.userData.combosGameObjectId`). There is no separate `hitArea` shape.
25
+
26
+ ### Events emitted by the `Event3D` component
27
+
28
+ `touchstart`, `touchmove`, `touchend`, `tap`, `touchendoutside`, `touchcancel`.
29
+
30
+ Callback payload:
31
+
32
+ ```ts
33
+ (payload) => {
34
+ payload.stopPropagation();
35
+ payload.data.pointerId;
36
+ payload.data.position; // canvas-pixel space
37
+ payload.data.localPosition; // hit point in the mesh's local space (x/y/z)
38
+ payload.gameObject;
39
+ };
40
+ ```
41
+
42
+ ## Required setup
43
+
44
+ - Add `Renderer3DSystem` before `Event3DSystem`.
45
+ - Put `Event3D` on the same GameObject as the visual (`Model3D`, `Graphics3D`, `Img3D`, …).
46
+ - Meshes must already be in the scene (the matching 3D renderer system must be registered).
47
+
48
+ ## Runtime behaviour
49
+
50
+ - First raycast hit that belongs to a GameObject with `Event3D` wins.
51
+ - `tap` fires on `pointerup` when the press and release hit the same GameObject.
52
+ - `touchendoutside` fires when the pointer is released on a different object (or none).
53
+
54
+ ## Common pitfalls
55
+
56
+ | Symptom | Fix |
57
+ |---------|-----|
58
+ | `tap` never fires | Add `Event3DSystem`, attach `Event3D` on the **same** GO as the mesh |
59
+ | Hits the wrong object | First mesh along the ray wins; move the camera or the mesh |
60
+ | Nothing is pickable | Confirm the 3D renderer tagged the mesh (official `plugin-renderer-3d-*` systems do) |
61
+ | Using 2D `Event` / `EventSystem` in a 3D game | Use `Event3D` / `Event3DSystem` — Pixi hit-areas do not apply |
62
+
63
+ ## Minimal example
64
+
65
+ ```ts
66
+ import { Event3D } from '@combos-fun/plugin-renderer-3d-event';
67
+ import { Graphics3D } from '@combos-fun/plugin-renderer-3d-graphics';
68
+
69
+ const box = new GameObject('box');
70
+ box.addComponent(new Graphics3D({ shape: 'box', width: 1, height: 1, depth: 1, color: 0xffaa00 }));
71
+ box.addComponent(new Event3D());
72
+ box.getComponent(Event3D)!.on('tap', () => {
73
+ /* handle */
74
+ });
75
+ ```
76
+
77
+ ## Verification
78
+
79
+ `pnpm --filter @combos-fun/plugin-renderer-3d-event run build`.
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "@combos-fun/plugin-renderer-3d-event",
3
+ "pluginId": "renderer-3d-event",
4
+ "category": "input",
5
+ "dimension": "3d",
6
+ "isCore": false,
7
+ "keywords": ["3d", "pointer", "touch", "tap", "raycast", "input", "event"],
8
+ "agentSkill": "./agent-skill.md",
9
+ "requires": ["@combos-fun/engine", "@combos-fun/plugin-renderer-3d"],
10
+ "exports": ["Event3DSystem", "Event3D"]
11
+ }
@@ -0,0 +1,174 @@
1
+ 'use strict';
2
+
3
+ var tslib = require('tslib');
4
+ var engine = require('@combos-fun/engine');
5
+ var pluginRenderer3d = require('@combos-fun/plugin-renderer-3d');
6
+ var three = require('three');
7
+
8
+ class Event3D extends engine.Component {
9
+ static { this.componentName = 'Event3D'; }
10
+ init(params) {
11
+ params && Object.assign(this, params);
12
+ }
13
+ emit(en, ...args) {
14
+ return super.emit(en, ...args);
15
+ }
16
+ once(en, fn, context) {
17
+ return super.once(en, fn, context);
18
+ }
19
+ on(en, fn, context) {
20
+ return super.on(en, fn, context);
21
+ }
22
+ }
23
+
24
+ let Event3DSystem = class Event3DSystem extends pluginRenderer3d.Renderer3D {
25
+ constructor() {
26
+ super(...arguments);
27
+ this.name = 'Event3DSystem';
28
+ this.raycaster = new three.Raycaster();
29
+ this.ndc = new three.Vector2();
30
+ this.localScratch = new three.Vector3();
31
+ this.downByPointer = new Map();
32
+ this.canvas = null;
33
+ this.onPointerDown = (e) => this.handlePointer(e, 'touchstart');
34
+ this.onPointerMove = (e) => this.handlePointer(e, 'touchmove');
35
+ this.onPointerUp = (e) => this.handlePointer(e, 'touchend');
36
+ this.onPointerCancel = (e) => this.handlePointer(e, 'touchcancel');
37
+ }
38
+ static { this.systemName = 'Event3DSystem'; }
39
+ init(_params) {
40
+ const renderer3DSystem = this.game.getSystem(pluginRenderer3d.Renderer3DSystem);
41
+ renderer3DSystem.rendererManager.register(this);
42
+ this.canvas = this.threeContext.renderer.domElement;
43
+ this.canvas.style.touchAction = 'none';
44
+ this.canvas.addEventListener('pointerdown', this.onPointerDown);
45
+ this.canvas.addEventListener('pointermove', this.onPointerMove);
46
+ this.canvas.addEventListener('pointerup', this.onPointerUp);
47
+ this.canvas.addEventListener('pointercancel', this.onPointerCancel);
48
+ this.canvas.addEventListener('pointerleave', this.onPointerCancel);
49
+ }
50
+ componentChanged(changed) {
51
+ if (changed.componentName !== 'Event3D')
52
+ return;
53
+ if (changed.type === engine.OBSERVER_TYPE.REMOVE) {
54
+ changed.component.removeAllListeners();
55
+ for (const [pointerId, hit] of this.downByPointer) {
56
+ if (hit.gameObject.id === changed.gameObject.id) {
57
+ this.downByPointer.delete(pointerId);
58
+ }
59
+ }
60
+ }
61
+ }
62
+ rendererUpdate(_gameObject) { }
63
+ onDestroy() {
64
+ if (!this.canvas)
65
+ return;
66
+ this.canvas.removeEventListener('pointerdown', this.onPointerDown);
67
+ this.canvas.removeEventListener('pointermove', this.onPointerMove);
68
+ this.canvas.removeEventListener('pointerup', this.onPointerUp);
69
+ this.canvas.removeEventListener('pointercancel', this.onPointerCancel);
70
+ this.canvas.removeEventListener('pointerleave', this.onPointerCancel);
71
+ this.canvas = null;
72
+ this.downByPointer.clear();
73
+ }
74
+ handlePointer(e, kind) {
75
+ const hit = this.hitFromEvent(e);
76
+ const canvasPos = this.canvasPosition(e);
77
+ if (kind === 'touchstart') {
78
+ if (!hit)
79
+ return;
80
+ this.downByPointer.set(e.pointerId, hit);
81
+ this.emitOn(hit, 'touchstart', e, canvasPos);
82
+ return;
83
+ }
84
+ if (kind === 'touchmove') {
85
+ if (!hit)
86
+ return;
87
+ this.emitOn(hit, 'touchmove', e, canvasPos);
88
+ return;
89
+ }
90
+ const down = this.downByPointer.get(e.pointerId);
91
+ this.downByPointer.delete(e.pointerId);
92
+ if (kind === 'touchcancel') {
93
+ if (down)
94
+ this.emitOn(down, 'touchcancel', e, canvasPos);
95
+ return;
96
+ }
97
+ if (hit && down && hit.gameObject.id === down.gameObject.id) {
98
+ this.emitOn(hit, 'touchend', e, canvasPos);
99
+ this.emitOn(hit, 'tap', e, canvasPos);
100
+ return;
101
+ }
102
+ if (down) {
103
+ this.emitOn(down, 'touchendoutside', e, canvasPos);
104
+ }
105
+ if (hit) {
106
+ this.emitOn(hit, 'touchend', e, canvasPos);
107
+ }
108
+ }
109
+ emitOn(hit, eventName, e, canvasPos) {
110
+ let stopped = false;
111
+ hit.component.emit(eventName, {
112
+ stopPropagation: () => { stopped = true; },
113
+ data: {
114
+ pointerId: e.pointerId,
115
+ position: canvasPos,
116
+ localPosition: {
117
+ x: hit.local.x,
118
+ y: hit.local.y,
119
+ z: hit.local.z,
120
+ },
121
+ },
122
+ gameObject: hit.gameObject,
123
+ });
124
+ if (stopped) {
125
+ e.stopPropagation();
126
+ }
127
+ }
128
+ canvasPosition(e) {
129
+ const rect = this.canvas.getBoundingClientRect();
130
+ return {
131
+ x: e.clientX - rect.left,
132
+ y: e.clientY - rect.top,
133
+ };
134
+ }
135
+ hitFromEvent(e) {
136
+ if (!this.canvas)
137
+ return null;
138
+ const rect = this.canvas.getBoundingClientRect();
139
+ if (rect.width === 0 || rect.height === 0)
140
+ return null;
141
+ this.ndc.set(((e.clientX - rect.left) / rect.width) * 2 - 1, -((e.clientY - rect.top) / rect.height) * 2 + 1);
142
+ this.raycaster.setFromCamera(this.ndc, this.threeContext.camera);
143
+ const intersections = this.raycaster.intersectObjects(this.threeContext.scene.children, true);
144
+ for (const intersection of intersections) {
145
+ const id = pluginRenderer3d.gameObjectIdFromObject3D(intersection.object);
146
+ if (id == null)
147
+ continue;
148
+ const gameObject = this.game.gameObjects.find((go) => go.id === id);
149
+ if (!gameObject)
150
+ continue;
151
+ const component = gameObject.getComponent(Event3D);
152
+ if (!component)
153
+ continue;
154
+ this.localScratch.copy(intersection.point);
155
+ intersection.object.worldToLocal(this.localScratch);
156
+ return {
157
+ gameObject,
158
+ component,
159
+ local: this.localScratch.clone(),
160
+ };
161
+ }
162
+ return null;
163
+ }
164
+ };
165
+ Event3DSystem = tslib.__decorate([
166
+ engine.decorators.componentObserver({
167
+ Event3D: [],
168
+ })
169
+ ], Event3DSystem);
170
+ var Event3DSystem_default = Event3DSystem;
171
+
172
+ exports.Event3D = Event3D;
173
+ exports.Event3DSystem = Event3DSystem_default;
174
+ //# sourceMappingURL=plugin-renderer-3d-event.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-renderer-3d-event.cjs.js","sources":["../lib/component.ts","../lib/system.ts"],"sourcesContent":["import { Component } from '@combos-fun/engine';\nimport type { GameObject } from '@combos-fun/engine';\n\ntype TouchEventName = 'touchstart' | 'touchmove' | 'touchend' | 'tap' | 'touchendoutside' | 'touchcancel';\n\nexport type Event3DParam = {\n stopPropagation: () => void,\n data: {\n pointerId: number,\n position: {\n x: number,\n y: number,\n },\n localPosition: {\n x: number,\n y: number,\n z: number,\n },\n },\n gameObject: GameObject,\n};\n\nexport interface Event3DParams {}\n\nexport default class Event3D extends Component<Event3DParams> {\n static componentName = 'Event3D';\n\n init(params?: Event3DParams) {\n params && Object.assign(this, params);\n }\n\n emit(eventName: TouchEventName, ...args: [Event3DParam]): boolean\n emit<T extends string>(eventName: Exclude<T, TouchEventName>, ...args: any[]): boolean\n emit(en: string, ...args: any[]) {\n return super.emit(en, ...args);\n }\n\n once(eventName: TouchEventName, fn: (arg: Event3DParam) => void, context?: any): this\n once<T extends string>(eventName: Exclude<T, TouchEventName>, fn: (...args: any[]) => void, context?: any): this\n once(en: string, fn: (...args: any[]) => void, context?: any) {\n return super.once(en, fn, context);\n }\n\n on(eventName: TouchEventName, fn: (arg: Event3DParam) => void, context?: any): this\n on<T extends string>(eventName: Exclude<T, TouchEventName>, fn: (...args: any[]) => void, context?: any): this\n on(en: string, fn: (...args: any[]) => void, context?: any) {\n return super.on(en, fn, context);\n }\n}\n","import { decorators, ComponentChanged, OBSERVER_TYPE, GameObject } from '@combos-fun/engine';\nimport {\n Renderer3D,\n Renderer3DSystem,\n gameObjectIdFromObject3D,\n} from '@combos-fun/plugin-renderer-3d';\nimport { Raycaster, Vector2, Vector3 } from 'three';\nimport Event3D from './component';\n\nexport interface Event3DSystemParams {}\n\ninterface PointerHit {\n gameObject: GameObject;\n component: Event3D;\n local: Vector3;\n}\n\n@decorators.componentObserver({\n Event3D: [],\n})\nexport default class Event3DSystem extends Renderer3D {\n static systemName = 'Event3DSystem';\n name: string = 'Event3DSystem';\n\n private raycaster = new Raycaster();\n private ndc = new Vector2();\n private localScratch = new Vector3();\n private downByPointer = new Map<number, PointerHit>();\n private canvas: HTMLCanvasElement | null = null;\n\n private onPointerDown = (e: PointerEvent) => this.handlePointer(e, 'touchstart');\n private onPointerMove = (e: PointerEvent) => this.handlePointer(e, 'touchmove');\n private onPointerUp = (e: PointerEvent) => this.handlePointer(e, 'touchend');\n private onPointerCancel = (e: PointerEvent) => this.handlePointer(e, 'touchcancel');\n\n init(_params?: Event3DSystemParams) {\n const renderer3DSystem = this.game.getSystem(Renderer3DSystem) as Renderer3DSystem;\n renderer3DSystem.rendererManager.register(this);\n\n this.canvas = this.threeContext.renderer.domElement;\n this.canvas.style.touchAction = 'none';\n this.canvas.addEventListener('pointerdown', this.onPointerDown);\n this.canvas.addEventListener('pointermove', this.onPointerMove);\n this.canvas.addEventListener('pointerup', this.onPointerUp);\n this.canvas.addEventListener('pointercancel', this.onPointerCancel);\n this.canvas.addEventListener('pointerleave', this.onPointerCancel);\n }\n\n componentChanged(changed: ComponentChanged) {\n if (changed.componentName !== 'Event3D') return;\n if (changed.type === OBSERVER_TYPE.REMOVE) {\n changed.component.removeAllListeners();\n for (const [pointerId, hit] of this.downByPointer) {\n if (hit.gameObject.id === changed.gameObject.id) {\n this.downByPointer.delete(pointerId);\n }\n }\n }\n }\n\n rendererUpdate(_gameObject: GameObject) {}\n\n onDestroy() {\n if (!this.canvas) return;\n this.canvas.removeEventListener('pointerdown', this.onPointerDown);\n this.canvas.removeEventListener('pointermove', this.onPointerMove);\n this.canvas.removeEventListener('pointerup', this.onPointerUp);\n this.canvas.removeEventListener('pointercancel', this.onPointerCancel);\n this.canvas.removeEventListener('pointerleave', this.onPointerCancel);\n this.canvas = null;\n this.downByPointer.clear();\n }\n\n private handlePointer(e: PointerEvent, kind: 'touchstart' | 'touchmove' | 'touchend' | 'touchcancel') {\n const hit = this.hitFromEvent(e);\n const canvasPos = this.canvasPosition(e);\n\n if (kind === 'touchstart') {\n if (!hit) return;\n this.downByPointer.set(e.pointerId, hit);\n this.emitOn(hit, 'touchstart', e, canvasPos);\n return;\n }\n\n if (kind === 'touchmove') {\n if (!hit) return;\n this.emitOn(hit, 'touchmove', e, canvasPos);\n return;\n }\n\n const down = this.downByPointer.get(e.pointerId);\n this.downByPointer.delete(e.pointerId);\n\n if (kind === 'touchcancel') {\n if (down) this.emitOn(down, 'touchcancel', e, canvasPos);\n return;\n }\n\n if (hit && down && hit.gameObject.id === down.gameObject.id) {\n this.emitOn(hit, 'touchend', e, canvasPos);\n this.emitOn(hit, 'tap', e, canvasPos);\n return;\n }\n\n if (down) {\n this.emitOn(down, 'touchendoutside', e, canvasPos);\n }\n if (hit) {\n this.emitOn(hit, 'touchend', e, canvasPos);\n }\n }\n\n private emitOn(\n hit: PointerHit,\n eventName: 'touchstart' | 'touchmove' | 'touchend' | 'tap' | 'touchendoutside' | 'touchcancel',\n e: PointerEvent,\n canvasPos: { x: number; y: number },\n ) {\n let stopped = false;\n hit.component.emit(eventName, {\n stopPropagation: () => { stopped = true; },\n data: {\n pointerId: e.pointerId,\n position: canvasPos,\n localPosition: {\n x: hit.local.x,\n y: hit.local.y,\n z: hit.local.z,\n },\n },\n gameObject: hit.gameObject,\n });\n if (stopped) {\n e.stopPropagation();\n }\n }\n\n private canvasPosition(e: PointerEvent) {\n const rect = this.canvas.getBoundingClientRect();\n return {\n x: e.clientX - rect.left,\n y: e.clientY - rect.top,\n };\n }\n\n private hitFromEvent(e: PointerEvent): PointerHit | null {\n if (!this.canvas) return null;\n const rect = this.canvas.getBoundingClientRect();\n if (rect.width === 0 || rect.height === 0) return null;\n\n this.ndc.set(\n ((e.clientX - rect.left) / rect.width) * 2 - 1,\n -((e.clientY - rect.top) / rect.height) * 2 + 1,\n );\n this.raycaster.setFromCamera(this.ndc, this.threeContext.camera);\n const intersections = this.raycaster.intersectObjects(this.threeContext.scene.children, true);\n\n for (const intersection of intersections) {\n const id = gameObjectIdFromObject3D(intersection.object);\n if (id == null) continue;\n const gameObject = this.game.gameObjects.find((go) => go.id === id);\n if (!gameObject) continue;\n const component = gameObject.getComponent(Event3D) as Event3D | undefined;\n if (!component) continue;\n\n this.localScratch.copy(intersection.point);\n intersection.object.worldToLocal(this.localScratch);\n return {\n gameObject,\n component,\n local: this.localScratch.clone(),\n };\n }\n return null;\n }\n}\n"],"names":["Component","Renderer3D","Raycaster","Vector2","Vector3","Renderer3DSystem","OBSERVER_TYPE","gameObjectIdFromObject3D","__decorate","decorators"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ "use strict";var t=require("tslib"),e=require("@combos-fun/engine"),n=require("@combos-fun/plugin-renderer-3d"),i=require("three");class o extends e.Component{static{this.componentName="Event3D"}init(t){t&&Object.assign(this,t)}emit(t,...e){return super.emit(t,...e)}once(t,e,n){return super.once(t,e,n)}on(t,e,n){return super.on(t,e,n)}}let s=class extends n.Renderer3D{constructor(){super(...arguments),this.name="Event3DSystem",this.raycaster=new i.Raycaster,this.ndc=new i.Vector2,this.localScratch=new i.Vector3,this.downByPointer=new Map,this.canvas=null,this.onPointerDown=t=>this.handlePointer(t,"touchstart"),this.onPointerMove=t=>this.handlePointer(t,"touchmove"),this.onPointerUp=t=>this.handlePointer(t,"touchend"),this.onPointerCancel=t=>this.handlePointer(t,"touchcancel")}static{this.systemName="Event3DSystem"}init(t){this.game.getSystem(n.Renderer3DSystem).rendererManager.register(this),this.canvas=this.threeContext.renderer.domElement,this.canvas.style.touchAction="none",this.canvas.addEventListener("pointerdown",this.onPointerDown),this.canvas.addEventListener("pointermove",this.onPointerMove),this.canvas.addEventListener("pointerup",this.onPointerUp),this.canvas.addEventListener("pointercancel",this.onPointerCancel),this.canvas.addEventListener("pointerleave",this.onPointerCancel)}componentChanged(t){if("Event3D"===t.componentName&&t.type===e.OBSERVER_TYPE.REMOVE){t.component.removeAllListeners();for(const[e,n]of this.downByPointer)n.gameObject.id===t.gameObject.id&&this.downByPointer.delete(e)}}rendererUpdate(t){}onDestroy(){this.canvas&&(this.canvas.removeEventListener("pointerdown",this.onPointerDown),this.canvas.removeEventListener("pointermove",this.onPointerMove),this.canvas.removeEventListener("pointerup",this.onPointerUp),this.canvas.removeEventListener("pointercancel",this.onPointerCancel),this.canvas.removeEventListener("pointerleave",this.onPointerCancel),this.canvas=null,this.downByPointer.clear())}handlePointer(t,e){const n=this.hitFromEvent(t),i=this.canvasPosition(t);if("touchstart"===e){if(!n)return;return this.downByPointer.set(t.pointerId,n),void this.emitOn(n,"touchstart",t,i)}if("touchmove"===e){if(!n)return;return void this.emitOn(n,"touchmove",t,i)}const o=this.downByPointer.get(t.pointerId);if(this.downByPointer.delete(t.pointerId),"touchcancel"!==e){if(n&&o&&n.gameObject.id===o.gameObject.id)return this.emitOn(n,"touchend",t,i),void this.emitOn(n,"tap",t,i);o&&this.emitOn(o,"touchendoutside",t,i),n&&this.emitOn(n,"touchend",t,i)}else o&&this.emitOn(o,"touchcancel",t,i)}emitOn(t,e,n,i){let o=!1;t.component.emit(e,{stopPropagation:()=>{o=!0},data:{pointerId:n.pointerId,position:i,localPosition:{x:t.local.x,y:t.local.y,z:t.local.z}},gameObject:t.gameObject}),o&&n.stopPropagation()}canvasPosition(t){const e=this.canvas.getBoundingClientRect();return{x:t.clientX-e.left,y:t.clientY-e.top}}hitFromEvent(t){if(!this.canvas)return null;const e=this.canvas.getBoundingClientRect();if(0===e.width||0===e.height)return null;this.ndc.set((t.clientX-e.left)/e.width*2-1,-(t.clientY-e.top)/e.height*2+1),this.raycaster.setFromCamera(this.ndc,this.threeContext.camera);const i=this.raycaster.intersectObjects(this.threeContext.scene.children,!0);for(const t of i){const e=n.gameObjectIdFromObject3D(t.object);if(null==e)continue;const i=this.game.gameObjects.find(t=>t.id===e);if(!i)continue;const s=i.getComponent(o);if(s)return this.localScratch.copy(t.point),t.object.worldToLocal(this.localScratch),{gameObject:i,component:s,local:this.localScratch.clone()}}return null}};s=t.__decorate([e.decorators.componentObserver({Event3D:[]})],s);var r=s;exports.Event3D=o,exports.Event3DSystem=r;
@@ -0,0 +1,59 @@
1
+ import { ComponentChanged, GameObject, Component } from '@combos-fun/engine';
2
+ import { Renderer3D } from '@combos-fun/plugin-renderer-3d';
3
+
4
+ interface Event3DSystemParams {
5
+ }
6
+ declare class Event3DSystem extends Renderer3D {
7
+ static systemName: string;
8
+ name: string;
9
+ private raycaster;
10
+ private ndc;
11
+ private localScratch;
12
+ private downByPointer;
13
+ private canvas;
14
+ private onPointerDown;
15
+ private onPointerMove;
16
+ private onPointerUp;
17
+ private onPointerCancel;
18
+ init(_params?: Event3DSystemParams): void;
19
+ componentChanged(changed: ComponentChanged): void;
20
+ rendererUpdate(_gameObject: GameObject): void;
21
+ onDestroy(): void;
22
+ private handlePointer;
23
+ private emitOn;
24
+ private canvasPosition;
25
+ private hitFromEvent;
26
+ }
27
+
28
+ type TouchEventName = 'touchstart' | 'touchmove' | 'touchend' | 'tap' | 'touchendoutside' | 'touchcancel';
29
+ type Event3DParam = {
30
+ stopPropagation: () => void;
31
+ data: {
32
+ pointerId: number;
33
+ position: {
34
+ x: number;
35
+ y: number;
36
+ };
37
+ localPosition: {
38
+ x: number;
39
+ y: number;
40
+ z: number;
41
+ };
42
+ };
43
+ gameObject: GameObject;
44
+ };
45
+ interface Event3DParams {
46
+ }
47
+ declare class Event3D extends Component<Event3DParams> {
48
+ static componentName: string;
49
+ init(params?: Event3DParams): void;
50
+ emit(eventName: TouchEventName, ...args: [Event3DParam]): boolean;
51
+ emit<T extends string>(eventName: Exclude<T, TouchEventName>, ...args: any[]): boolean;
52
+ once(eventName: TouchEventName, fn: (arg: Event3DParam) => void, context?: any): this;
53
+ once<T extends string>(eventName: Exclude<T, TouchEventName>, fn: (...args: any[]) => void, context?: any): this;
54
+ on(eventName: TouchEventName, fn: (arg: Event3DParam) => void, context?: any): this;
55
+ on<T extends string>(eventName: Exclude<T, TouchEventName>, fn: (...args: any[]) => void, context?: any): this;
56
+ }
57
+
58
+ export { Event3D, Event3DSystem };
59
+ export type { Event3DParam, Event3DParams, Event3DSystemParams };
@@ -0,0 +1,171 @@
1
+ import { __decorate } from 'tslib';
2
+ import { Component, OBSERVER_TYPE, decorators } from '@combos-fun/engine';
3
+ import { Renderer3D, Renderer3DSystem, gameObjectIdFromObject3D } from '@combos-fun/plugin-renderer-3d';
4
+ import { Raycaster, Vector2, Vector3 } from 'three';
5
+
6
+ class Event3D extends Component {
7
+ static { this.componentName = 'Event3D'; }
8
+ init(params) {
9
+ params && Object.assign(this, params);
10
+ }
11
+ emit(en, ...args) {
12
+ return super.emit(en, ...args);
13
+ }
14
+ once(en, fn, context) {
15
+ return super.once(en, fn, context);
16
+ }
17
+ on(en, fn, context) {
18
+ return super.on(en, fn, context);
19
+ }
20
+ }
21
+
22
+ let Event3DSystem = class Event3DSystem extends Renderer3D {
23
+ constructor() {
24
+ super(...arguments);
25
+ this.name = 'Event3DSystem';
26
+ this.raycaster = new Raycaster();
27
+ this.ndc = new Vector2();
28
+ this.localScratch = new Vector3();
29
+ this.downByPointer = new Map();
30
+ this.canvas = null;
31
+ this.onPointerDown = (e) => this.handlePointer(e, 'touchstart');
32
+ this.onPointerMove = (e) => this.handlePointer(e, 'touchmove');
33
+ this.onPointerUp = (e) => this.handlePointer(e, 'touchend');
34
+ this.onPointerCancel = (e) => this.handlePointer(e, 'touchcancel');
35
+ }
36
+ static { this.systemName = 'Event3DSystem'; }
37
+ init(_params) {
38
+ const renderer3DSystem = this.game.getSystem(Renderer3DSystem);
39
+ renderer3DSystem.rendererManager.register(this);
40
+ this.canvas = this.threeContext.renderer.domElement;
41
+ this.canvas.style.touchAction = 'none';
42
+ this.canvas.addEventListener('pointerdown', this.onPointerDown);
43
+ this.canvas.addEventListener('pointermove', this.onPointerMove);
44
+ this.canvas.addEventListener('pointerup', this.onPointerUp);
45
+ this.canvas.addEventListener('pointercancel', this.onPointerCancel);
46
+ this.canvas.addEventListener('pointerleave', this.onPointerCancel);
47
+ }
48
+ componentChanged(changed) {
49
+ if (changed.componentName !== 'Event3D')
50
+ return;
51
+ if (changed.type === OBSERVER_TYPE.REMOVE) {
52
+ changed.component.removeAllListeners();
53
+ for (const [pointerId, hit] of this.downByPointer) {
54
+ if (hit.gameObject.id === changed.gameObject.id) {
55
+ this.downByPointer.delete(pointerId);
56
+ }
57
+ }
58
+ }
59
+ }
60
+ rendererUpdate(_gameObject) { }
61
+ onDestroy() {
62
+ if (!this.canvas)
63
+ return;
64
+ this.canvas.removeEventListener('pointerdown', this.onPointerDown);
65
+ this.canvas.removeEventListener('pointermove', this.onPointerMove);
66
+ this.canvas.removeEventListener('pointerup', this.onPointerUp);
67
+ this.canvas.removeEventListener('pointercancel', this.onPointerCancel);
68
+ this.canvas.removeEventListener('pointerleave', this.onPointerCancel);
69
+ this.canvas = null;
70
+ this.downByPointer.clear();
71
+ }
72
+ handlePointer(e, kind) {
73
+ const hit = this.hitFromEvent(e);
74
+ const canvasPos = this.canvasPosition(e);
75
+ if (kind === 'touchstart') {
76
+ if (!hit)
77
+ return;
78
+ this.downByPointer.set(e.pointerId, hit);
79
+ this.emitOn(hit, 'touchstart', e, canvasPos);
80
+ return;
81
+ }
82
+ if (kind === 'touchmove') {
83
+ if (!hit)
84
+ return;
85
+ this.emitOn(hit, 'touchmove', e, canvasPos);
86
+ return;
87
+ }
88
+ const down = this.downByPointer.get(e.pointerId);
89
+ this.downByPointer.delete(e.pointerId);
90
+ if (kind === 'touchcancel') {
91
+ if (down)
92
+ this.emitOn(down, 'touchcancel', e, canvasPos);
93
+ return;
94
+ }
95
+ if (hit && down && hit.gameObject.id === down.gameObject.id) {
96
+ this.emitOn(hit, 'touchend', e, canvasPos);
97
+ this.emitOn(hit, 'tap', e, canvasPos);
98
+ return;
99
+ }
100
+ if (down) {
101
+ this.emitOn(down, 'touchendoutside', e, canvasPos);
102
+ }
103
+ if (hit) {
104
+ this.emitOn(hit, 'touchend', e, canvasPos);
105
+ }
106
+ }
107
+ emitOn(hit, eventName, e, canvasPos) {
108
+ let stopped = false;
109
+ hit.component.emit(eventName, {
110
+ stopPropagation: () => { stopped = true; },
111
+ data: {
112
+ pointerId: e.pointerId,
113
+ position: canvasPos,
114
+ localPosition: {
115
+ x: hit.local.x,
116
+ y: hit.local.y,
117
+ z: hit.local.z,
118
+ },
119
+ },
120
+ gameObject: hit.gameObject,
121
+ });
122
+ if (stopped) {
123
+ e.stopPropagation();
124
+ }
125
+ }
126
+ canvasPosition(e) {
127
+ const rect = this.canvas.getBoundingClientRect();
128
+ return {
129
+ x: e.clientX - rect.left,
130
+ y: e.clientY - rect.top,
131
+ };
132
+ }
133
+ hitFromEvent(e) {
134
+ if (!this.canvas)
135
+ return null;
136
+ const rect = this.canvas.getBoundingClientRect();
137
+ if (rect.width === 0 || rect.height === 0)
138
+ return null;
139
+ this.ndc.set(((e.clientX - rect.left) / rect.width) * 2 - 1, -((e.clientY - rect.top) / rect.height) * 2 + 1);
140
+ this.raycaster.setFromCamera(this.ndc, this.threeContext.camera);
141
+ const intersections = this.raycaster.intersectObjects(this.threeContext.scene.children, true);
142
+ for (const intersection of intersections) {
143
+ const id = gameObjectIdFromObject3D(intersection.object);
144
+ if (id == null)
145
+ continue;
146
+ const gameObject = this.game.gameObjects.find((go) => go.id === id);
147
+ if (!gameObject)
148
+ continue;
149
+ const component = gameObject.getComponent(Event3D);
150
+ if (!component)
151
+ continue;
152
+ this.localScratch.copy(intersection.point);
153
+ intersection.object.worldToLocal(this.localScratch);
154
+ return {
155
+ gameObject,
156
+ component,
157
+ local: this.localScratch.clone(),
158
+ };
159
+ }
160
+ return null;
161
+ }
162
+ };
163
+ Event3DSystem = __decorate([
164
+ decorators.componentObserver({
165
+ Event3D: [],
166
+ })
167
+ ], Event3DSystem);
168
+ var Event3DSystem_default = Event3DSystem;
169
+
170
+ export { Event3D, Event3DSystem_default as Event3DSystem };
171
+ //# sourceMappingURL=plugin-renderer-3d-event.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-renderer-3d-event.esm.js","sources":["../lib/component.ts","../lib/system.ts"],"sourcesContent":["import { Component } from '@combos-fun/engine';\nimport type { GameObject } from '@combos-fun/engine';\n\ntype TouchEventName = 'touchstart' | 'touchmove' | 'touchend' | 'tap' | 'touchendoutside' | 'touchcancel';\n\nexport type Event3DParam = {\n stopPropagation: () => void,\n data: {\n pointerId: number,\n position: {\n x: number,\n y: number,\n },\n localPosition: {\n x: number,\n y: number,\n z: number,\n },\n },\n gameObject: GameObject,\n};\n\nexport interface Event3DParams {}\n\nexport default class Event3D extends Component<Event3DParams> {\n static componentName = 'Event3D';\n\n init(params?: Event3DParams) {\n params && Object.assign(this, params);\n }\n\n emit(eventName: TouchEventName, ...args: [Event3DParam]): boolean\n emit<T extends string>(eventName: Exclude<T, TouchEventName>, ...args: any[]): boolean\n emit(en: string, ...args: any[]) {\n return super.emit(en, ...args);\n }\n\n once(eventName: TouchEventName, fn: (arg: Event3DParam) => void, context?: any): this\n once<T extends string>(eventName: Exclude<T, TouchEventName>, fn: (...args: any[]) => void, context?: any): this\n once(en: string, fn: (...args: any[]) => void, context?: any) {\n return super.once(en, fn, context);\n }\n\n on(eventName: TouchEventName, fn: (arg: Event3DParam) => void, context?: any): this\n on<T extends string>(eventName: Exclude<T, TouchEventName>, fn: (...args: any[]) => void, context?: any): this\n on(en: string, fn: (...args: any[]) => void, context?: any) {\n return super.on(en, fn, context);\n }\n}\n","import { decorators, ComponentChanged, OBSERVER_TYPE, GameObject } from '@combos-fun/engine';\nimport {\n Renderer3D,\n Renderer3DSystem,\n gameObjectIdFromObject3D,\n} from '@combos-fun/plugin-renderer-3d';\nimport { Raycaster, Vector2, Vector3 } from 'three';\nimport Event3D from './component';\n\nexport interface Event3DSystemParams {}\n\ninterface PointerHit {\n gameObject: GameObject;\n component: Event3D;\n local: Vector3;\n}\n\n@decorators.componentObserver({\n Event3D: [],\n})\nexport default class Event3DSystem extends Renderer3D {\n static systemName = 'Event3DSystem';\n name: string = 'Event3DSystem';\n\n private raycaster = new Raycaster();\n private ndc = new Vector2();\n private localScratch = new Vector3();\n private downByPointer = new Map<number, PointerHit>();\n private canvas: HTMLCanvasElement | null = null;\n\n private onPointerDown = (e: PointerEvent) => this.handlePointer(e, 'touchstart');\n private onPointerMove = (e: PointerEvent) => this.handlePointer(e, 'touchmove');\n private onPointerUp = (e: PointerEvent) => this.handlePointer(e, 'touchend');\n private onPointerCancel = (e: PointerEvent) => this.handlePointer(e, 'touchcancel');\n\n init(_params?: Event3DSystemParams) {\n const renderer3DSystem = this.game.getSystem(Renderer3DSystem) as Renderer3DSystem;\n renderer3DSystem.rendererManager.register(this);\n\n this.canvas = this.threeContext.renderer.domElement;\n this.canvas.style.touchAction = 'none';\n this.canvas.addEventListener('pointerdown', this.onPointerDown);\n this.canvas.addEventListener('pointermove', this.onPointerMove);\n this.canvas.addEventListener('pointerup', this.onPointerUp);\n this.canvas.addEventListener('pointercancel', this.onPointerCancel);\n this.canvas.addEventListener('pointerleave', this.onPointerCancel);\n }\n\n componentChanged(changed: ComponentChanged) {\n if (changed.componentName !== 'Event3D') return;\n if (changed.type === OBSERVER_TYPE.REMOVE) {\n changed.component.removeAllListeners();\n for (const [pointerId, hit] of this.downByPointer) {\n if (hit.gameObject.id === changed.gameObject.id) {\n this.downByPointer.delete(pointerId);\n }\n }\n }\n }\n\n rendererUpdate(_gameObject: GameObject) {}\n\n onDestroy() {\n if (!this.canvas) return;\n this.canvas.removeEventListener('pointerdown', this.onPointerDown);\n this.canvas.removeEventListener('pointermove', this.onPointerMove);\n this.canvas.removeEventListener('pointerup', this.onPointerUp);\n this.canvas.removeEventListener('pointercancel', this.onPointerCancel);\n this.canvas.removeEventListener('pointerleave', this.onPointerCancel);\n this.canvas = null;\n this.downByPointer.clear();\n }\n\n private handlePointer(e: PointerEvent, kind: 'touchstart' | 'touchmove' | 'touchend' | 'touchcancel') {\n const hit = this.hitFromEvent(e);\n const canvasPos = this.canvasPosition(e);\n\n if (kind === 'touchstart') {\n if (!hit) return;\n this.downByPointer.set(e.pointerId, hit);\n this.emitOn(hit, 'touchstart', e, canvasPos);\n return;\n }\n\n if (kind === 'touchmove') {\n if (!hit) return;\n this.emitOn(hit, 'touchmove', e, canvasPos);\n return;\n }\n\n const down = this.downByPointer.get(e.pointerId);\n this.downByPointer.delete(e.pointerId);\n\n if (kind === 'touchcancel') {\n if (down) this.emitOn(down, 'touchcancel', e, canvasPos);\n return;\n }\n\n if (hit && down && hit.gameObject.id === down.gameObject.id) {\n this.emitOn(hit, 'touchend', e, canvasPos);\n this.emitOn(hit, 'tap', e, canvasPos);\n return;\n }\n\n if (down) {\n this.emitOn(down, 'touchendoutside', e, canvasPos);\n }\n if (hit) {\n this.emitOn(hit, 'touchend', e, canvasPos);\n }\n }\n\n private emitOn(\n hit: PointerHit,\n eventName: 'touchstart' | 'touchmove' | 'touchend' | 'tap' | 'touchendoutside' | 'touchcancel',\n e: PointerEvent,\n canvasPos: { x: number; y: number },\n ) {\n let stopped = false;\n hit.component.emit(eventName, {\n stopPropagation: () => { stopped = true; },\n data: {\n pointerId: e.pointerId,\n position: canvasPos,\n localPosition: {\n x: hit.local.x,\n y: hit.local.y,\n z: hit.local.z,\n },\n },\n gameObject: hit.gameObject,\n });\n if (stopped) {\n e.stopPropagation();\n }\n }\n\n private canvasPosition(e: PointerEvent) {\n const rect = this.canvas.getBoundingClientRect();\n return {\n x: e.clientX - rect.left,\n y: e.clientY - rect.top,\n };\n }\n\n private hitFromEvent(e: PointerEvent): PointerHit | null {\n if (!this.canvas) return null;\n const rect = this.canvas.getBoundingClientRect();\n if (rect.width === 0 || rect.height === 0) return null;\n\n this.ndc.set(\n ((e.clientX - rect.left) / rect.width) * 2 - 1,\n -((e.clientY - rect.top) / rect.height) * 2 + 1,\n );\n this.raycaster.setFromCamera(this.ndc, this.threeContext.camera);\n const intersections = this.raycaster.intersectObjects(this.threeContext.scene.children, true);\n\n for (const intersection of intersections) {\n const id = gameObjectIdFromObject3D(intersection.object);\n if (id == null) continue;\n const gameObject = this.game.gameObjects.find((go) => go.id === id);\n if (!gameObject) continue;\n const component = gameObject.getComponent(Event3D) as Event3D | undefined;\n if (!component) continue;\n\n this.localScratch.copy(intersection.point);\n intersection.object.worldToLocal(this.localScratch);\n return {\n gameObject,\n component,\n local: this.localScratch.clone(),\n };\n }\n return null;\n }\n}\n"],"names":[],"mappings":"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"}
package/index.js ADDED
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ if (process.env.NODE_ENV === 'production') {
4
+ module.exports = require('./dist/plugin-renderer-3d-event.cjs.prod.js');
5
+ } else {
6
+ module.exports = require('./dist/plugin-renderer-3d-event.cjs.js');
7
+ }
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@combos-fun/plugin-renderer-3d-event",
3
+ "version": "0.0.40",
4
+ "description": "Pointer / touch event input via Three.js raycasting",
5
+ "main": "index.js",
6
+ "module": "dist/plugin-renderer-3d-event.esm.js",
7
+ "bundle": "CombosFun.plugin.renderer.3d.event",
8
+ "unpkg": "dist/CombosFun.plugin.renderer.3d.event.min.js",
9
+ "types": "dist/plugin-renderer-3d-event.d.ts",
10
+ "files": [
11
+ "index.js",
12
+ "dist",
13
+ "agent-skill.md",
14
+ "combos-plugin.json"
15
+ ],
16
+ "exports": {
17
+ ".": {
18
+ "import": "./dist/plugin-renderer-3d-event.esm.js",
19
+ "require": "./index.js",
20
+ "types": "./dist/plugin-renderer-3d-event.d.ts"
21
+ },
22
+ "./plugin-manifest": "./combos-plugin.json",
23
+ "./agent-skill": "./agent-skill.md"
24
+ },
25
+ "combos": {
26
+ "pluginManifest": "./combos-plugin.json"
27
+ },
28
+ "dependencies": {
29
+ "@combos-fun/engine": "0.0.39",
30
+ "@combos-fun/plugin-renderer-3d": "0.0.39",
31
+ "three": "^0.172.0"
32
+ },
33
+ "keywords": [
34
+ "combos-fun",
35
+ "3d",
36
+ "pointer",
37
+ "touch",
38
+ "tap",
39
+ "raycast",
40
+ "input",
41
+ "event"
42
+ ],
43
+ "scripts": {
44
+ "build": "node ../../scripts/build-package.mjs"
45
+ }
46
+ }