This is a mobile optimized page that loads fast, if you want to load the real page, click this text.

code game Physics Apple Catcher html

omaigot

Gà con
<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8">
<title>🍎 Physics Apple - Bouncing Effect</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
text-align: center;
background: #050a10;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
font-family: 'Segoe UI', sans-serif;
color: white;
overflow: hidden;
cursor: none;
}
canvas {
background: radial-gradient(circle, #1a2a44 0%, #0a1018 100%);
border-radius: 20px;
box-shadow: 0 0 60px rgba(0,0,0,1);
display: block;
touch-action: none;
}
.hud { position: absolute; top: 20px; left: 20px; text-align: left; pointer-events: none; }
h2 { color: #f1c40f; font-size: 24px; text-shadow: 0 2px 4px rgba(0,0,0,0.5); }
</style>
</head>
<body>

<div class="hud">
<h2>🍎 ĐIỂM: <span id="scoreVal">0</span></h2>
</div>

<canvas id="gameCanvas" width="480" height="720"></canvas>

<script>
const canvas = document.getElementById("gameCanvas");
const ctx = canvas.getContext("2d");
const scoreVal = document.getElementById("scoreVal");

// --- CẤU HÌNH VẬT LÝ NÂNG CAO ---
const GRAVITY = 0.4;
const BOUNCE_FORCE = -0.65; // Tăng độ nảy khi chạm đáy giỏ (Sắp rơi ra ngoài)
const FRICTION_IN_BASKET = 0.96;
const MAX_APPLES = 15;

let score = 0;
let gameOver = false;
let fallingApples = [];
let basketApples = [];
let particles = [];

const basket = {
width: 140,
height: 70,
x: 170,
y: 600,
targetX: 170,
vx: 0,
color: "#A0522D",
rimY: 610
};

class FallingApple {
constructor() { this.reset(); }
reset() {
this.x = Math.random() * (canvas.width - 80) + 40;
this.y = -60;
this.radius = 18;
this.vy = 2;
this.color = "#ff4d4d";
}
update() {
this.vy += GRAVITY;
this.y += this.vy;
}
draw() {
drawApple(this.x, this.y, this.radius, this.color, true);
}
}

class BasketApple {
constructor(x, y, vy) {
this.x = x;
this.y = y;
this.radius = 15;
this.vx = (Math.random() - 0.5) * 5;
this.vy = vy * BOUNCE_FORCE; // Phản lực nảy lên khi rơi vào
this.angle = 0;
this.va = 0; // Vận tốc góc (xoay khi lắc)
}
update() {
this.vy += GRAVITY;

// Lực quán tính từ giỏ tác động lên táo
this.vx -= basket.vx * 0.15;
this.va += basket.vx * 0.01; // Táo xoay nhẹ khi giỏ di chuyển

this.x += this.vx;
this.y += this.vy;
this.angle += this.va;

this.vx *= FRICTION_IN_BASKET;
this.va *= 0.9;

// --- GIỚI HẠN VẬT LÝ TRONG GIỎ (VÁCH NGĂN VÔ HÌNH) ---
const leftWall = basket.x + 15;
const rightWall = basket.x + basket.width - 15;
const bottomWall = basket.y + basket.height - 15;
const topLimit = basket.y - 40; // Cho phép nảy cao hơn mép giỏ 40px

// Chạm đáy giỏ (Nảy lên)
if (this.y > bottomWall - this.radius) {
this.y = bottomWall - this.radius;
this.vy *= BOUNCE_FORCE;
if (Math.abs(this.vy) < 1) this.vy = 0;
}
// Chạm tường trái/phải
if (this.x < leftWall + this.radius) {
this.x = leftWall + this.radius;
this.vx *= -0.5;
}
if (this.x > rightWall - this.radius) {
this.x = rightWall - this.radius;
this.vx *= -0.5;
}
// Chặn không cho nảy quá cao rồi bay mất
if (this.y < topLimit) {
this.y = topLimit;
this.vy = 0.5; // Đẩy nhẹ xuống
}
}
draw() {
ctx.save();
ctx.translate(this.x, this.y);
ctx.rotate(this.angle);
drawApple(0, 0, this.radius, "#d63031", false);
ctx.restore();
}
}

function drawApple(x, y, r, color, glow) {
if (glow) {
ctx.shadowBlur = 20; ctx.shadowColor = "rgba(255,0,0,0.4)";
}
ctx.fillStyle = color;
ctx.beginPath(); ctx.arc(x, y, r, 0, Math.PI*2); ctx.fill();
// Chi tiết highlight cho táo thật hơn
ctx.fillStyle = "rgba(255,255,255,0.3)";
ctx.beginPath(); ctx.arc(x - r/3, y - r/3, r/4, 0, Math.PI*2); ctx.fill();
// Cuống
ctx.fillStyle = "#4a2c2a"; ctx.fillRect(x-2, y-r-5, 4, 8);
ctx.shadowBlur = 0;
}

// Điều khiển
window.addEventListener("mousemove", (e) => {
const rect = canvas.getBoundingClientRect();
basket.targetX = e.clientX - rect.left - basket.width / 2;
});

window.addEventListener("mousedown", () => { if (gameOver) restartGame(); });

function update() {
// Cập nhật vị trí và vận tốc giỏ
let oldX = basket.x;
basket.x += (basket.targetX - basket.x) * 0.2;
basket.vx = basket.x - oldX;

if (gameOver) return;

// Quản lý táo rơi
if (fallingApples.length < 1 + Math.floor(score/10)) {
if (Math.random() < 0.02) fallingApples.push(new FallingApple());
}

fallingApples.forEach((a, i) => {
a.update();
// Check hứng được táo
if (a.y + a.radius > basket.y && a.y < basket.y + 30 &&
a.x > basket.x && a.x < basket.x + basket.width) {

if (basketApples.length < MAX_APPLES) {
basketApples.push(new BasketApple(a.x, a.y, a.vy));
}
score++;
scoreVal.innerText = score;
fallingApples.splice(i, 1);
}
// Check thua
if (a.y > canvas.height + 50) gameOver = true;
});

basketApples.forEach(ba => ba.update());
}

function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);

// Vẽ giỏ - Lớp sau
ctx.fillStyle = "#5D4037";
ctx.beginPath();
ctx.roundRect(basket.x, basket.y, basket.width, basket.height, [5, 5, 20, 20]);
ctx.fill();

// Vẽ táo trong giỏ (nằm giữa lớp trước và lớp sau của giỏ)
basketApples.forEach(ba => ba.draw());

// Vẽ giỏ - Lớp trước (tạo hiệu ứng táo ở bên trong)
ctx.fillStyle = "rgba(139, 69, 19, 0.9)";
ctx.beginPath();
ctx.roundRect(basket.x, basket.y + 20, basket.width, basket.height - 20, [0, 0, 20, 20]);
ctx.fill();
// Vân rổ
ctx.strokeStyle = "rgba(0,0,0,0.2)";
for(let i=20; i<basket.width; i+=25) {
ctx.strokeRect(basket.x + i, basket.y + 25, 1, basket.height - 35);
}

fallingApples.forEach(a => a.draw());

if (gameOver) {
ctx.fillStyle = "rgba(0,0,0,0.8)";
ctx.fillRect(0,0,canvas.width, canvas.height);
ctx.fillStyle = "white";
ctx.textAlign = "center";
ctx.font = "bold 40px Arial";
ctx.fillText("TÁO RƠI MẤT RỒI!", 240, 340);
ctx.font = "20px Arial";
ctx.fillText("Click để chơi lại", 240, 390);
}
}

function restartGame() {
score = 0; scoreVal.innerText = "0";
gameOver = false;
fallingApples = [new FallingApple()];
basketApples = [];
}

function loop() { update(); draw(); requestAnimationFrame(loop); }
loop();
</script>
</body>
</html>
 
Sửa lần cuối: