From 73130dd8cebc41f01c3251ae2692ad4599c0357d Mon Sep 17 00:00:00 2001 From: Tomas Horsky Date: Wed, 29 Apr 2026 23:09:07 +0900 Subject: [PATCH] optimazed camera controll again Co-authored-by: Copilot --- public/favicon.png | Bin 3127 -> 0 bytes public/index.html | 2 +- src/game/cameraControl.js | 102 +++++++++++++++++++------------------- src/game/constants.js | 2 +- 4 files changed, 52 insertions(+), 54 deletions(-) delete mode 100644 public/favicon.png diff --git a/public/favicon.png b/public/favicon.png deleted file mode 100644 index 7e6f5eb5a2f1f1c882d265cf479de25caa925645..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3127 zcmV-749N3|P)i z7)}s4L53SJCkR}iVi00SFk;`MXX*#X*kkwKs@nFGS}c;=?XFjU|G$3t^5sjIVS2G+ zw)WGF83CpoGXhLGW(1gW%uV|X7>1P6VhCX=Ux)Lb!*DZ%@I3!{Gsf7d?gtIQ%nQiK z3%(LUSkBji;C5Rfgd6$VsF@H`Pk@xtY6t<>FNR-pD}=C~$?)9pdm3XZ36N5PNWYjb z$xd$yNQR9N!dfj-Vd@BwQo^FIIWPPmT&sZyQ$v81(sCBV=PGy{0wltEjB%~h157*t zvbe_!{=I_783x!0t1-r#-d{Y?ae$Q4N_Nd^Ui^@y(%)Gjou6y<3^XJdu{rmUf-Me?)zZ>9OR&6U5H*cK; z$gUlB{g0O4gN0sLSO|Of?hU(l?;h(jA3uH!Z{EBKuV23ouU@^Y6#%v+QG;>e*E}%?wlu-NT4DG zs)z)7WbLr)vGAu(ohrKc^em@OpO&f~6_>E61n_e0_V3@{U3^O;j{`^mNCJUj_>;7v zsMs6Hu3g7+@v+lSo;=yTYFqq}jZmQ-BK8K{C4kqi_i*jBaQE(Au0607V-zKeT;EPg zX(`vrn=L+e74+-Tqeok@_`tDa$G9I|$nTU5H*2V8@y()n*zqM?J1G!-1aX;CfDC9B zTnJ#j_%*n8Qb1)re*Bno7g0RG{Eb;IK14irJYJp$5Z6ac9~b_P?+5t~95~SRG$g?1 znFJ7p$xV&GZ18m~79TGRdfsc-BcX$9yXTR*n)mPD@1~O(_?cT$ZvFPucRmGlq&se0 zKrcUf^k}4hM*biEJOWKzz!qQe;CB_ZtSOO9Owg#lZAc=s65^rb{fZe(TYu_rk!wKkEf}RIt=#Om( zR8mN`DM<^xj~59euMMspBolVN zAPTr8sSDI104orIAdmL$uOXn*6hga1G+0WD0E?UtabxC#VC~vf3|10|phW;yQ3CY8 z2CM=)ErF;xq-YJ5G|um}>*1#E+O_Mu|Nr#qQ&G1P-NMq@f?@*XUcSbV?tX=)ilM-Q zBZP|!Bpv0V;#ojKcpc7$=eqO;#Uy~#?^kNI{vSZfLx&DEt~LTmaKWXcx=joubklI<*Aw z>LtMaQ7DR<1I2LkWvwyu#Rwn~;ezT}_g(@5l3h?W%-a86Y-t#O1PubP+z<%?V5D(U zy57A6{h+{?kOZp7&WKZR+=sznMJ}+Dnpo=C_0%R_x_t~J5T?E_{+))l5v1%52>)d-`iiZyx|5!%M2Fb2dU zW3~MwwpEH9Rhue+k$UIOoo($Ds!NbOyMR36fRHu;*15(YcA7siIZk#%JWz>P!qX1?IUojG&nKR>^gArBt2 zit(ETyZ=@V&7mv_Fi4bABcnwP+jzQuHcfU&BrAV91u-rFvEi7y-KnWsvHH=d2 zgAk(GKm_S8RcTJ>2N3~&Hbwp{Z3NF_Xeh}g4Eke)V&dY{W(3&b1j9t4yK_aYJisZZ{1rcU5- z;eD>K;ndPq&B-8yA_S0F!4ThA&{1{x)H<#?k9a#6Pc6L?V^s0``ynL&D;p(!Nmx`Y zFkHex{4p!Ggm^@DlehW}iHHVi}~u=$&N? z(NEBLQ#UxxAkdW>X9LnqUr#t4Lu0=9L8&o>JsqTtT5|%gb3QA~hr0pED71+iFFr)dZ=Q=E6ng{NE{Z~0)C?deO#?Aj zSDQ$z#TeC2T^|=}6GBo-&$;E{HL3!q3Z-szuf)O=G#zDjin4SSP%o%6+2IT#sLjQa ziyxFFz~LMjWY+_a5H!U6%a<=b7QVP^ z*90a62;bVq{?@)P6^DWd^Yilq4|YTV2Nw!Yu;a1lPI-sxR)rf@Fe5DhDP7FH zZZ%4S*1C30P;|O+jB!1;m|rXT90Sm5*RBbQN`PKu+hDD*S^yE(CdtSfg=z>u$cIj> zSvelte app - + diff --git a/src/game/cameraControl.js b/src/game/cameraControl.js index e896ebc..c588045 100644 --- a/src/game/cameraControl.js +++ b/src/game/cameraControl.js @@ -1,91 +1,89 @@ -import { FaceDetector, FilesetResolver } from '@mediapipe/tasks-vision'; +import { FaceLandmarker, FilesetResolver } from '@mediapipe/tasks-vision'; export const cameraInput = { left: false, right: false, active: false, - zone: 'center', - x: 0.5 + zone: 'center' }; let video; -let faceDetector; +let faceLandmarker; +let lastTime = 0; -// 🔥 NARROW CENTER ZONE: -// 0.48 and 0.52 means the center is only 4% of the screen width. -// Adjust these if it's still too hard to trigger movement. -const LEFT_THRESHOLD = 0.48; -const RIGHT_THRESHOLD = 0.52; +const DETECTION_INTERVAL = 60; export async function initCameraControl() { - if (cameraInput.active) return video; + if (cameraInput.active) + return video; video = document.createElement('video'); video.autoplay = true; video.playsInline = true; video.muted = true; + // Set low resolution and frame rate for better performance const stream = await navigator.mediaDevices.getUserMedia({ - video: { - width: 160, - height: 120, - frameRate: { ideal: 20 } // Slightly higher for faster reaction + video: { + width: { ideal: 160 }, + height: { ideal: 120 }, + frameRate: { ideal: 30 } } }); video.srcObject = stream; + //load models for face landmark detection const vision = await FilesetResolver.forVisionTasks( 'https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision/wasm' ); - - faceDetector = await FaceDetector.createFromOptions(vision, { + faceLandmarker = await FaceLandmarker.createFromOptions(vision, { baseOptions: { - modelAssetPath: `https://storage.googleapis.com/mediapipe-models/face_detector/blaze_face_short_range/float16/1/blaze_face_short_range.task`, - delegate: 'GPU' + modelAssetPath: + 'https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/latest/face_landmarker.task', }, - runningMode: 'VIDEO' - }); - - video.addEventListener('loadeddata', () => { - cameraInput.active = true; - startDetectionLoop(); + runningMode: 'VIDEO', + numFaces: 1 }); + cameraInput.active = true; return video; } -function startDetectionLoop() { - const detect = (now) => { - const result = faceDetector.detectForVideo(video, now); +export function updateCameraControl() { + const now = performance.now(); - if (result.detections.length > 0) { - const box = result.detections[0].boundingBox; - // Calculate raw center point (0 to 1) - const rawX = (box.originX + box.width / 2) / 160; - - cameraInput.x = rawX; + // Limit detection frequency to improve performance + if (now - lastTime < DETECTION_INTERVAL) + return; + lastTime = now; - // INSTANT LOGIC (No smoothing) - if (rawX < LEFT_THRESHOLD) { - updateZones(false, true, 'right'); // Mirrored: Face on left of cam = move right - } else if (rawX > RIGHT_THRESHOLD) { - updateZones(true, false, 'left'); // Mirrored: Face on right of cam = move left - } else { - updateZones(false, false, 'center'); - } - } else { - updateZones(false, false, 'center'); - } + if (!faceLandmarker || !video || video.readyState < 2) + return; - video.requestVideoFrameCallback(detect); - }; + const result = faceLandmarker.detectForVideo(video, now); - video.requestVideoFrameCallback(detect); -} + // If no face is detected, reset to center + if (!result.faceLandmarks.length) { + cameraInput.left = false; + cameraInput.right = false; + cameraInput.zone = 'center'; + return; + } -function updateZones(l, r, z) { - cameraInput.left = l; - cameraInput.right = r; - cameraInput.zone = z; + // look at the nose and determine zone + const x = result.faceLandmarks[0][1].x; + if (x < 0.4) { + cameraInput.left = false; + cameraInput.right = true; + cameraInput.zone = 'right'; + } else if (x > 0.6) { + cameraInput.left = true; + cameraInput.right = false; + cameraInput.zone = 'left'; + } else { + cameraInput.left = false; + cameraInput.right = false; + cameraInput.zone = 'center'; + } } \ No newline at end of file diff --git a/src/game/constants.js b/src/game/constants.js index 01d6a88..fa36be8 100644 --- a/src/game/constants.js +++ b/src/game/constants.js @@ -16,4 +16,4 @@ export const PLAT_TYPE = { MOVING: 'moving', SPRING: 'spring', ONE_TIME: 'one-time' -}; +}; \ No newline at end of file