Auto commit: 2025-11-21T17:37:25.475Z
This commit is contained in:
parent
c7d1a28045
commit
3be4edf7cd
@ -27,6 +27,6 @@
|
|||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
|
||||||
|
|
||||||
<!-- Custom JS -->
|
<!-- Custom JS -->
|
||||||
<script src="{% static 'js/game.js' %}"></script>
|
{% block scripts %}{% endblock %}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
{% block title %}Orange Catcher - A Fun Arcade Game{% endblock %}
|
{% block title %}Orange Catcher - A Fun Arcade Game{% endblock %}
|
||||||
|
|
||||||
@ -45,4 +46,289 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block scripts %}
|
||||||
|
<script>
|
||||||
|
const canvas = document.getElementById('gameCanvas');
|
||||||
|
if (!canvas) {
|
||||||
|
console.error('Canvas element not found!');
|
||||||
|
}
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
// Set fixed canvas size
|
||||||
|
canvas.width = 800;
|
||||||
|
canvas.height = 600;
|
||||||
|
|
||||||
|
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
|
||||||
|
let score = 0;
|
||||||
|
let lives = 3;
|
||||||
|
let gameOver = false;
|
||||||
|
let gameStarted = false;
|
||||||
|
let orangeInterval;
|
||||||
|
|
||||||
|
const player = {
|
||||||
|
width: 100,
|
||||||
|
height: 50,
|
||||||
|
x: canvas.width / 2 - 50,
|
||||||
|
y: canvas.height - 60,
|
||||||
|
speed: 500,
|
||||||
|
dx: 0,
|
||||||
|
draw() {
|
||||||
|
ctx.fillStyle = '#8B4513';
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(this.x, this.y);
|
||||||
|
ctx.lineTo(this.x + this.width, this.y);
|
||||||
|
ctx.lineTo(this.x + this.width - 15, this.y + this.height);
|
||||||
|
ctx.lineTo(this.x + 15, this.y + this.height);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.fill();
|
||||||
|
ctx.strokeStyle = '#5C2F0E';
|
||||||
|
ctx.lineWidth = 8;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(this.x, this.y);
|
||||||
|
ctx.lineTo(this.x + this.width, this.y);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const orangeProps = {
|
||||||
|
size: 20,
|
||||||
|
speed: 150,
|
||||||
|
};
|
||||||
|
let oranges = [];
|
||||||
|
|
||||||
|
function createOrange() {
|
||||||
|
oranges.push({
|
||||||
|
x: Math.random() * (canvas.width - orangeProps.size),
|
||||||
|
y: -orangeProps.size,
|
||||||
|
size: orangeProps.size,
|
||||||
|
speed: orangeProps.speed + Math.random() * 100,
|
||||||
|
draw() {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(this.x + this.size / 2, this.y + this.size / 2, this.size, 0, Math.PI * 2);
|
||||||
|
ctx.fillStyle = '#FFA500';
|
||||||
|
ctx.fill();
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.ellipse(this.x + this.size / 2 + 5, this.y, 8, 3, Math.PI / 4, 0, Math.PI * 2);
|
||||||
|
ctx.fillStyle = '#228B22';
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let lastTime = 0;
|
||||||
|
|
||||||
|
function update(timestamp) {
|
||||||
|
if (!gameStarted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gameOver) {
|
||||||
|
drawGameOver();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const deltaTime = (timestamp - lastTime) / 1000;
|
||||||
|
lastTime = timestamp;
|
||||||
|
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
drawScene();
|
||||||
|
player.draw();
|
||||||
|
player.x += player.dx * player.speed * deltaTime;
|
||||||
|
|
||||||
|
if (player.x < 0) player.x = 0;
|
||||||
|
if (player.x + player.width > canvas.width) {
|
||||||
|
player.x = canvas.width - player.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
oranges.forEach((orange, index) => {
|
||||||
|
orange.y += orange.speed * deltaTime;
|
||||||
|
orange.draw();
|
||||||
|
|
||||||
|
if (
|
||||||
|
orange.y + orange.size > player.y &&
|
||||||
|
orange.x < player.x + player.width &&
|
||||||
|
orange.x + orange.size > player.x
|
||||||
|
) {
|
||||||
|
score++;
|
||||||
|
playCatchSound();
|
||||||
|
oranges.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (orange.y > canvas.height) {
|
||||||
|
lives--;
|
||||||
|
oranges.splice(index, 1);
|
||||||
|
if (lives <= 0) {
|
||||||
|
gameOver = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
drawUI();
|
||||||
|
requestAnimationFrame(update);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawScene() {
|
||||||
|
ctx.fillStyle = '#0A192F';
|
||||||
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
|
ctx.fillStyle = '#228B22';
|
||||||
|
ctx.fillRect(0, canvas.height - 20, canvas.width, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawUI() {
|
||||||
|
ctx.fillStyle = '#E6F1FF';
|
||||||
|
ctx.font = '24px Poppins, sans-serif';
|
||||||
|
ctx.textAlign = 'left';
|
||||||
|
ctx.fillText(`Score: ${score}`, 20, 40);
|
||||||
|
ctx.textAlign = 'right';
|
||||||
|
ctx.fillText(`Lives: ${lives}`, canvas.width - 20, 40);
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawGameOver() {
|
||||||
|
clearInterval(orangeInterval);
|
||||||
|
ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
|
||||||
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
|
ctx.fillStyle = '#E6F1FF';
|
||||||
|
ctx.font = '50px Poppins, sans-serif';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.fillText('GAME OVER', canvas.width / 2, canvas.height / 2 - 40);
|
||||||
|
ctx.font = '30px Poppins, sans-serif';
|
||||||
|
ctx.fillText(`Final Score: ${score}`, canvas.width / 2, canvas.height / 2 + 20);
|
||||||
|
ctx.font = '20px Poppins, sans-serif';
|
||||||
|
ctx.fillText('Click to Restart', canvas.width / 2, canvas.height / 2 + 70);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawInitialMessage() {
|
||||||
|
ctx.fillStyle = '#E6F1FF';
|
||||||
|
ctx.font = '30px Poppins, sans-serif';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.fillText('Click "Play Now" to Start!', canvas.width / 2, canvas.height / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function playCatchSound() {
|
||||||
|
if (!audioCtx) return;
|
||||||
|
const oscillator = audioCtx.createOscillator();
|
||||||
|
const gainNode = audioCtx.createGain();
|
||||||
|
oscillator.connect(gainNode);
|
||||||
|
gainNode.connect(audioCtx.destination);
|
||||||
|
oscillator.type = 'sine';
|
||||||
|
oscillator.frequency.setValueAtTime(880, audioCtx.currentTime);
|
||||||
|
gainNode.gain.setValueAtTime(0.1, audioCtx.currentTime);
|
||||||
|
gainNode.gain.exponentialRampToValueAtTime(0.00001, audioCtx.currentTime + 0.5);
|
||||||
|
oscillator.start(audioCtx.currentTime);
|
||||||
|
oscillator.stop(audioCtx.currentTime + 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateLeaderboard() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/scores/');
|
||||||
|
const scores = await response.json();
|
||||||
|
const leaderboardBody = document.getElementById('leaderboard-body');
|
||||||
|
leaderboardBody.innerHTML = '';
|
||||||
|
scores.forEach((score, index) => {
|
||||||
|
const row = `<tr>
|
||||||
|
<th scope="row">${index + 1}</th>
|
||||||
|
<td>${score.player_name}</td>
|
||||||
|
<td>${score.score}</td>
|
||||||
|
</tr>`;
|
||||||
|
leaderboardBody.innerHTML += row;
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error updating leaderboard:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function move(e) {
|
||||||
|
if (!gameStarted || gameOver) return;
|
||||||
|
if (e.key === 'ArrowRight' || e.key === 'd') {
|
||||||
|
player.dx = 1;
|
||||||
|
} else if (e.key === 'ArrowLeft' || e.key === 'a') {
|
||||||
|
player.dx = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopMove(e) {
|
||||||
|
if (['ArrowRight', 'd', 'ArrowLeft', 'a'].includes(e.key)) {
|
||||||
|
player.dx = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initGame() {
|
||||||
|
score = 0;
|
||||||
|
lives = 3;
|
||||||
|
oranges = [];
|
||||||
|
gameOver = false;
|
||||||
|
gameStarted = false;
|
||||||
|
player.x = canvas.width / 2 - player.width / 2;
|
||||||
|
drawScene();
|
||||||
|
drawInitialMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
function restartGame() {
|
||||||
|
score = 0;
|
||||||
|
lives = 3;
|
||||||
|
oranges = [];
|
||||||
|
gameOver = false;
|
||||||
|
player.x = canvas.width / 2 - player.width / 2;
|
||||||
|
startCountdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
function startCountdown() {
|
||||||
|
let count = 3;
|
||||||
|
const countdown = setInterval(() => {
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
drawScene();
|
||||||
|
ctx.fillStyle = '#E6F1FF';
|
||||||
|
ctx.font = '60px Poppins, sans-serif';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
if (count > 0) {
|
||||||
|
ctx.fillText(count, canvas.width / 2, canvas.height / 2);
|
||||||
|
count--;
|
||||||
|
} else {
|
||||||
|
clearInterval(countdown);
|
||||||
|
ctx.fillText('Go!', canvas.width / 2, canvas.height / 2);
|
||||||
|
setTimeout(startGame, 500);
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function startGame() {
|
||||||
|
gameStarted = true;
|
||||||
|
lastTime = performance.now();
|
||||||
|
if (orangeInterval) clearInterval(orangeInterval);
|
||||||
|
orangeInterval = setInterval(createOrange, 1200);
|
||||||
|
requestAnimationFrame(update);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event Listeners
|
||||||
|
document.addEventListener('keydown', move);
|
||||||
|
document.addEventListener('keyup', stopMove);
|
||||||
|
|
||||||
|
canvas.addEventListener('click', () => {
|
||||||
|
if (gameOver) {
|
||||||
|
restartGame();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const playButton = document.querySelector('.btn-play');
|
||||||
|
const gameArea = document.getElementById('game-area');
|
||||||
|
|
||||||
|
if (playButton && gameArea) {
|
||||||
|
playButton.addEventListener('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
gameArea.scrollIntoView({ behavior: 'smooth' });
|
||||||
|
startCountdown();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.error('Play button or game area not found!');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initial Setup
|
||||||
|
initGame();
|
||||||
|
updateLeaderboard();
|
||||||
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@ -1,279 +1,280 @@
|
|||||||
document.addEventListener('DOMContentLoaded', () => {
|
const canvas = document.getElementById('gameCanvas');
|
||||||
const canvas = document.getElementById('gameCanvas');
|
if (!canvas) {
|
||||||
if (!canvas) {
|
console.error('Canvas element not found!');
|
||||||
console.error('Canvas element not found!');
|
}
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
// Set fixed canvas size
|
||||||
|
canvas.width = 800;
|
||||||
|
canvas.height = 600;
|
||||||
|
|
||||||
|
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
|
||||||
|
let score = 0;
|
||||||
|
let lives = 3;
|
||||||
|
let gameOver = false;
|
||||||
|
let gameStarted = false;
|
||||||
|
let orangeInterval;
|
||||||
|
|
||||||
|
const player = {
|
||||||
|
width: 100,
|
||||||
|
height: 50,
|
||||||
|
x: canvas.width / 2 - 50,
|
||||||
|
y: canvas.height - 60,
|
||||||
|
speed: 500,
|
||||||
|
dx: 0,
|
||||||
|
draw() {
|
||||||
|
ctx.fillStyle = '#8B4513';
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(this.x, this.y);
|
||||||
|
ctx.lineTo(this.x + this.width, this.y);
|
||||||
|
ctx.lineTo(this.x + this.width - 15, this.y + this.height);
|
||||||
|
ctx.lineTo(this.x + 15, this.y + this.height);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.fill();
|
||||||
|
ctx.strokeStyle = '#5C2F0E';
|
||||||
|
ctx.lineWidth = 8;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(this.x, this.y);
|
||||||
|
ctx.lineTo(this.x + this.width, this.y);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const orangeProps = {
|
||||||
|
size: 20,
|
||||||
|
speed: 150,
|
||||||
|
};
|
||||||
|
let oranges = [];
|
||||||
|
|
||||||
|
function createOrange() {
|
||||||
|
oranges.push({
|
||||||
|
x: Math.random() * (canvas.width - orangeProps.size),
|
||||||
|
y: -orangeProps.size,
|
||||||
|
size: orangeProps.size,
|
||||||
|
speed: orangeProps.speed + Math.random() * 100,
|
||||||
|
draw() {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(this.x + this.size / 2, this.y + this.size / 2, this.size, 0, Math.PI * 2);
|
||||||
|
ctx.fillStyle = '#FFA500';
|
||||||
|
ctx.fill();
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.ellipse(this.x + this.size / 2 + 5, this.y, 8, 3, Math.PI / 4, 0, Math.PI * 2);
|
||||||
|
ctx.fillStyle = '#228B22';
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let lastTime = 0;
|
||||||
|
|
||||||
|
function update(timestamp) {
|
||||||
|
if (!gameStarted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const ctx = canvas.getContext('2d');
|
|
||||||
|
|
||||||
// Set fixed canvas size
|
if (gameOver) {
|
||||||
canvas.width = 800;
|
drawGameOver();
|
||||||
canvas.height = 600;
|
return;
|
||||||
|
|
||||||
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
|
|
||||||
let score = 0;
|
|
||||||
let lives = 3;
|
|
||||||
let gameOver = false;
|
|
||||||
let gameStarted = false;
|
|
||||||
let orangeInterval;
|
|
||||||
|
|
||||||
const player = {
|
|
||||||
width: 100,
|
|
||||||
height: 50,
|
|
||||||
x: canvas.width / 2 - 50,
|
|
||||||
y: canvas.height - 60,
|
|
||||||
speed: 500,
|
|
||||||
dx: 0,
|
|
||||||
draw() {
|
|
||||||
ctx.fillStyle = '#8B4513';
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo(this.x, this.y);
|
|
||||||
ctx.lineTo(this.x + this.width, this.y);
|
|
||||||
ctx.lineTo(this.x + this.width - 15, this.y + this.height);
|
|
||||||
ctx.lineTo(this.x + 15, this.y + this.height);
|
|
||||||
ctx.closePath();
|
|
||||||
ctx.fill();
|
|
||||||
ctx.strokeStyle = '#5C2F0E';
|
|
||||||
ctx.lineWidth = 8;
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo(this.x, this.y);
|
|
||||||
ctx.lineTo(this.x + this.width, this.y);
|
|
||||||
ctx.stroke();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const orangeProps = {
|
|
||||||
size: 20,
|
|
||||||
speed: 150,
|
|
||||||
};
|
|
||||||
let oranges = [];
|
|
||||||
|
|
||||||
function createOrange() {
|
|
||||||
oranges.push({
|
|
||||||
x: Math.random() * (canvas.width - orangeProps.size),
|
|
||||||
y: -orangeProps.size,
|
|
||||||
size: orangeProps.size,
|
|
||||||
speed: orangeProps.speed + Math.random() * 100,
|
|
||||||
draw() {
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.arc(this.x + this.size / 2, this.y + this.size / 2, this.size, 0, Math.PI * 2);
|
|
||||||
ctx.fillStyle = '#FFA500';
|
|
||||||
ctx.fill();
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.ellipse(this.x + this.size / 2 + 5, this.y, 8, 3, Math.PI / 4, 0, Math.PI * 2);
|
|
||||||
ctx.fillStyle = '#228B22';
|
|
||||||
ctx.fill();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let lastTime = 0;
|
const deltaTime = (timestamp - lastTime) / 1000;
|
||||||
|
lastTime = timestamp;
|
||||||
|
|
||||||
function update(timestamp) {
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
if (!gameStarted) return;
|
drawScene();
|
||||||
|
player.draw();
|
||||||
|
player.x += player.dx * player.speed * deltaTime;
|
||||||
|
|
||||||
if (gameOver) {
|
if (player.x < 0) player.x = 0;
|
||||||
drawGameOver();
|
if (player.x + player.width > canvas.width) {
|
||||||
return;
|
player.x = canvas.width - player.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
oranges.forEach((orange, index) => {
|
||||||
|
orange.y += orange.speed * deltaTime;
|
||||||
|
orange.draw();
|
||||||
|
|
||||||
|
if (
|
||||||
|
orange.y + orange.size > player.y &&
|
||||||
|
orange.x < player.x + player.width &&
|
||||||
|
orange.x + orange.size > player.x
|
||||||
|
) {
|
||||||
|
score++;
|
||||||
|
playCatchSound();
|
||||||
|
oranges.splice(index, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const deltaTime = (timestamp - lastTime) / 1000;
|
if (orange.y > canvas.height) {
|
||||||
lastTime = timestamp;
|
lives--;
|
||||||
|
oranges.splice(index, 1);
|
||||||
|
if (lives <= 0) {
|
||||||
|
gameOver = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
drawUI();
|
||||||
|
requestAnimationFrame(update);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawScene() {
|
||||||
|
ctx.fillStyle = '#0A192F';
|
||||||
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
|
ctx.fillStyle = '#228B22';
|
||||||
|
ctx.fillRect(0, canvas.height - 20, canvas.width, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawUI() {
|
||||||
|
ctx.fillStyle = '#E6F1FF';
|
||||||
|
ctx.font = '24px Poppins, sans-serif';
|
||||||
|
ctx.textAlign = 'left';
|
||||||
|
ctx.fillText(`Score: ${score}`, 20, 40);
|
||||||
|
ctx.textAlign = 'right';
|
||||||
|
ctx.fillText(`Lives: ${lives}`, canvas.width - 20, 40);
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawGameOver() {
|
||||||
|
clearInterval(orangeInterval);
|
||||||
|
ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
|
||||||
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
|
ctx.fillStyle = '#E6F1FF';
|
||||||
|
ctx.font = '50px Poppins, sans-serif';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.fillText('GAME OVER', canvas.width / 2, canvas.height / 2 - 40);
|
||||||
|
ctx.font = '30px Poppins, sans-serif';
|
||||||
|
ctx.fillText(`Final Score: ${score}`, canvas.width / 2, canvas.height / 2 + 20);
|
||||||
|
ctx.font = '20px Poppins, sans-serif';
|
||||||
|
ctx.fillText('Click to Restart', canvas.width / 2, canvas.height / 2 + 70);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawInitialMessage() {
|
||||||
|
ctx.fillStyle = '#E6F1FF';
|
||||||
|
ctx.font = '30px Poppins, sans-serif';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.fillText('Click "Play Now" to Start!', canvas.width / 2, canvas.height / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function playCatchSound() {
|
||||||
|
if (!audioCtx) return;
|
||||||
|
const oscillator = audioCtx.createOscillator();
|
||||||
|
const gainNode = audioCtx.createGain();
|
||||||
|
oscillator.connect(gainNode);
|
||||||
|
gainNode.connect(audioCtx.destination);
|
||||||
|
oscillator.type = 'sine';
|
||||||
|
oscillator.frequency.setValueAtTime(880, audioCtx.currentTime);
|
||||||
|
gainNode.gain.setValueAtTime(0.1, audioCtx.currentTime);
|
||||||
|
gainNode.gain.exponentialRampToValueAtTime(0.00001, audioCtx.currentTime + 0.5);
|
||||||
|
oscillator.start(audioCtx.currentTime);
|
||||||
|
oscillator.stop(audioCtx.currentTime + 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateLeaderboard() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/scores/');
|
||||||
|
const scores = await response.json();
|
||||||
|
const leaderboardBody = document.getElementById('leaderboard-body');
|
||||||
|
leaderboardBody.innerHTML = '';
|
||||||
|
scores.forEach((score, index) => {
|
||||||
|
const row = `<tr>
|
||||||
|
<th scope="row">${index + 1}</th>
|
||||||
|
<td>${score.player_name}</td>
|
||||||
|
<td>${score.score}</td>
|
||||||
|
</tr>`;
|
||||||
|
leaderboardBody.innerHTML += row;
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error updating leaderboard:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function move(e) {
|
||||||
|
if (!gameStarted || gameOver) return;
|
||||||
|
if (e.key === 'ArrowRight' || e.key === 'd') {
|
||||||
|
player.dx = 1;
|
||||||
|
} else if (e.key === 'ArrowLeft' || e.key === 'a') {
|
||||||
|
player.dx = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopMove(e) {
|
||||||
|
if (['ArrowRight', 'd', 'ArrowLeft', 'a'].includes(e.key)) {
|
||||||
|
player.dx = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initGame() {
|
||||||
|
score = 0;
|
||||||
|
lives = 3;
|
||||||
|
oranges = [];
|
||||||
|
gameOver = false;
|
||||||
|
gameStarted = false;
|
||||||
|
player.x = canvas.width / 2 - player.width / 2;
|
||||||
|
drawScene();
|
||||||
|
drawInitialMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
function restartGame() {
|
||||||
|
score = 0;
|
||||||
|
lives = 3;
|
||||||
|
oranges = [];
|
||||||
|
gameOver = false;
|
||||||
|
player.x = canvas.width / 2 - player.width / 2;
|
||||||
|
startCountdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
function startCountdown() {
|
||||||
|
let count = 3;
|
||||||
|
const countdown = setInterval(() => {
|
||||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
drawScene();
|
drawScene();
|
||||||
player.draw();
|
|
||||||
player.x += player.dx * player.speed * deltaTime;
|
|
||||||
|
|
||||||
if (player.x < 0) player.x = 0;
|
|
||||||
if (player.x + player.width > canvas.width) {
|
|
||||||
player.x = canvas.width - player.width;
|
|
||||||
}
|
|
||||||
|
|
||||||
oranges.forEach((orange, index) => {
|
|
||||||
orange.y += orange.speed * deltaTime;
|
|
||||||
orange.draw();
|
|
||||||
|
|
||||||
if (
|
|
||||||
orange.y + orange.size > player.y &&
|
|
||||||
orange.x < player.x + player.width &&
|
|
||||||
orange.x + orange.size > player.x
|
|
||||||
) {
|
|
||||||
score++;
|
|
||||||
playCatchSound();
|
|
||||||
oranges.splice(index, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (orange.y > canvas.height) {
|
|
||||||
lives--;
|
|
||||||
oranges.splice(index, 1);
|
|
||||||
if (lives <= 0) {
|
|
||||||
gameOver = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
drawUI();
|
|
||||||
requestAnimationFrame(update);
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawScene() {
|
|
||||||
ctx.fillStyle = '#0A192F';
|
|
||||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
||||||
ctx.fillStyle = '#228B22';
|
|
||||||
ctx.fillRect(0, canvas.height - 20, canvas.width, 20);
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawUI() {
|
|
||||||
ctx.fillStyle = '#E6F1FF';
|
ctx.fillStyle = '#E6F1FF';
|
||||||
ctx.font = '24px Poppins, sans-serif';
|
ctx.font = '60px Poppins, sans-serif';
|
||||||
ctx.textAlign = 'left';
|
|
||||||
ctx.fillText(`Score: ${score}`, 20, 40);
|
|
||||||
ctx.textAlign = 'right';
|
|
||||||
ctx.fillText(`Lives: ${lives}`, canvas.width - 20, 40);
|
|
||||||
ctx.textAlign = 'center';
|
ctx.textAlign = 'center';
|
||||||
}
|
if (count > 0) {
|
||||||
|
ctx.fillText(count, canvas.width / 2, canvas.height / 2);
|
||||||
function drawGameOver() {
|
count--;
|
||||||
clearInterval(orangeInterval);
|
} else {
|
||||||
ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
|
clearInterval(countdown);
|
||||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
ctx.fillText('Go!', canvas.width / 2, canvas.height / 2);
|
||||||
ctx.fillStyle = '#E6F1FF';
|
setTimeout(startGame, 500);
|
||||||
ctx.font = '50px Poppins, sans-serif';
|
|
||||||
ctx.textAlign = 'center';
|
|
||||||
ctx.fillText('GAME OVER', canvas.width / 2, canvas.height / 2 - 40);
|
|
||||||
ctx.font = '30px Poppins, sans-serif';
|
|
||||||
ctx.fillText(`Final Score: ${score}`, canvas.width / 2, canvas.height / 2 + 20);
|
|
||||||
ctx.font = '20px Poppins, sans-serif';
|
|
||||||
ctx.fillText('Click to Restart', canvas.width / 2, canvas.height / 2 + 70);
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawInitialMessage() {
|
|
||||||
ctx.fillStyle = '#E6F1FF';
|
|
||||||
ctx.font = '30px Poppins, sans-serif';
|
|
||||||
ctx.textAlign = 'center';
|
|
||||||
ctx.fillText('Click "Play Now" to Start!', canvas.width / 2, canvas.height / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
function playCatchSound() {
|
|
||||||
if (!audioCtx) return;
|
|
||||||
const oscillator = audioCtx.createOscillator();
|
|
||||||
const gainNode = audioCtx.createGain();
|
|
||||||
oscillator.connect(gainNode);
|
|
||||||
gainNode.connect(audioCtx.destination);
|
|
||||||
oscillator.type = 'sine';
|
|
||||||
oscillator.frequency.setValueAtTime(880, audioCtx.currentTime);
|
|
||||||
gainNode.gain.setValueAtTime(0.1, audioCtx.currentTime);
|
|
||||||
gainNode.gain.exponentialRampToValueAtTime(0.00001, audioCtx.currentTime + 0.5);
|
|
||||||
oscillator.start(audioCtx.currentTime);
|
|
||||||
oscillator.stop(audioCtx.currentTime + 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateLeaderboard() {
|
|
||||||
try {
|
|
||||||
const response = await fetch('/api/scores/');
|
|
||||||
const scores = await response.json();
|
|
||||||
const leaderboardBody = document.getElementById('leaderboard-body');
|
|
||||||
leaderboardBody.innerHTML = '';
|
|
||||||
scores.forEach((score, index) => {
|
|
||||||
const row = `<tr>
|
|
||||||
<th scope="row">${index + 1}</th>
|
|
||||||
<td>${score.player_name}</td>
|
|
||||||
<td>${score.score}</td>
|
|
||||||
</tr>`;
|
|
||||||
leaderboardBody.innerHTML += row;
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error updating leaderboard:', error);
|
|
||||||
}
|
}
|
||||||
}
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
function move(e) {
|
function startGame() {
|
||||||
if (!gameStarted || gameOver) return;
|
gameStarted = true;
|
||||||
if (e.key === 'ArrowRight' || e.key === 'd') {
|
lastTime = performance.now();
|
||||||
player.dx = 1;
|
if (orangeInterval) clearInterval(orangeInterval);
|
||||||
} else if (e.key === 'ArrowLeft' || e.key === 'a') {
|
orangeInterval = setInterval(createOrange, 1200);
|
||||||
player.dx = -1;
|
requestAnimationFrame(update);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function stopMove(e) {
|
// Event Listeners
|
||||||
if (['ArrowRight', 'd', 'ArrowLeft', 'a'].includes(e.key)) {
|
document.addEventListener('keydown', move);
|
||||||
player.dx = 0;
|
document.addEventListener('keyup', stopMove);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function initGame() {
|
canvas.addEventListener('click', () => {
|
||||||
score = 0;
|
if (gameOver) {
|
||||||
lives = 3;
|
restartGame();
|
||||||
oranges = [];
|
|
||||||
gameOver = false;
|
|
||||||
gameStarted = false;
|
|
||||||
player.x = canvas.width / 2 - player.width / 2;
|
|
||||||
drawScene();
|
|
||||||
drawInitialMessage();
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
function restartGame() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
score = 0;
|
|
||||||
lives = 3;
|
|
||||||
oranges = [];
|
|
||||||
gameOver = false;
|
|
||||||
player.x = canvas.width / 2 - player.width / 2;
|
|
||||||
startCountdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
function startCountdown() {
|
|
||||||
let count = 3;
|
|
||||||
const countdown = setInterval(() => {
|
|
||||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
||||||
drawScene();
|
|
||||||
ctx.fillStyle = '#E6F1FF';
|
|
||||||
ctx.font = '60px Poppins, sans-serif';
|
|
||||||
ctx.textAlign = 'center';
|
|
||||||
if (count > 0) {
|
|
||||||
ctx.fillText(count, canvas.width / 2, canvas.height / 2);
|
|
||||||
count--;
|
|
||||||
} else {
|
|
||||||
clearInterval(countdown);
|
|
||||||
ctx.fillText('Go!', canvas.width / 2, canvas.height / 2);
|
|
||||||
setTimeout(startGame, 500);
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
function startGame() {
|
|
||||||
gameStarted = true;
|
|
||||||
lastTime = performance.now();
|
|
||||||
if (orangeInterval) clearInterval(orangeInterval);
|
|
||||||
orangeInterval = setInterval(createOrange, 1200);
|
|
||||||
requestAnimationFrame(update);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Event Listeners
|
|
||||||
document.addEventListener('keydown', move);
|
|
||||||
document.addEventListener('keyup', stopMove);
|
|
||||||
|
|
||||||
const playButton = document.querySelector('.btn-play');
|
const playButton = document.querySelector('.btn-play');
|
||||||
const gameArea = document.getElementById('game-area');
|
const gameArea = document.getElementById('game-area');
|
||||||
|
|
||||||
if (playButton && gameArea) {
|
if (playButton && gameArea) {
|
||||||
playButton.addEventListener('click', (e) => {
|
playButton.addEventListener('click', function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
gameArea.scrollIntoView({ behavior: 'smooth' });
|
gameArea.scrollIntoView({ behavior: 'smooth' });
|
||||||
if (!gameStarted && !gameOver) {
|
startCountdown();
|
||||||
startCountdown();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
console.error('Play button or game area not found!');
|
||||||
}
|
}
|
||||||
|
});
|
||||||
canvas.addEventListener('click', () => {
|
|
||||||
if (gameOver) {
|
|
||||||
restartGame();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Initial Setup
|
// Initial Setup
|
||||||
initGame();
|
initGame();
|
||||||
updateLeaderboard();
|
updateLeaderboard();
|
||||||
});
|
|
||||||
|
|||||||
@ -1,279 +1,280 @@
|
|||||||
document.addEventListener('DOMContentLoaded', () => {
|
const canvas = document.getElementById('gameCanvas');
|
||||||
const canvas = document.getElementById('gameCanvas');
|
if (!canvas) {
|
||||||
if (!canvas) {
|
console.error('Canvas element not found!');
|
||||||
console.error('Canvas element not found!');
|
}
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
// Set fixed canvas size
|
||||||
|
canvas.width = 800;
|
||||||
|
canvas.height = 600;
|
||||||
|
|
||||||
|
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
|
||||||
|
let score = 0;
|
||||||
|
let lives = 3;
|
||||||
|
let gameOver = false;
|
||||||
|
let gameStarted = false;
|
||||||
|
let orangeInterval;
|
||||||
|
|
||||||
|
const player = {
|
||||||
|
width: 100,
|
||||||
|
height: 50,
|
||||||
|
x: canvas.width / 2 - 50,
|
||||||
|
y: canvas.height - 60,
|
||||||
|
speed: 500,
|
||||||
|
dx: 0,
|
||||||
|
draw() {
|
||||||
|
ctx.fillStyle = '#8B4513';
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(this.x, this.y);
|
||||||
|
ctx.lineTo(this.x + this.width, this.y);
|
||||||
|
ctx.lineTo(this.x + this.width - 15, this.y + this.height);
|
||||||
|
ctx.lineTo(this.x + 15, this.y + this.height);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.fill();
|
||||||
|
ctx.strokeStyle = '#5C2F0E';
|
||||||
|
ctx.lineWidth = 8;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(this.x, this.y);
|
||||||
|
ctx.lineTo(this.x + this.width, this.y);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const orangeProps = {
|
||||||
|
size: 20,
|
||||||
|
speed: 150,
|
||||||
|
};
|
||||||
|
let oranges = [];
|
||||||
|
|
||||||
|
function createOrange() {
|
||||||
|
oranges.push({
|
||||||
|
x: Math.random() * (canvas.width - orangeProps.size),
|
||||||
|
y: -orangeProps.size,
|
||||||
|
size: orangeProps.size,
|
||||||
|
speed: orangeProps.speed + Math.random() * 100,
|
||||||
|
draw() {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(this.x + this.size / 2, this.y + this.size / 2, this.size, 0, Math.PI * 2);
|
||||||
|
ctx.fillStyle = '#FFA500';
|
||||||
|
ctx.fill();
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.ellipse(this.x + this.size / 2 + 5, this.y, 8, 3, Math.PI / 4, 0, Math.PI * 2);
|
||||||
|
ctx.fillStyle = '#228B22';
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let lastTime = 0;
|
||||||
|
|
||||||
|
function update(timestamp) {
|
||||||
|
if (!gameStarted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const ctx = canvas.getContext('2d');
|
|
||||||
|
|
||||||
// Set fixed canvas size
|
if (gameOver) {
|
||||||
canvas.width = 800;
|
drawGameOver();
|
||||||
canvas.height = 600;
|
return;
|
||||||
|
|
||||||
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
|
|
||||||
let score = 0;
|
|
||||||
let lives = 3;
|
|
||||||
let gameOver = false;
|
|
||||||
let gameStarted = false;
|
|
||||||
let orangeInterval;
|
|
||||||
|
|
||||||
const player = {
|
|
||||||
width: 100,
|
|
||||||
height: 50,
|
|
||||||
x: canvas.width / 2 - 50,
|
|
||||||
y: canvas.height - 60,
|
|
||||||
speed: 500,
|
|
||||||
dx: 0,
|
|
||||||
draw() {
|
|
||||||
ctx.fillStyle = '#8B4513';
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo(this.x, this.y);
|
|
||||||
ctx.lineTo(this.x + this.width, this.y);
|
|
||||||
ctx.lineTo(this.x + this.width - 15, this.y + this.height);
|
|
||||||
ctx.lineTo(this.x + 15, this.y + this.height);
|
|
||||||
ctx.closePath();
|
|
||||||
ctx.fill();
|
|
||||||
ctx.strokeStyle = '#5C2F0E';
|
|
||||||
ctx.lineWidth = 8;
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo(this.x, this.y);
|
|
||||||
ctx.lineTo(this.x + this.width, this.y);
|
|
||||||
ctx.stroke();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const orangeProps = {
|
|
||||||
size: 20,
|
|
||||||
speed: 150,
|
|
||||||
};
|
|
||||||
let oranges = [];
|
|
||||||
|
|
||||||
function createOrange() {
|
|
||||||
oranges.push({
|
|
||||||
x: Math.random() * (canvas.width - orangeProps.size),
|
|
||||||
y: -orangeProps.size,
|
|
||||||
size: orangeProps.size,
|
|
||||||
speed: orangeProps.speed + Math.random() * 100,
|
|
||||||
draw() {
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.arc(this.x + this.size / 2, this.y + this.size / 2, this.size, 0, Math.PI * 2);
|
|
||||||
ctx.fillStyle = '#FFA500';
|
|
||||||
ctx.fill();
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.ellipse(this.x + this.size / 2 + 5, this.y, 8, 3, Math.PI / 4, 0, Math.PI * 2);
|
|
||||||
ctx.fillStyle = '#228B22';
|
|
||||||
ctx.fill();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let lastTime = 0;
|
const deltaTime = (timestamp - lastTime) / 1000;
|
||||||
|
lastTime = timestamp;
|
||||||
|
|
||||||
function update(timestamp) {
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
if (!gameStarted) return;
|
drawScene();
|
||||||
|
player.draw();
|
||||||
|
player.x += player.dx * player.speed * deltaTime;
|
||||||
|
|
||||||
if (gameOver) {
|
if (player.x < 0) player.x = 0;
|
||||||
drawGameOver();
|
if (player.x + player.width > canvas.width) {
|
||||||
return;
|
player.x = canvas.width - player.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
oranges.forEach((orange, index) => {
|
||||||
|
orange.y += orange.speed * deltaTime;
|
||||||
|
orange.draw();
|
||||||
|
|
||||||
|
if (
|
||||||
|
orange.y + orange.size > player.y &&
|
||||||
|
orange.x < player.x + player.width &&
|
||||||
|
orange.x + orange.size > player.x
|
||||||
|
) {
|
||||||
|
score++;
|
||||||
|
playCatchSound();
|
||||||
|
oranges.splice(index, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const deltaTime = (timestamp - lastTime) / 1000;
|
if (orange.y > canvas.height) {
|
||||||
lastTime = timestamp;
|
lives--;
|
||||||
|
oranges.splice(index, 1);
|
||||||
|
if (lives <= 0) {
|
||||||
|
gameOver = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
drawUI();
|
||||||
|
requestAnimationFrame(update);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawScene() {
|
||||||
|
ctx.fillStyle = '#0A192F';
|
||||||
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
|
ctx.fillStyle = '#228B22';
|
||||||
|
ctx.fillRect(0, canvas.height - 20, canvas.width, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawUI() {
|
||||||
|
ctx.fillStyle = '#E6F1FF';
|
||||||
|
ctx.font = '24px Poppins, sans-serif';
|
||||||
|
ctx.textAlign = 'left';
|
||||||
|
ctx.fillText(`Score: ${score}`, 20, 40);
|
||||||
|
ctx.textAlign = 'right';
|
||||||
|
ctx.fillText(`Lives: ${lives}`, canvas.width - 20, 40);
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawGameOver() {
|
||||||
|
clearInterval(orangeInterval);
|
||||||
|
ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
|
||||||
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
|
ctx.fillStyle = '#E6F1FF';
|
||||||
|
ctx.font = '50px Poppins, sans-serif';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.fillText('GAME OVER', canvas.width / 2, canvas.height / 2 - 40);
|
||||||
|
ctx.font = '30px Poppins, sans-serif';
|
||||||
|
ctx.fillText(`Final Score: ${score}`, canvas.width / 2, canvas.height / 2 + 20);
|
||||||
|
ctx.font = '20px Poppins, sans-serif';
|
||||||
|
ctx.fillText('Click to Restart', canvas.width / 2, canvas.height / 2 + 70);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawInitialMessage() {
|
||||||
|
ctx.fillStyle = '#E6F1FF';
|
||||||
|
ctx.font = '30px Poppins, sans-serif';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.fillText('Click "Play Now" to Start!', canvas.width / 2, canvas.height / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function playCatchSound() {
|
||||||
|
if (!audioCtx) return;
|
||||||
|
const oscillator = audioCtx.createOscillator();
|
||||||
|
const gainNode = audioCtx.createGain();
|
||||||
|
oscillator.connect(gainNode);
|
||||||
|
gainNode.connect(audioCtx.destination);
|
||||||
|
oscillator.type = 'sine';
|
||||||
|
oscillator.frequency.setValueAtTime(880, audioCtx.currentTime);
|
||||||
|
gainNode.gain.setValueAtTime(0.1, audioCtx.currentTime);
|
||||||
|
gainNode.gain.exponentialRampToValueAtTime(0.00001, audioCtx.currentTime + 0.5);
|
||||||
|
oscillator.start(audioCtx.currentTime);
|
||||||
|
oscillator.stop(audioCtx.currentTime + 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateLeaderboard() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/scores/');
|
||||||
|
const scores = await response.json();
|
||||||
|
const leaderboardBody = document.getElementById('leaderboard-body');
|
||||||
|
leaderboardBody.innerHTML = '';
|
||||||
|
scores.forEach((score, index) => {
|
||||||
|
const row = `<tr>
|
||||||
|
<th scope="row">${index + 1}</th>
|
||||||
|
<td>${score.player_name}</td>
|
||||||
|
<td>${score.score}</td>
|
||||||
|
</tr>`;
|
||||||
|
leaderboardBody.innerHTML += row;
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error updating leaderboard:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function move(e) {
|
||||||
|
if (!gameStarted || gameOver) return;
|
||||||
|
if (e.key === 'ArrowRight' || e.key === 'd') {
|
||||||
|
player.dx = 1;
|
||||||
|
} else if (e.key === 'ArrowLeft' || e.key === 'a') {
|
||||||
|
player.dx = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopMove(e) {
|
||||||
|
if (['ArrowRight', 'd', 'ArrowLeft', 'a'].includes(e.key)) {
|
||||||
|
player.dx = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initGame() {
|
||||||
|
score = 0;
|
||||||
|
lives = 3;
|
||||||
|
oranges = [];
|
||||||
|
gameOver = false;
|
||||||
|
gameStarted = false;
|
||||||
|
player.x = canvas.width / 2 - player.width / 2;
|
||||||
|
drawScene();
|
||||||
|
drawInitialMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
function restartGame() {
|
||||||
|
score = 0;
|
||||||
|
lives = 3;
|
||||||
|
oranges = [];
|
||||||
|
gameOver = false;
|
||||||
|
player.x = canvas.width / 2 - player.width / 2;
|
||||||
|
startCountdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
function startCountdown() {
|
||||||
|
let count = 3;
|
||||||
|
const countdown = setInterval(() => {
|
||||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
drawScene();
|
drawScene();
|
||||||
player.draw();
|
|
||||||
player.x += player.dx * player.speed * deltaTime;
|
|
||||||
|
|
||||||
if (player.x < 0) player.x = 0;
|
|
||||||
if (player.x + player.width > canvas.width) {
|
|
||||||
player.x = canvas.width - player.width;
|
|
||||||
}
|
|
||||||
|
|
||||||
oranges.forEach((orange, index) => {
|
|
||||||
orange.y += orange.speed * deltaTime;
|
|
||||||
orange.draw();
|
|
||||||
|
|
||||||
if (
|
|
||||||
orange.y + orange.size > player.y &&
|
|
||||||
orange.x < player.x + player.width &&
|
|
||||||
orange.x + orange.size > player.x
|
|
||||||
) {
|
|
||||||
score++;
|
|
||||||
playCatchSound();
|
|
||||||
oranges.splice(index, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (orange.y > canvas.height) {
|
|
||||||
lives--;
|
|
||||||
oranges.splice(index, 1);
|
|
||||||
if (lives <= 0) {
|
|
||||||
gameOver = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
drawUI();
|
|
||||||
requestAnimationFrame(update);
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawScene() {
|
|
||||||
ctx.fillStyle = '#0A192F';
|
|
||||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
||||||
ctx.fillStyle = '#228B22';
|
|
||||||
ctx.fillRect(0, canvas.height - 20, canvas.width, 20);
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawUI() {
|
|
||||||
ctx.fillStyle = '#E6F1FF';
|
ctx.fillStyle = '#E6F1FF';
|
||||||
ctx.font = '24px Poppins, sans-serif';
|
ctx.font = '60px Poppins, sans-serif';
|
||||||
ctx.textAlign = 'left';
|
|
||||||
ctx.fillText(`Score: ${score}`, 20, 40);
|
|
||||||
ctx.textAlign = 'right';
|
|
||||||
ctx.fillText(`Lives: ${lives}`, canvas.width - 20, 40);
|
|
||||||
ctx.textAlign = 'center';
|
ctx.textAlign = 'center';
|
||||||
}
|
if (count > 0) {
|
||||||
|
ctx.fillText(count, canvas.width / 2, canvas.height / 2);
|
||||||
function drawGameOver() {
|
count--;
|
||||||
clearInterval(orangeInterval);
|
} else {
|
||||||
ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
|
clearInterval(countdown);
|
||||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
ctx.fillText('Go!', canvas.width / 2, canvas.height / 2);
|
||||||
ctx.fillStyle = '#E6F1FF';
|
setTimeout(startGame, 500);
|
||||||
ctx.font = '50px Poppins, sans-serif';
|
|
||||||
ctx.textAlign = 'center';
|
|
||||||
ctx.fillText('GAME OVER', canvas.width / 2, canvas.height / 2 - 40);
|
|
||||||
ctx.font = '30px Poppins, sans-serif';
|
|
||||||
ctx.fillText(`Final Score: ${score}`, canvas.width / 2, canvas.height / 2 + 20);
|
|
||||||
ctx.font = '20px Poppins, sans-serif';
|
|
||||||
ctx.fillText('Click to Restart', canvas.width / 2, canvas.height / 2 + 70);
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawInitialMessage() {
|
|
||||||
ctx.fillStyle = '#E6F1FF';
|
|
||||||
ctx.font = '30px Poppins, sans-serif';
|
|
||||||
ctx.textAlign = 'center';
|
|
||||||
ctx.fillText('Click "Play Now" to Start!', canvas.width / 2, canvas.height / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
function playCatchSound() {
|
|
||||||
if (!audioCtx) return;
|
|
||||||
const oscillator = audioCtx.createOscillator();
|
|
||||||
const gainNode = audioCtx.createGain();
|
|
||||||
oscillator.connect(gainNode);
|
|
||||||
gainNode.connect(audioCtx.destination);
|
|
||||||
oscillator.type = 'sine';
|
|
||||||
oscillator.frequency.setValueAtTime(880, audioCtx.currentTime);
|
|
||||||
gainNode.gain.setValueAtTime(0.1, audioCtx.currentTime);
|
|
||||||
gainNode.gain.exponentialRampToValueAtTime(0.00001, audioCtx.currentTime + 0.5);
|
|
||||||
oscillator.start(audioCtx.currentTime);
|
|
||||||
oscillator.stop(audioCtx.currentTime + 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateLeaderboard() {
|
|
||||||
try {
|
|
||||||
const response = await fetch('/api/scores/');
|
|
||||||
const scores = await response.json();
|
|
||||||
const leaderboardBody = document.getElementById('leaderboard-body');
|
|
||||||
leaderboardBody.innerHTML = '';
|
|
||||||
scores.forEach((score, index) => {
|
|
||||||
const row = `<tr>
|
|
||||||
<th scope="row">${index + 1}</th>
|
|
||||||
<td>${score.player_name}</td>
|
|
||||||
<td>${score.score}</td>
|
|
||||||
</tr>`;
|
|
||||||
leaderboardBody.innerHTML += row;
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error updating leaderboard:', error);
|
|
||||||
}
|
}
|
||||||
}
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
function move(e) {
|
function startGame() {
|
||||||
if (!gameStarted || gameOver) return;
|
gameStarted = true;
|
||||||
if (e.key === 'ArrowRight' || e.key === 'd') {
|
lastTime = performance.now();
|
||||||
player.dx = 1;
|
if (orangeInterval) clearInterval(orangeInterval);
|
||||||
} else if (e.key === 'ArrowLeft' || e.key === 'a') {
|
orangeInterval = setInterval(createOrange, 1200);
|
||||||
player.dx = -1;
|
requestAnimationFrame(update);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function stopMove(e) {
|
// Event Listeners
|
||||||
if (['ArrowRight', 'd', 'ArrowLeft', 'a'].includes(e.key)) {
|
document.addEventListener('keydown', move);
|
||||||
player.dx = 0;
|
document.addEventListener('keyup', stopMove);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function initGame() {
|
canvas.addEventListener('click', () => {
|
||||||
score = 0;
|
if (gameOver) {
|
||||||
lives = 3;
|
restartGame();
|
||||||
oranges = [];
|
|
||||||
gameOver = false;
|
|
||||||
gameStarted = false;
|
|
||||||
player.x = canvas.width / 2 - player.width / 2;
|
|
||||||
drawScene();
|
|
||||||
drawInitialMessage();
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
function restartGame() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
score = 0;
|
|
||||||
lives = 3;
|
|
||||||
oranges = [];
|
|
||||||
gameOver = false;
|
|
||||||
player.x = canvas.width / 2 - player.width / 2;
|
|
||||||
startCountdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
function startCountdown() {
|
|
||||||
let count = 3;
|
|
||||||
const countdown = setInterval(() => {
|
|
||||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
||||||
drawScene();
|
|
||||||
ctx.fillStyle = '#E6F1FF';
|
|
||||||
ctx.font = '60px Poppins, sans-serif';
|
|
||||||
ctx.textAlign = 'center';
|
|
||||||
if (count > 0) {
|
|
||||||
ctx.fillText(count, canvas.width / 2, canvas.height / 2);
|
|
||||||
count--;
|
|
||||||
} else {
|
|
||||||
clearInterval(countdown);
|
|
||||||
ctx.fillText('Go!', canvas.width / 2, canvas.height / 2);
|
|
||||||
setTimeout(startGame, 500);
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
function startGame() {
|
|
||||||
gameStarted = true;
|
|
||||||
lastTime = performance.now();
|
|
||||||
if (orangeInterval) clearInterval(orangeInterval);
|
|
||||||
orangeInterval = setInterval(createOrange, 1200);
|
|
||||||
requestAnimationFrame(update);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Event Listeners
|
|
||||||
document.addEventListener('keydown', move);
|
|
||||||
document.addEventListener('keyup', stopMove);
|
|
||||||
|
|
||||||
const playButton = document.querySelector('.btn-play');
|
const playButton = document.querySelector('.btn-play');
|
||||||
const gameArea = document.getElementById('game-area');
|
const gameArea = document.getElementById('game-area');
|
||||||
|
|
||||||
if (playButton && gameArea) {
|
if (playButton && gameArea) {
|
||||||
playButton.addEventListener('click', (e) => {
|
playButton.addEventListener('click', function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
gameArea.scrollIntoView({ behavior: 'smooth' });
|
gameArea.scrollIntoView({ behavior: 'smooth' });
|
||||||
if (!gameStarted && !gameOver) {
|
startCountdown();
|
||||||
startCountdown();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
console.error('Play button or game area not found!');
|
||||||
}
|
}
|
||||||
|
});
|
||||||
canvas.addEventListener('click', () => {
|
|
||||||
if (gameOver) {
|
|
||||||
restartGame();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Initial Setup
|
// Initial Setup
|
||||||
initGame();
|
initGame();
|
||||||
updateLeaderboard();
|
updateLeaderboard();
|
||||||
});
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user