Meta Description" name="description" />
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>aa Game Clone</title>
<style>
body {
margin: 0;
background-color: #f5f5f5;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
font-family: Arial, sans-serif;
overflow: hidden;
user-select: none;
}
#gameContainer {
position: relative;
width: 100%;
max-width: 400px;
height: 100%;
max-height: 600px;
background-color: #ffffff;
box-shadow: 0 0 20px rgba(0,0,0,0.1);
}
canvas {
display: block;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div id="gameContainer">
<canvas id="gameCanvas" width="400" height="600"></canvas>
</div>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
// Game Core State
const centerX = canvas.width / 2;
const centerY = canvas.height * 0.4;
const mainRadius = 50;
const pinRadius = 8;
const lineLength = 120;
const shootY = canvas.height * 0.85;
let rotationAngle = 0;
let rotationSpeed = 0.02; // Change this to increase difficulty
let attachedPins = []; // Pins fixed to the central wheel [{angle: 0}, ...]
let pinsLeft = 12; // Total number of pins to shoot to win
let currentShootingPin = null; // Currently flying pin
let isGameOver = false;
let isGameWon = false;
// Initialize level with some starting pins
function initGame() {
rotationAngle = 0;
isGameOver = false;
isGameWon = false;
pinsLeft = 12;
currentShootingPin = null;
// Add 4 pre-attached starting pins
attachedPins = [
{ angle: 0 },
{ angle: Math.PI / 2 },
{ angle: Math.PI },
{ angle: (Math.PI * 3) / 2 }
];
}
// Input Handler: Tap/Click anywhere to shoot
window.addEventListener('pointerdown', (e) => {
e.preventDefault();
if (isGameOver || isGameWon) {
initGame(); // Restart game if over
return;
}
if (!currentShootingPin && pinsLeft > 0) {
currentShootingPin = { y: shootY };
pinsLeft--;
}
});
// Main Game Loop
function update() {
// 1. Rotate the wheel
if (!isGameOver) {
rotationAngle += rotationSpeed;
if (rotationAngle > Math.PI * 2) rotationAngle -= Math.PI * 2;
}
// 2. Move flying pin
if (currentShootingPin) {
currentShootingPin.y -= 15; // Flight speed
// Check if pin reached the wheel
const targetY = centerY + lineLength;
if (currentShootingPin.y <= targetY) {
// Calculate relative angle when hitting the spinning wheel
const targetAngle = Math.PI / 2 - rotationAngle;
// Collision Detection: Check against all attached pins
let collision = false;
const minDistance = (pinRadius * 2.2) / lineLength; // Angle threshold for collision
for (let i = 0; i < attachedPins.length; i++) {
let angleDiff = Math.abs(targetAngle - attachedPins[i].angle);
// Normalize angle difference
angleDiff = angleDiff % (Math.PI * 2);
if (angleDiff > Math.PI) angleDiff = Math.PI * 2 - angleDiff;
if (angleDiff < minDistance) {
collision = true;
break;
}
}
if (collision) {
isGameOver = true;
currentShootingPin = null;
} else {
// Successfully attached pin
attachedPins.push({ angle: targetAngle });
currentShootingPin = null;
// Win Condition check
if (pinsLeft === 0) {
isGameWon = true;
}
}
}
}
}
function draw() {
// Clear frame
ctx.fillStyle = isGameOver ? '#ffdad9' : (isGameWon ? '#d4edda' : '#ffffff');
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw Attached Pins (Lines and Head)
attachedPins.forEach(pin => {
const currentAngle = pin.angle + rotationAngle;
const endX = centerX + Math.cos(currentAngle) * lineLength;
const endY = centerY + Math.sin(currentAngle) * lineLength;
ctx.beginPath();
ctx.moveTo(centerX, centerY);
ctx.lineTo(endX, endY);
ctx.strokeStyle = '#111111';
ctx.lineWidth = 2;
ctx.stroke();
ctx.beginPath();
ctx.arc(endX, endY, pinRadius, 0, Math.PI * 2);
ctx.fillStyle = '#111111';
ctx.fill();
});
// Draw Big Central Wheel
ctx.beginPath();
ctx.arc(centerX, centerY, mainRadius, 0, Math.PI * 2);
ctx.fillStyle = '#111111';
ctx.fill();
// Level text in Center
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 24px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText("aa", centerX, centerY);
// Draw Flying Pin
if (currentShootingPin) {
ctx.beginPath();
ctx.arc(centerX, currentShootingPin.y, pinRadius, 0, Math.PI * 2);
ctx.fillStyle = '#111111';
ctx.fill();
}
// Draw Waiting Pins Reserve Queue
const pinsToDraw = pinsLeft + (currentShootingPin ? 1 : 0);
const displayLimit = Math.min(pinsToDraw, 5); // Max preview items
for (let i = 0; i < displayLimit; i++) {
const offset = i * (pinRadius * 2.5);
ctx.beginPath();
ctx.arc(centerX, shootY + offset, pinRadius, 0, Math.PI * 2);
ctx.fillStyle = '#111111';
ctx.fill();
// Counter number overlay inside the bottom main pin
if (i === 0 && !currentShootingPin) {
ctx.fillStyle = '#ffffff';
ctx.font = '10px Arial';
ctx.fillText(pinsLeft, centerX, shootY);
}
}
// Status Messages Overlay
if (isGameOver || isGameWon) {
ctx.fillStyle = 'rgba(0, 0, 0, 0.6)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 30px Arial';
ctx.fillText(isGameWon ? "LEVEL CLEARED! π" : "GAME OVER π₯", centerX, centerY + 190);
ctx.font = '16px Arial';
ctx.fillText("Tap anywhere to restart", centerX, centerY + 230);
}
}
function loop() {
update();
draw();
requestAnimationFrame(loop);
}
// Run Game
initGame();
loop();
</script>
</body>
</html>
1
1
8KB
8KB
96.0ms
136.0ms
96.0ms