Web网页开发——水果忍者
// Game variables
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const startScreen = document.getElementById('startScreen');
const gameOverScreen = document.getElementById('gameOverScreen');
const startButton = document.getElementById('startButton');
const restartButton = document.getElementById('restartButton');
const scoreDisplay = document.getElementById('scoreDisplay');
const finalScore = document.getElementById('finalScore');
let score = 0;
let gameActive = false;
let gameObjects = [];
let sliceTrail = [];
let sliceActive = false;
let lastTimestamp = 0;
let spawnTimer = 0;
let comboCount = 0;
let comboTimer = 0;
// Resize canvas
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
// Initialize game
function initGame() {
resizeCanvas();
score = 0;
gameObjects = [];
sliceTrail = [];
sliceActive = false;
scoreDisplay.textContent = score;
gameActive = true;
lastTimestamp = 0;
spawnTimer = 0;
// Start animation
requestAnimationFrame(gameLoop);
}
// Game objects classes
class GameObject {
constructor(x, y, type) {
this.x = x;
this.y = y;
this.type = type;
this.sliced = false;
this.velocityX = Math.random() * 8 - 4;
this.velocityY = -15 - Math.random() * 5;
this.gravity = 0.5;
this.rotation = 0;
this.rotationSpeed = Math.random() * 0.1 - 0.05;
this.size = 40 + Math.random() * 20;
if (this.type === 'banana') {
this.color = '#FFD700';
} else if (this.type === 'bomb') {
this.color = '#333';
this.size = 30 + Math.random() * 10;
} else if (this.type === 'pomegranate') {
this.color = '#FF4500';
this.size = 50 + Math.random() * 10;
} else if (this.type === 'watermelon') {
this.color = '#32CD32';
this.size = 60 + Math.random() * 10;
} else {
// Default apple
this.color = '#FF0000';
}
this.slicedColor1 = this.color;
this.slicedColor2 = this.type === 'watermelon' ? '#FF6347' : this.color;
// For slice animation
this.sliceAngle = 0;
this.slicePart1 = { x: 0, y: 0, vx: 0, vy: 0, rotation: 0 };
this.slicePart2 = { x: 0, y: 0, vx: 0, vy: 0, rotation: 0 };
}
update() {
if (this.sliced) {
// Update sliced parts
this.slicePart1.x += this.slicePart1.vx;
this.slicePart1.y += this.slicePart1.vy;
this.slicePart1.vy += this.gravity;
this.slicePart1.rotation += 0.05;
this.slicePart2.x += this.slicePart2.vx;
this.slicePart2.y += this.slicePart2.vy;
this.slicePart2.vy += this.gravity;
this.slicePart2.rotation += 0.05;
return this.slicePart1.y < canvas.height && this.slicePart2.y < canvas.height;
} else {
// Update normal object
this.x += this.velocityX;
this.y += this.velocityY;
this.velocityY += this.gravity;
this.rotation += this.rotationSpeed;
return this.y < canvas.height + 100;
}
}
draw() {
ctx.save();
if (this.sliced) {
// Draw first slice part
ctx.save();
ctx.translate(this.slicePart1.x, this.slicePart1.y);
ctx.rotate(this.slicePart1.rotation);
ctx.beginPath();
ctx.arc(0, 0, this.size / 2, 0, Math.PI, false);
ctx.fillStyle = this.slicedColor1;
ctx.fill();
if (this.type === 'watermelon') {
ctx.beginPath();
ctx.arc(0, 0, this.size / 2 - 5, 0, Math.PI, false);
ctx.fillStyle = this.slicedColor2;
ctx.fill();
}
ctx.restore();
// Draw second slice part
ctx.save();
ctx.translate(this.slicePart2.x, this.slicePart2.y);
ctx.rotate(this.slicePart2.rotation);
ctx.beginPath();
ctx.arc(0, 0, this.size / 2, Math.PI, 2 * Math.PI, false);
ctx.fillStyle = this.slicedColor1;
ctx.fill();
if (this.type === 'watermelon') {
ctx.beginPath();
ctx.arc(0, 0, this.size / 2 - 5, Math.PI, 2 * Math.PI, false);
ctx.fillStyle = this.slicedColor2;
ctx.fill();
}
ctx.restore();
} else {
// Draw normal object
ctx.translate(this.x, this.y);
ctx.rotate(this.rotation);
if (this.type === 'bomb') {
// Draw bomb
ctx.beginPath();
ctx.arc(0, 0, this.size / 2, 0, 2 * Math.PI);
ctx.fillStyle = this.color;
ctx.fill();
// Draw fuse
ctx.beginPath();
ctx.moveTo(0, -this.size / 2);
ctx.quadraticCurveTo(10, -this.size / 2 - 15, 20, -this.size / 2 - 10);
ctx.lineWidth = 3;
ctx.strokeStyle = '#8B4513';
ctx.stroke();
} else if (this.type === 'banana') {
// Draw banana
ctx.beginPath();
ctx.arc(0, 0, this.size / 2, 0.3 * Math.PI, 1.7 * Math.PI);
ctx.lineWidth = this.size / 2;
ctx.strokeStyle = this.color;
ctx.stroke();
} else if (this.type === 'watermelon') {
// Draw watermelon
ctx.beginPath();
ctx.arc(0, 0, this.size / 2, 0, 2 * Math.PI);
ctx.fillStyle = '#32CD32';
ctx.fill();
// Inner part
ctx.beginPath();
ctx.arc(0, 0, this.size / 2 - 5, 0, 2 * Math.PI);
ctx.fillStyle = '#FF6347';
ctx.fill();
// Seeds
ctx.fillStyle = 'black';
for (let i = 0; i < 8; i++) {
const angle = i * (Math.PI / 4);
const distance = this.size / 4;
ctx.beginPath();
ctx.ellipse(
Math.cos(angle) * distance,
Math.sin(angle) * distance,
3, 5, angle, 0, 2 * Math.PI
);
ctx.fill();
}
} else if (this.type === 'pomegranate') {
// Draw pomegranate
ctx.beginPath();
ctx.arc(0, 0, this.size / 2, 0, 2 * Math.PI);
ctx.fillStyle = this.color;
ctx.fill();
// Crown
ctx.beginPath();
ctx.moveTo(-10, -this.size / 2);
ctx.lineTo(10, -this.size / 2);
ctx.lineTo(0, -this.size / 2 - 10);
ctx.closePath();
ctx.fillStyle = '#8B4513';
ctx.fill();
} else {
// Draw apple
ctx.beginPath();
ctx.arc(0, 0, this.size / 2, 0, 2 * Math.PI);
ctx.fillStyle = this.color;
ctx.fill();
// Stem
ctx.beginPath();
ctx.moveTo(0, -this.size / 2);
ctx.lineTo(0, -this.size / 2 - 7);
ctx.lineWidth = 3;
ctx.strokeStyle = '#8B4513';
ctx.stroke();
}
}
ctx.restore();
}
checkSlice(slicePath) {
if (this.sliced) return false;
// Check if the slice path intersects with the object
for (let i = 1; i < slicePath.length; i++) {
const x1 = slicePath[i-1].x;
const y1 = slicePath[i-1].y;
const x2 = slicePath[i].x;
const y2 = slicePath[i].y;
// Calculate distance from line segment to center of object
const distance = distToSegment(this.x, this.y, x1, y1, x2, y2);
if (distance < this.size / 2) {
// Calculate slice angle
this.sliceAngle = Math.atan2(y2 - y1, x2 - x1);
// Set the slice parts
this.slicePart1.x = this.x;
this.slicePart1.y = this.y;
this.slicePart1.vx = this.velocityX - 1 + Math.random() * 2;
this.slicePart1.vy = this.velocityY - 2;
this.slicePart2.x =