diff --git a/package-lock.json b/package-lock.json
index b4b73a3..fd2feab 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,13 +1,14 @@
{
- "name": "svelte-app",
+ "name": "nubzuki-jump",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
- "name": "svelte-app",
+ "name": "nubzuki-jump",
"version": "1.0.0",
"dependencies": {
+ "@mediapipe/tasks-vision": "^0.10.35",
"p5": "1.11.4",
"p5-svelte": "^3.1.2",
"p5play": "^3.8.14",
@@ -85,6 +86,12 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
+ "node_modules/@mediapipe/tasks-vision": {
+ "version": "0.10.35",
+ "resolved": "https://registry.npmjs.org/@mediapipe/tasks-vision/-/tasks-vision-0.10.35.tgz",
+ "integrity": "sha512-HOvadwVRE6JC+45nyYhmnywnr5h/J8KZvOeUNVOG9q/0875pZgItznFB9bRTvLc264YSJqiZ1NsIpCStJw/egg==",
+ "license": "Apache-2.0"
+ },
"node_modules/@polka/url": {
"version": "1.0.0-next.29",
"resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz",
diff --git a/package.json b/package.json
index a19bef6..152f09a 100644
--- a/package.json
+++ b/package.json
@@ -1,5 +1,5 @@
{
- "name": "svelte-app",
+ "name": "nubzuki-jump",
"version": "1.0.0",
"private": true,
"type": "module",
@@ -19,6 +19,7 @@
"svelte": "^5.55.5"
},
"dependencies": {
+ "@mediapipe/tasks-vision": "^0.10.35",
"p5": "1.11.4",
"p5-svelte": "^3.1.2",
"p5play": "^3.8.14",
diff --git a/src/App.svelte b/src/App.svelte
index b157f8b..3b055e6 100644
--- a/src/App.svelte
+++ b/src/App.svelte
@@ -1,17 +1,17 @@
+
+
+
Camera Control
+
+ Enable camera and move your head to control the game, or use the arrows.
+
+
+
+
+
+ {#if !enabled}
+
+ {/if}
+
+
+
GO LEFT
+
STAY
+
GO RIGHT
+
+
+
+ {#if error}
+
{error}
+ {/if}
+
+
+
diff --git a/src/game/cameraControl.js b/src/game/cameraControl.js
new file mode 100644
index 0000000..22f5265
--- /dev/null
+++ b/src/game/cameraControl.js
@@ -0,0 +1,90 @@
+import { FaceDetector, FilesetResolver } from '@mediapipe/tasks-vision';
+
+export const cameraInput = {
+ left: false,
+ right: false,
+ active: false,
+ zone: 'center',
+ x: 0.5
+};
+
+let video;
+let faceDetector;
+let smoothedX = 0.5;
+
+// 🔥 TWEAK THESE FOR FEEL:
+const SMOOTHING_FACTOR = 0.6; // Higher = more responsive/less lag (try 0.5 - 0.8)
+const LEFT_THRESHOLD = 0.45; // Closer to 0.5 = smaller center zone
+const RIGHT_THRESHOLD = 0.55; // Closer to 0.5 = smaller center zone
+
+export async function initCameraControl() {
+ if (cameraInput.active) return video;
+
+ video = document.createElement('video');
+ video.autoplay = true;
+ video.playsInline = true;
+ video.muted = true;
+
+ const stream = await navigator.mediaDevices.getUserMedia({
+ video: { width: 160, height: 120, frameRate: 15 }
+ });
+
+ video.srcObject = stream;
+
+ const vision = await FilesetResolver.forVisionTasks(
+ 'https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision/wasm'
+ );
+
+ faceDetector = await FaceDetector.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'
+ },
+ runningMode: 'VIDEO'
+ });
+
+ video.addEventListener('loadeddata', () => {
+ cameraInput.active = true;
+ startDetectionLoop();
+ });
+
+ return video;
+}
+
+function startDetectionLoop() {
+ const detect = (now) => {
+ const result = faceDetector.detectForVideo(video, now);
+
+ if (result.detections.length > 0) {
+ const box = result.detections[0].boundingBox;
+ const centerX = box.originX + box.width / 2;
+ const normalizedX = centerX / 160;
+
+ // Snappier smoothing
+ smoothedX += (normalizedX - smoothedX) * SMOOTHING_FACTOR;
+ cameraInput.x = smoothedX;
+
+ // Updated Logic with smaller center zone
+ if (smoothedX < LEFT_THRESHOLD) {
+ updateZones(false, true, 'right'); // Mirrored
+ } else if (smoothedX > RIGHT_THRESHOLD) {
+ updateZones(true, false, 'left'); // Mirrored
+ } else {
+ updateZones(false, false, 'center');
+ }
+ } else {
+ // Snap to center if face is lost
+ updateZones(false, false, 'center');
+ }
+
+ video.requestVideoFrameCallback(detect);
+ };
+
+ video.requestVideoFrameCallback(detect);
+}
+
+function updateZones(l, r, z) {
+ cameraInput.left = l;
+ cameraInput.right = r;
+ cameraInput.zone = z;
+}
\ No newline at end of file
diff --git a/src/game/player.js b/src/game/player.js
index 7659a7d..7da86f1 100644
--- a/src/game/player.js
+++ b/src/game/player.js
@@ -1,4 +1,6 @@
import { PLAT_TYPE } from './constants.js';
+import { cameraInput } from './cameraControl.js';
+
export function createPlayer() {
const player = new Sprite();
@@ -44,13 +46,15 @@ export function updatePlayerPosition(player, platforms) {
// Controls
player.vel.x = 0;
- if (keyIsDown(LEFT_ARROW)) {
+ if (keyIsDown(LEFT_ARROW) || cameraInput.left) {
player.vel.x = -5;
- }
- if (keyIsDown(RIGHT_ARROW)) {
+ } else if (keyIsDown(RIGHT_ARROW) || cameraInput.right) {
player.vel.x = 5;
+ } else {
+ player.vel.x *= 0.5;
}
+
// Wrap horizontally
if (player.x > width) {
player.x = 0;