diff --git a/app/page.tsx b/app/page.tsx index d651878..aed4836 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -56,19 +56,19 @@ export default function RobotDashboard() { const trajectoryRef = useRef([]); const prevBtnsRef = useRef({ A: false, B: false, Y: false, X: false }); const wsRef = useRef(null); - const keysRef = useRef({ up: false, down: false, left: false, right: false }); + + const keysRef = useRef({ up: false, down: false, left: false, right: false, q: false, e: false }); + const touchStateRef = useRef({ turn: 0, throttle: 0, pan: 0 }); - const touchStateRef = useRef({ turn: 0, throttle: 0 }); - - const sendCommand = (x: number, y: number) => { + const sendCommand = (x: number, y: number, pan: number = 0) => { if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) { - wsRef.current.send(JSON.stringify({ x, y })); + wsRef.current.send(JSON.stringify({ x, y, pan })); } }; const stopRobot = () => { setSteering({ x: 0, y: 0 }); - sendCommand(0, 0); + sendCommand(0, 0, touchStateRef.current.pan); }; const getMotionType = (x: number, y: number): MotionType => { @@ -126,7 +126,7 @@ export default function RobotDashboard() { if (currentMotion !== 'STOP') lastActiveMotion = currentMotion; setSteering({ x: targetX, y: targetY }); - sendCommand(targetX, targetY); + sendCommand(targetX, targetY, touchStateRef.current.pan); const startL = currentTicksRef.current.l; const startR = currentTicksRef.current.r; @@ -246,7 +246,6 @@ export default function RobotDashboard() { } }; - // Full-Screen const enterFullscreen = () => { const elem = document.documentElement; if (elem.requestFullscreen) { @@ -279,23 +278,39 @@ export default function RobotDashboard() { }; wsRef.current = ws; + // PERBAIKAN LOGIKA KEYBOARD (Q dan E ditambahkan & CapsLock kebal) const handleKeyDown = (e: KeyboardEvent) => { - if (['ArrowUp', 'w', 'W'].includes(e.key)) keysRef.current.up = true; - if (['ArrowDown', 's', 'S'].includes(e.key)) keysRef.current.down = true; - if (['ArrowLeft', 'a', 'A'].includes(e.key)) keysRef.current.left = true; - if (['ArrowRight', 'd', 'D'].includes(e.key)) keysRef.current.right = true; + const key = e.key.toLowerCase(); + if (key === 'arrowup' || key === 'w') keysRef.current.up = true; + if (key === 'arrowdown' || key === 's') keysRef.current.down = true; + if (key === 'arrowleft' || key === 'a') keysRef.current.left = true; + if (key === 'arrowright' || key === 'd') keysRef.current.right = true; + if (key === 'q') keysRef.current.q = true; + if (key === 'e') keysRef.current.e = true; }; + const handleKeyUp = (e: KeyboardEvent) => { - if (['ArrowUp', 'w', 'W'].includes(e.key)) keysRef.current.up = false; - if (['ArrowDown', 's', 'S'].includes(e.key)) keysRef.current.down = false; - if (['ArrowLeft', 'a', 'A'].includes(e.key)) keysRef.current.left = false; - if (['ArrowRight', 'd', 'D'].includes(e.key)) keysRef.current.right = false; + const key = e.key.toLowerCase(); + if (key === 'arrowup' || key === 'w') keysRef.current.up = false; + if (key === 'arrowdown' || key === 's') keysRef.current.down = false; + if (key === 'arrowleft' || key === 'a') keysRef.current.left = false; + if (key === 'arrowright' || key === 'd') keysRef.current.right = false; + + // Saat tombol dilepas, kembalikan posisi pan (menoleh) ke 0 secara paksa + if (key === 'q') { + keysRef.current.q = false; + touchStateRef.current.pan = 0; + } + if (key === 'e') { + keysRef.current.e = false; + touchStateRef.current.pan = 0; + } }; window.addEventListener('keydown', handleKeyDown); window.addEventListener('keyup', handleKeyUp); const interval = setInterval(() => { - let currentX = 0; let currentY = 0; + let currentX = 0; let currentY = 0; let currentPan = 0; let btnA = false; let btnB = false; let btnY = false; let btnX = false; if (controlMode === 'gamepad') { @@ -306,11 +321,15 @@ export default function RobotDashboard() { if (gamepads[i]) { activeGp = gamepads[i]; break; } } if (activeGp) { - const rawX = activeGp.axes[0] || 0; + const rawX = activeGp.axes[0] || 0; + const rawPan = activeGp.axes[2] || 0; const maju = activeGp.buttons[7]?.value || 0; const mundur = activeGp.buttons[6]?.value || 0; + currentX = Math.abs(rawX) > 0.15 ? Number(rawX.toFixed(2)) : 0; currentY = Number((mundur - maju).toFixed(2)); + currentPan = Math.abs(rawPan) > 0.15 ? Number(rawPan.toFixed(2)) : 0; + btnA = activeGp.buttons[0]?.pressed || false; btnB = activeGp.buttons[1]?.pressed || false; btnY = activeGp.buttons[3]?.pressed || false; @@ -320,9 +339,21 @@ export default function RobotDashboard() { } else if (controlMode === 'keyboard') { currentY = (keysRef.current.down ? 1 : 0) - (keysRef.current.up ? 1 : 0); currentX = (keysRef.current.right ? 1 : 0) - (keysRef.current.left ? 1 : 0); + currentPan = (keysRef.current.e ? 1 : 0) - (keysRef.current.q ? 1 : 0); } else if (controlMode === 'touch' || isFullscreen) { currentX = touchStateRef.current.turn; currentY = touchStateRef.current.throttle; + currentPan = touchStateRef.current.pan; + } + + // GLOBAL KEYBOARD OVERRIDE: Berlaku dimanapun asalkan tombol keyboard ditekan + if (keysRef.current.up || keysRef.current.down || keysRef.current.left || keysRef.current.right) { + currentY = (keysRef.current.down ? 1 : 0) - (keysRef.current.up ? 1 : 0); + currentX = (keysRef.current.right ? 1 : 0) - (keysRef.current.left ? 1 : 0); + } + if (keysRef.current.q || keysRef.current.e) { + currentPan = (keysRef.current.e ? 1 : 0) - (keysRef.current.q ? 1 : 0); + touchStateRef.current.pan = currentPan; // Sinkronkan ke UI Sentuh } const currentMotion = getMotionType(currentX, currentY); @@ -382,7 +413,7 @@ export default function RobotDashboard() { } setSteering({ x: currentX, y: currentY }); - sendCommand(currentX, currentY); + sendCommand(currentX, currentY, currentPan); }, RECORD_INTERVAL_MS); return () => { @@ -433,7 +464,7 @@ export default function RobotDashboard() {
- {/* (KAMERA & TOUCH CONTROLLER) */} + {/* (KAMERA & TOUCH CONTROLLER INLINE) */}
@@ -450,75 +481,101 @@ export default function RobotDashboard() { Full Screen
+
- Robot view { e.currentTarget.style.display = 'none'; }} />
- {/* DUAL-PAD */} + {/* TOUCH UI BARU SESUAI DESAIN */} {controlMode === 'touch' && ( -
+
- {/* SISI KIRI: BELOK */} -
-
Steering
-
- - + + +
+ + {/* Slider Kamera di Bawah Tombol Aksi */} +
+ Q + { touchStateRef.current.pan = Number(e.currentTarget.value); }} + onPointerUp={(e) => { + e.currentTarget.value = "0"; + touchStateRef.current.pan = 0; + }} + onPointerLeave={(e) => { + e.currentTarget.value = "0"; + touchStateRef.current.pan = 0; + }} + className="w-full accent-red-500" + /> + E
- {/* SISI KANAN: THROTTLE */} -
-
Throttle
-
- + + {/* KIRI DAN KANAN */} +
+ -
+ + {/* MUNDUR */} +
-
)}
- {/* KOLOM KANAN (STATUS & AKSI RUTE) */} + {/* KOLOM KANAN (STATUS) */}

@@ -528,7 +585,7 @@ export default function RobotDashboard() {
- {controlMode === 'touch' ? '📱 AKTIF: DUAL-PAD SENTUH' : controlMode === 'keyboard' ? '⌨️ AKTIF: W-A-S-D' : gamepadStatus} + {controlMode === 'touch' ? '📱 AKTIF: DUAL-PAD SENTUH' : controlMode === 'keyboard' ? '⌨️ AKTIF: W-A-S-D & Q-E' : gamepadStatus}
@@ -550,47 +607,22 @@ export default function RobotDashboard() {

- - {/* TOMBOL AKSI RUTE */} + + {/* Tombol aksi cadangan untuk UI Desktop */}
-
Memori Rute
-
- - - - +
Panel Cadangan Aksi
+
+ + + +
-
- {/* OVERLAY LAYAR PENUH (FULL SCREEN) */} + {/* OVERLAY LAYAR PENUH (FULL SCREEN) LAYOUT BARU */} {isFullscreen && (
@@ -609,65 +641,86 @@ export default function RobotDashboard() { {/* AREA KONTROL OVERLAY */} -
+
- {/* KIRI: Tombol Kiri & Kanan*/} -
- - + {/* SISI KIRI: Aksi dan Slider Kamera */} +
+ {/* 4 Tombol Aksi Berjejer */} +
+ + + + +
+ + {/* Slider Kamera di Bawah Tombol Aksi */} +
+ Q + { touchStateRef.current.pan = Number(e.currentTarget.value); }} + onPointerUp={(e) => { + e.currentTarget.value = "0"; + touchStateRef.current.pan = 0; + }} + onPointerLeave={(e) => { + e.currentTarget.value = "0"; + touchStateRef.current.pan = 0; + }} + className="w-full accent-red-500" + /> + E +
- {/* TENGAH: 4 Tombol Aksi (D-Pad Style) */} -
- {/*(Rekam) */} - - {/*(Simpan) */} - - {/*(Play Rev) */} - - {/*(Play Fwd) */} - -
- - {/* KANAN: Tombol Atas & Bawah */} -
+ {/* SISI KANAN: D-Pad Pergerakan Salib */} +
+ {/* MAJU */} + + {/* KIRI DAN KANAN */} +
+ + +
+ + {/* MUNDUR */}