diff --git a/apps/client/src/input/Joystick.ts b/apps/client/src/input/Joystick.ts new file mode 100644 index 0000000..da21610 --- /dev/null +++ b/apps/client/src/input/Joystick.ts @@ -0,0 +1,75 @@ +import { Container, Graphics, Point } from 'pixi.js'; + +export class Joystick extends Container { + private outer: Graphics; + private inner: Graphics; + private pointerId: number | null = null; + private startPos: Point = new Point(); + private currentPos: Point = new Point(); + + // 入力ベクトル (x, y: -1.0 ~ 1.0) + public input: Point = new Point(0, 0); + + constructor() { + super(); + + // 外枠(グレーの丸) + this.outer = new Graphics().circle(0, 0, 60).fill({ color: 0xffffff, alpha: 0.3 }); + // 内側(白い丸) + this.inner = new Graphics().circle(0, 0, 30).fill({ color: 0xffffff, alpha: 0.8 }); + + this.addChild(this.outer); + this.addChild(this.inner); + + // 最初は非表示 + this.visible = false; + + // イベントリスナー設定 + window.addEventListener('pointerdown', this.onDown.bind(this)); + window.addEventListener('pointermove', this.onMove.bind(this)); + window.addEventListener('pointerup', this.onUp.bind(this)); + } + + private onDown(e: PointerEvent) { + // 画面の左半分だけ反応させるなどの制御もここで可能 + this.pointerId = e.pointerId; + this.startPos.set(e.clientX, e.clientY); + this.currentPos.copyFrom(this.startPos); + + // ジョイスティックを表示 + this.position.copyFrom(this.startPos); + this.inner.position.set(0, 0); + this.visible = true; + this.input.set(0, 0); + } + + private onMove(e: PointerEvent) { + if (this.pointerId !== e.pointerId) return; + + const maxDist = 60; //スティックが動ける最大半径 + const dx = e.clientX - this.startPos.x; + const dy = e.clientY - this.startPos.y; + const dist = Math.sqrt(dx * dx + dy * dy); + + // 入力ベクトルの計算 + if (dist > 0) { + const scale = Math.min(dist, maxDist); + const angle = Math.atan2(dy, dx); + + // 内側の丸を移動 + this.inner.x = Math.cos(angle) * scale; + this.inner.y = Math.sin(angle) * scale; + + // 正規化された入力値 (-1.0 ~ 1.0) + this.input.x = this.inner.x / maxDist; + this.input.y = this.inner.y / maxDist; + } + } + + private onUp(e: PointerEvent) { + if (this.pointerId !== e.pointerId) return; + this.pointerId = null; + this.input.set(0, 0); + this.visible = false; + } +} \ No newline at end of file