User prompt
Make the ball move slower
User prompt
Make it so the ball moves at a consistent speed and once it loses energy it falls
User prompt
Make the ball have realistic physics
User prompt
Remove the rotation
User prompt
Make it so it rotates at the players mouth rotation ↪💡 Consider importing and using the following plugins: @upit/facekit.v1
User prompt
Make it so the paddle can move all around and can rotate based on the players mouth ↪💡 Consider importing and using the following plugins: @upit/facekit.v1
Code edit (1 edits merged)
Please save this source code
User prompt
Mouth Pong: Face-Controlled Paddle
Initial prompt
Make a game where it is a pong game and the players mouth is tracked in real life and that is the paddle to hit the ball
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var facekit = LK.import("@upit/facekit.v1");
/****
* Classes
****/
// Ball class
var Ball = Container.expand(function () {
var self = Container.call(this);
var ballAsset = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
// Ball velocity
self.vx = 0;
self.vy = 0;
// Ball acceleration (for realism, e.g. gravity or air resistance)
self.ax = 0;
self.ay = 0;
// Ball friction (air resistance)
self.friction = 0.998;
// Ball max speed
self.maxSpeed = 38;
// Ball radius for collision
self.getRadius = function () {
return ballAsset.width / 2;
};
// Ball update: move and handle wall bounces
self.update = function () {
// Apply acceleration
self.vx += self.ax;
self.vy += self.ay;
// Apply friction (air resistance)
self.vx *= self.friction;
self.vy *= self.friction;
// Clamp speed to maxSpeed
var speed = Math.sqrt(self.vx * self.vx + self.vy * self.vy);
if (speed > self.maxSpeed) {
var scale = self.maxSpeed / speed;
self.vx *= scale;
self.vy *= scale;
}
// Move ball
self.x += self.vx;
self.y += self.vy;
};
return self;
});
// Paddle class
var Paddle = Container.expand(function () {
var self = Container.call(this);
var paddleAsset = self.attachAsset('paddle', {
anchorX: 0.5,
anchorY: 0.5
});
// Paddle width/height for collision
self.getWidth = function () {
return paddleAsset.width;
};
self.getHeight = function () {
return paddleAsset.height;
};
return self;
});
// Wall class (for top wall)
var Wall = Container.expand(function () {
var self = Container.call(this);
var wallAsset = self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
});
self.getWidth = function () {
return wallAsset.width;
};
self.getHeight = function () {
return wallAsset.height;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// LK.init.sound('bounce', {volume: 0.5});
// Sound for bounce (optional, but not used as per instructions)
// Wall: thin, wide rectangle (for top wall)
// Ball: circle
// Paddle: wide, short rectangle
// Center positions
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
// Score
var score = 0;
// Create paddle
var paddle = new Paddle();
game.addChild(paddle);
// Paddle position: bottom center, above bottom edge
paddle.x = GAME_WIDTH / 2;
paddle.y = GAME_HEIGHT - 180;
// Create ball
var ball = new Ball();
game.addChild(ball);
// Ball start position: above paddle
ball.x = GAME_WIDTH / 2;
ball.y = paddle.y - 120;
// Ball initial velocity (randomized angle upwards)
function resetBallVelocity() {
// Angle: between -60 and +60 degrees from vertical
var angle = Math.random() * Math.PI / 3 - Math.PI / 6;
var speed = 28;
ball.vx = Math.sin(angle) * speed;
ball.vy = -Math.cos(angle) * speed;
}
resetBallVelocity();
// Create top wall
var topWall = new Wall();
game.addChild(topWall);
topWall.x = GAME_WIDTH / 2;
topWall.y = 40 / 2 + 10; // 10px margin from top
// Score text
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Helper: clamp paddle within game bounds
function clampPaddleX(x) {
var halfWidth = paddle.getWidth() / 2;
if (x < halfWidth) return halfWidth;
if (x > GAME_WIDTH - halfWidth) return GAME_WIDTH - halfWidth;
return x;
}
// Face tracking: update paddle position and rotation
game.update = function () {
var mouthX = GAME_WIDTH / 2;
var mouthY = GAME_HEIGHT - 180;
var mouthAngle = 0;
// Use facekit for paddle position if available (ignore angle/rotation)
if (facekit.mouthCenter && typeof facekit.mouthCenter.x === 'number' && typeof facekit.mouthCenter.y === 'number') {
mouthX = facekit.mouthCenter.x;
mouthY = facekit.mouthCenter.y;
}
// Clamp paddle inside game area
var halfWidth = paddle.getWidth() / 2;
var halfHeight = paddle.getHeight() / 2;
if (typeof paddle.lastX !== "number") paddle.lastX = paddle.x;
if (typeof paddle.lastY !== "number") paddle.lastY = paddle.y;
var prevX = paddle.x;
var prevY = paddle.y;
paddle.x = Math.max(halfWidth, Math.min(GAME_WIDTH - halfWidth, mouthX));
paddle.y = Math.max(halfHeight + 40, Math.min(GAME_HEIGHT - halfHeight, mouthY));
paddle.rotation = 0;
paddle.lastX = prevX;
paddle.lastY = prevY;
// Ball movement
ball.update();
// Ball collision with left/right walls
var ballRadius = ball.getRadius();
if (ball.x - ballRadius < 0) {
ball.x = ballRadius;
// Add a little randomness to bounce angle for realism
var angleJitter = (Math.random() - 0.5) * 0.12;
var speed = Math.sqrt(ball.vx * ball.vx + ball.vy * ball.vy) * 0.98; // lose a bit of energy
var angle = Math.atan2(ball.vy, -ball.vx) + angleJitter;
ball.vx = Math.abs(Math.cos(angle) * speed);
ball.vy = Math.sin(angle) * speed;
}
if (ball.x + ballRadius > GAME_WIDTH) {
ball.x = GAME_WIDTH - ballRadius;
var angleJitter = (Math.random() - 0.5) * 0.12;
var speed = Math.sqrt(ball.vx * ball.vx + ball.vy * ball.vy) * 0.98;
var angle = Math.atan2(ball.vy, -ball.vx) + angleJitter;
ball.vx = -Math.abs(Math.cos(angle) * speed);
ball.vy = Math.sin(angle) * speed;
}
// Ball collision with top wall
if (ball.y - ballRadius < topWall.y + topWall.getHeight() / 2) {
ball.y = topWall.y + topWall.getHeight() / 2 + ballRadius;
var angleJitter = (Math.random() - 0.5) * 0.12;
var speed = Math.sqrt(ball.vx * ball.vx + ball.vy * ball.vy) * 0.98;
var angle = Math.atan2(-ball.vy, ball.vx) + angleJitter;
ball.vy = Math.abs(Math.cos(angle) * speed);
ball.vx = Math.sin(angle) * speed;
}
// Ball collision with paddle (axis-aligned rectangle vs circle)
var hw = paddle.getWidth() / 2;
var hh = paddle.getHeight() / 2;
var closestX = Math.max(paddle.x - hw, Math.min(ball.x, paddle.x + hw));
var closestY = Math.max(paddle.y - hh, Math.min(ball.y, paddle.y + hh));
var dx = ball.x - closestX;
var dy = ball.y - closestY;
var distSq = dx * dx + dy * dy;
// Only allow bounce if ball is moving towards the paddle (ball.vy > 0)
if (distSq < ballRadius * ballRadius && ball.vy > 0) {
// Calculate bounce direction based on where it hit the paddle
var hitPos = (ball.x - paddle.x) / hw; // -1 (left) to 1 (right)
var angle = hitPos * (Math.PI / 2.2); // up to +/-~82deg for more control
// Add a little randomness to bounce angle for realism
var angleJitter = (Math.random() - 0.5) * 0.10;
angle += angleJitter;
// Add paddle movement to ball (if paddle is moving)
var paddleSpeedX = 0;
if (typeof paddle.lastX === "number") {
paddleSpeedX = paddle.x - paddle.lastX;
}
// Ball speed increases slightly on each bounce
var speed = Math.sqrt(ball.vx * ball.vx + ball.vy * ball.vy) * 1.04 + Math.abs(paddleSpeedX) * 0.5;
ball.vx = Math.sin(angle) * speed + paddleSpeedX * 0.4;
ball.vy = -Math.abs(Math.cos(angle) * speed);
// Move ball just outside paddle to prevent sticking
var sep = ballRadius + 2;
if (dy > 0) {
ball.y = paddle.y + hh + sep;
} else {
ball.y = paddle.y - hh - sep;
}
// Flash paddle
LK.effects.flashObject(paddle, 0xffff00, 200);
// Increase score
score += 1;
LK.setScore(score);
scoreTxt.setText(score);
// Optionally: play sound
// LK.getSound('bounce').play();
}
// Ball falls below paddle: game over
if (ball.y - ballRadius > GAME_HEIGHT) {
// Flash screen red
LK.effects.flashScreen(0xff0000, 800);
// Show game over (auto resets game)
LK.showGameOver();
}
};
// No dragging or touch controls needed; paddle is controlled by facekit only
// Reset score on game start
LK.setScore(0);
score = 0;
scoreTxt.setText('0'); ===================================================================
--- original.js
+++ change.js
@@ -16,14 +16,35 @@
});
// Ball velocity
self.vx = 0;
self.vy = 0;
+ // Ball acceleration (for realism, e.g. gravity or air resistance)
+ self.ax = 0;
+ self.ay = 0;
+ // Ball friction (air resistance)
+ self.friction = 0.998;
+ // Ball max speed
+ self.maxSpeed = 38;
// Ball radius for collision
self.getRadius = function () {
return ballAsset.width / 2;
};
// Ball update: move and handle wall bounces
self.update = function () {
+ // Apply acceleration
+ self.vx += self.ax;
+ self.vy += self.ay;
+ // Apply friction (air resistance)
+ self.vx *= self.friction;
+ self.vy *= self.friction;
+ // Clamp speed to maxSpeed
+ var speed = Math.sqrt(self.vx * self.vx + self.vy * self.vy);
+ if (speed > self.maxSpeed) {
+ var scale = self.maxSpeed / speed;
+ self.vx *= scale;
+ self.vy *= scale;
+ }
+ // Move ball
self.x += self.vx;
self.y += self.vy;
};
return self;
@@ -132,27 +153,46 @@
}
// Clamp paddle inside game area
var halfWidth = paddle.getWidth() / 2;
var halfHeight = paddle.getHeight() / 2;
+ if (typeof paddle.lastX !== "number") paddle.lastX = paddle.x;
+ if (typeof paddle.lastY !== "number") paddle.lastY = paddle.y;
+ var prevX = paddle.x;
+ var prevY = paddle.y;
paddle.x = Math.max(halfWidth, Math.min(GAME_WIDTH - halfWidth, mouthX));
paddle.y = Math.max(halfHeight + 40, Math.min(GAME_HEIGHT - halfHeight, mouthY));
paddle.rotation = 0;
+ paddle.lastX = prevX;
+ paddle.lastY = prevY;
// Ball movement
ball.update();
// Ball collision with left/right walls
var ballRadius = ball.getRadius();
if (ball.x - ballRadius < 0) {
ball.x = ballRadius;
- ball.vx = -ball.vx;
+ // Add a little randomness to bounce angle for realism
+ var angleJitter = (Math.random() - 0.5) * 0.12;
+ var speed = Math.sqrt(ball.vx * ball.vx + ball.vy * ball.vy) * 0.98; // lose a bit of energy
+ var angle = Math.atan2(ball.vy, -ball.vx) + angleJitter;
+ ball.vx = Math.abs(Math.cos(angle) * speed);
+ ball.vy = Math.sin(angle) * speed;
}
if (ball.x + ballRadius > GAME_WIDTH) {
ball.x = GAME_WIDTH - ballRadius;
- ball.vx = -ball.vx;
+ var angleJitter = (Math.random() - 0.5) * 0.12;
+ var speed = Math.sqrt(ball.vx * ball.vx + ball.vy * ball.vy) * 0.98;
+ var angle = Math.atan2(ball.vy, -ball.vx) + angleJitter;
+ ball.vx = -Math.abs(Math.cos(angle) * speed);
+ ball.vy = Math.sin(angle) * speed;
}
// Ball collision with top wall
if (ball.y - ballRadius < topWall.y + topWall.getHeight() / 2) {
ball.y = topWall.y + topWall.getHeight() / 2 + ballRadius;
- ball.vy = -ball.vy;
+ var angleJitter = (Math.random() - 0.5) * 0.12;
+ var speed = Math.sqrt(ball.vx * ball.vx + ball.vy * ball.vy) * 0.98;
+ var angle = Math.atan2(-ball.vy, ball.vx) + angleJitter;
+ ball.vy = Math.abs(Math.cos(angle) * speed);
+ ball.vx = Math.sin(angle) * speed;
}
// Ball collision with paddle (axis-aligned rectangle vs circle)
var hw = paddle.getWidth() / 2;
var hh = paddle.getHeight() / 2;
@@ -164,11 +204,20 @@
// Only allow bounce if ball is moving towards the paddle (ball.vy > 0)
if (distSq < ballRadius * ballRadius && ball.vy > 0) {
// Calculate bounce direction based on where it hit the paddle
var hitPos = (ball.x - paddle.x) / hw; // -1 (left) to 1 (right)
- var angle = hitPos * (Math.PI / 3); // up to +/-60deg
- var speed = Math.sqrt(ball.vx * ball.vx + ball.vy * ball.vy) * 1.04;
- ball.vx = Math.sin(angle) * speed;
+ var angle = hitPos * (Math.PI / 2.2); // up to +/-~82deg for more control
+ // Add a little randomness to bounce angle for realism
+ var angleJitter = (Math.random() - 0.5) * 0.10;
+ angle += angleJitter;
+ // Add paddle movement to ball (if paddle is moving)
+ var paddleSpeedX = 0;
+ if (typeof paddle.lastX === "number") {
+ paddleSpeedX = paddle.x - paddle.lastX;
+ }
+ // Ball speed increases slightly on each bounce
+ var speed = Math.sqrt(ball.vx * ball.vx + ball.vy * ball.vy) * 1.04 + Math.abs(paddleSpeedX) * 0.5;
+ ball.vx = Math.sin(angle) * speed + paddleSpeedX * 0.4;
ball.vy = -Math.abs(Math.cos(angle) * speed);
// Move ball just outside paddle to prevent sticking
var sep = ballRadius + 2;
if (dy > 0) {