anycoder-a31911e4 / index.html
Mousco's picture
Upload folder using huggingface_hub
e0474c2 verified
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Catch Up: Neon Chase</title>
<!-- Importation de la police Google Fonts (Orbitron pour le look Sci-Fi) -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=Roboto:wght@300;400&display=swap" rel="stylesheet">
<!-- Importation de FontAwesome pour les icônes -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--bg-color: #0b0c15;
--primary-color: #00f3ff; /* Cyan Néon */
--secondary-color: #ff0055; /* Rouge Néon */
--accent-color: #00ff88; /* Vert Néon */
--text-color: #ffffff;
--glass-bg: rgba(255, 255, 255, 0.05);
--glass-border: rgba(255, 255, 255, 0.1);
--font-display: 'Orbitron', sans-serif;
--font-body: 'Roboto', sans-serif;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
user-select: none; /* Empêche la sélection de texte pendant le jeu */
-webkit-user-select: none;
}
body {
background-color: var(--bg-color);
color: var(--text-color);
font-family: var(--font-body);
overflow: hidden; /* Empêche le défilement */
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
}
/* --- Header --- */
header {
position: absolute;
top: 0;
left: 0;
width: 100%;
padding: 1rem 2rem;
display: flex;
justify-content: space-between;
align-items: center;
z-index: 100;
pointer-events: none; /* Laisse passer les clics vers le canvas si besoin */
}
.brand {
font-family: var(--font-display);
font-size: 1.5rem;
font-weight: 900;
text-transform: uppercase;
letter-spacing: 2px;
color: var(--primary-color);
text-shadow: 0 0 10px var(--primary-color);
pointer-events: auto;
}
.credits {
font-size: 0.9rem;
opacity: 0.8;
pointer-events: auto;
}
.credits a {
color: var(--accent-color);
text-decoration: none;
font-weight: bold;
transition: color 0.3s ease;
}
.credits a:hover {
color: var(--primary-color);
text-shadow: 0 0 8px var(--primary-color);
}
/* --- Canvas Container --- */
#game-container {
position: relative;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
canvas {
display: block;
box-shadow: 0 0 50px rgba(0, 0, 0, 0.5);
}
/* --- UI Overlays (Start / Game Over) --- */
.overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(11, 12, 21, 0.85);
backdrop-filter: blur(8px);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 50;
transition: opacity 0.4s ease;
}
.overlay.hidden {
opacity: 0;
pointer-events: none;
}
.menu-card {
background: var(--glass-bg);
border: 1px solid var(--glass-border);
padding: 3rem;
border-radius: 20px;
text-align: center;
box-shadow: 0 0 30px rgba(0, 243, 255, 0.1);
max-width: 500px;
width: 90%;
animation: float 6s ease-in-out infinite;
}
h1 {
font-family: var(--font-display);
font-size: 3rem;
margin-bottom: 0.5rem;
background: linear-gradient(90deg, var(--primary-color), var(--accent-color));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
p.subtitle {
font-size: 1.1rem;
margin-bottom: 2rem;
color: #aaa;
}
.score-display {
font-family: var(--font-display);
font-size: 4rem;
margin: 1rem 0;
color: var(--text-color);
}
.instructions {
margin-bottom: 2rem;
font-size: 0.95rem;
line-height: 1.6;
color: #ddd;
text-align: left;
background: rgba(0,0,0,0.3);
padding: 1rem;
border-radius: 8px;
}
.instructions ul {
list-style: none;
}
.instructions li {
margin-bottom: 0.5rem;
}
.instructions i {
margin-right: 10px;
width: 20px;
text-align: center;
}
.btn {
background: linear-gradient(45deg, var(--primary-color), #00a8ff);
color: #000;
font-family: var(--font-display);
font-weight: 700;
font-size: 1.2rem;
padding: 1rem 3rem;
border: none;
border-radius: 50px;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
text-transform: uppercase;
letter-spacing: 1px;
}
.btn:hover {
transform: translateY(-3px);
box-shadow: 0 0 20px var(--primary-color);
}
.btn:active {
transform: translateY(1px);
}
/* --- HUD In-Game --- */
#hud {
position: absolute;
top: 20px;
width: 100%;
padding: 0 40px;
display: flex;
justify-content: space-between;
pointer-events: none;
z-index: 10;
}
.hud-item {
font-family: var(--font-display);
font-size: 1.5rem;
color: var(--text-color);
text-shadow: 0 0 5px rgba(0,0,0,0.5);
}
.hud-label {
font-size: 0.8rem;
color: #888;
display: block;
text-transform: uppercase;
}
/* --- Animations --- */
@keyframes float {
0% { transform: translateY(0px); }
50% { transform: translateY(-10px); }
100% { transform: translateY(0px); }
}
/* --- Mobile Adjustments --- */
@media (max-width: 600px) {
h1 { font-size: 2rem; }
.menu-card { padding: 1.5rem; }
#hud { padding: 0 20px; }
.hud-item { font-size: 1.2rem; }
header { padding: 1rem; }
.brand { font-size: 1.1rem; }
}
</style>
</head>
<body>
<!-- Header avec le lien requis -->
<header>
<div class="brand">
<i class="fa-solid fa-bolt"></i> Catch Up
</div>
<div class="credits">
Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank">anycoder</a>
</div>
</header>
<!-- HUD (Score en jeu) -->
<div id="hud" class="hidden">
<div class="hud-item">
<span class="hud-label">Score</span>
<span id="current-score">0</span>
</div>
<div class="hud-item" style="text-align: right;">
<span class="hud-label">Record</span>
<span id="high-score">0</span>
</div>
</div>
<!-- Conteneur principal du jeu -->
<div id="game-container">
<canvas id="gameCanvas"></canvas>
<!-- Écran de Démarrage -->
<div id="start-screen" class="overlay">
<div class="menu-card">
<h1>Catch Up</h1>
<p class="subtitle">Neon Chase Edition</p>
<div class="instructions">
<ul>
<li><i class="fa-solid fa-computer-mouse" style="color: var(--primary-color)"></i> Bougez la souris ou glissez pour contrôler le point bleu.</li>
<li><i class="fa-solid fa-bullseye" style="color: var(--accent-color)"></i> Attrapez les orbes <strong>VERTS</strong> pour gagner.</li>
<li><i class="fa-solid fa-skull" style="color: var(--secondary-color)"></i> Évitez les triangles <strong>ROUGES</strong>.</li>
<li><i class="fa-solid fa-gauge-high"></i> La vitesse augmente à chaque point !</li>
</ul>
</div>
<button class="btn" id="start-btn">Jouer</button>
</div>
</div>
<!-- Écran Game Over -->
<div id="game-over-screen" class="overlay hidden">
<div class="menu-card">
<h1 style="color: var(--secondary-color);">Partie Terminée</h1>
<p class="subtitle">Vous avez été attrapé !</p>
<div class="score-display" id="final-score">0</div>
<p style="margin-bottom: 2rem; color: #888;">Meilleur Score: <span id="final-high-score">0</span></p>
<button class="btn" id="restart-btn">Réessayer</button>
</div>
</div>
</div>
<script>
/**
* Logique du Jeu "Catch Up"
* Utilise HTML5 Canvas pour le rendu
*/
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
// Éléments UI
const startScreen = document.getElementById('start-screen');
const gameOverScreen = document.getElementById('game-over-screen');
const hud = document.getElementById('hud');
const currentScoreEl = document.getElementById('current-score');
const highScoreEl = document.getElementById('high-score');
const finalScoreEl = document.getElementById('final-score');
const finalHighScoreEl = document.getElementById('final-high-score');
const startBtn = document.getElementById('start-btn');
const restartBtn = document.getElementById('restart-btn');
// État du jeu
let gameRunning = false;
let score = 0;
let highScore = localStorage.getItem('catchUpHighScore') || 0;
let animationId;
let frameCount = 0;
// Configuration
const config = {
playerSpeed: 0.15, // Lerp factor
enemyBaseSpeed: 2,
enemySpeedIncrement: 0.2,
colors: {
player: '#00f3ff',
target: '#00ff88',
enemy: '#ff0055',
particle: '#ffffff'
}
};
// Dimensions du canevas
let width, height;
function resize() {
width = window.innerWidth;
height = window.innerHeight;
canvas.width = width;
canvas.height = height;
}
window.addEventListener('resize', resize);
resize();
// --- Classes du Jeu ---
class Player {
constructor() {
this.x = width / 2;
this.y = height / 2;
this.radius = 15;
this.targetX = this.x;
this.targetY = this.y;
this.trail = []; // Queue visuelle
}
update(mouseX, mouseY) {
// Lissage du mouvement (Lerp)
if (mouseX !== undefined && mouseY !== undefined) {
this.targetX = mouseX;
this.targetY = mouseY;
}
this.x += (this.targetX - this.x) * config.playerSpeed;
this.y += (this.targetY - this.y) * config.playerSpeed;
// Ajout à la traînée
this.trail.push({ x: this.x, y: this.y, alpha: 1.0 });
if (this.trail.length > 20) {
this.trail.shift();
}
// Mise à jour de l'alpha de la traînée
this.trail.forEach(t => t.alpha -= 0.05);
}
draw() {
// Dessin de la traînée
ctx.save();
for (let i = 0; i < this.trail.length; i++) {
const point = this.trail[i];
ctx.beginPath();
ctx.arc(point.x, point.y, this.radius * (i / this.trail.length), 0, Math.PI * 2);
ctx.fillStyle = `rgba(0, 243, 255, ${point.alpha * 0.5})`;
ctx.fill();
}
ctx.restore();
// Dessin du joueur
ctx.save();
ctx.shadowBlur = 20;
ctx.shadowColor = config.colors.player;
ctx.fillStyle = config.colors.player;
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
ctx.fill();
// Noyau blanc
ctx.fillStyle = '#fff';
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius * 0.4, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
}
class Target {
constructor() {
this.radius = 12;
this.respawn();
this.pulse = 0;
}
respawn() {
// Marge de sécurité pour ne pas apparaître sur les bords
const margin = 50;
this.x = margin + Math.random() * (width - margin * 2);
this.y = margin + Math.random() * (height - margin * 2);
}
update() {
this.pulse += 0.1;
}
draw() {
const scale = 1 + Math.sin(this.pulse) * 0.1;
ctx.save();
ctx.translate(this.x, this.y);
ctx.scale(scale, scale);
ctx.shadowBlur = 15;
ctx.shadowColor = config.colors.target;
ctx.fillStyle = config.colors.target;
// Forme de losange
ctx.beginPath();
ctx.moveTo(0, -this.radius);
ctx.lineTo(this.radius, 0);
ctx.lineTo(0, this.radius);
ctx.lineTo(-this.radius, 0);
ctx.closePath();
ctx.fill();
ctx.restore();
}
}
class Enemy {
constructor() {
this.radius = 18;
this.resetPosition();
this.angle = Math.random() * Math.PI * 2;
this.speed = config.enemyBaseSpeed + (score * config.enemySpeedIncrement);
}
resetPosition() {
// Apparaît en dehors de l'écran
if (Math.random() < 0.5) {
this.x = Math.random() < 0.5 ? -50 : width + 50;
this.y = Math.random() * height;
} else {
this.x = Math.random() * width;
this.y = Math.random() < 0.5 ? -50 : height + 50;
}
}
update(playerX, playerY) {
// Suit le joueur
const dx = playerX - this.x;
const dy = playerY - this.y;
const distance = Math.sqrt(dx * dx + dy * dy);
this.x += (dx / distance) * this.speed;
this.y += (dy / distance) * this.speed;
// Rotation visuelle
this.angle += 0.05;
}
draw() {
ctx.save();
ctx.translate(this.x, this.y);
ctx.rotate(this.angle);
ctx.shadowBlur = 15;
ctx.shadowColor = config.colors.enemy;
ctx.fillStyle = config.colors.enemy;
// Forme de triangle
ctx.beginPath();
ctx.moveTo(0, -this.radius);
ctx.lineTo(this.radius, this.radius);
ctx.lineTo(-this.radius, this.radius);
ctx.closePath();
ctx.fill();
ctx.restore();
}
}
class Particle {
constructor(x, y, color) {
this.x = x;
this.y = y;
this.color = color;
const angle = Math.random() * Math.PI * 2;
const speed = Math.random() * 4 + 1;
this.vx = Math.cos(angle) * speed;
this.vy = Math.sin(angle) * speed;
this.life = 1.0;
this.decay = Math.random() * 0.03 + 0.02;
}
update() {
this.x += this.vx;
this.y += this.vy;
this.life -= this.decay;
}
draw() {
ctx.save();
ctx.globalAlpha = this.life;
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(this.x, this.y, 3, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
}
// --- Gestion du Jeu ---
let player;
let target;
let enemies = [];
let particles = [];
let mouseX = width / 2;
let mouseY = height / 2;
function init() {
player = new Player();
target = new Target();
enemies = [];
particles = [];
score = 0;
updateScoreUI();
// Spawn initial d'ennemis
spawnEnemy();
}
function spawnEnemy() {
enemies.push(new Enemy());
}
function createExplosion(x, y, color) {
for (let i = 0; i < 15; i++) {
particles.push(new Particle(x, y, color));
}
}
function updateScoreUI() {
currentScoreEl.textContent = score;
highScoreEl.textContent = highScore;
}
function gameOver() {
gameRunning = false;
cancelAnimationFrame(animationId);
if (score > highScore) {
highScore = score;
localStorage.setItem('catchUpHighScore', highScore);
}
finalScoreEl.textContent = score;
finalHighScoreEl.textContent = highScore;
hud.classList.add('hidden');
gameOverScreen.classList.remove('hidden');
}
function gameLoop() {
if (!gameRunning) return;
// Nettoyage de l'écran avec une traînée légère
ctx.fillStyle = 'rgba(11, 12, 21, 0.3)';
ctx.fillRect(0, 0, width, height);
// Effet de grille subtil en arrière-plan
ctx.strokeStyle = 'rgba(255, 255, 255, 0.03)';
ctx.lineWidth = 1;
const gridSize = 50;
const offset = (frameCount * 0.5) % gridSize;
ctx.beginPath();
// Lignes verticales
for (let x = offset; x < width; x += gridSize) {
ctx.moveTo(x, 0);
ctx.lineTo(x, height);
}
// Lignes horizontales
for (let y = offset; y < height; y += gridSize) {
ctx.moveTo(0, y);
ctx.lineTo(width, y);
}
ctx.stroke();
// Logique Joueur
player.update(mouseX, mouseY);
player.draw();
// Logique Cible
target.update();
target.draw();
// Collision Joueur - Cible
const distToTarget = Math.hypot(player.x - target.x, player.y - target.y);
if (distToTarget < player.radius + target.radius) {
score++;
createExplosion(target.x, target.y, config.colors.target);
updateScoreUI();
target.respawn();
// Difficulté : Ajouter un ennemi tous les 3 points
if (score % 3 === 0) {
spawnEnemy();
}
}
// Logique Ennemis
enemies.forEach(enemy => {
enemy.update(player.x, player.y);
enemy.draw();
// Collision Joueur - Ennemi
const distToEnemy = Math.hypot(player.x - enemy.x, player.y - enemy.y);
if (distToEnemy < player.radius + enemy.radius - 5) { // -5 pour hitbox plus indulgente
createExplosion(player.x, player.y, config.colors.player);
gameOver();
}
});
// Logique Particules
particles.forEach((p, index) => {
p.update();
p.draw();
if (p.life <= 0) {
particles.splice(index, 1);
}
});
frameCount++;
animationId = requestAnimationFrame(gameLoop);
}
// --- Entrées Utilisateur ---
// Souris
window.addEventListener('mousemove', (e) => {
if (gameRunning) {
mouseX = e.clientX;
mouseY = e.clientY;
}
});
// Tactile
window.addEventListener('touchmove', (e) => {
if (gameRunning) {
e.preventDefault(); // Empêche le scroll
mouseX = e.touches[0].clientX;
mouseY = e.touches[0].clientY;
}
}, { passive: false });
window.addEventListener('touchstart', (e) => {
if (gameRunning) {
mouseX = e.touches[0].clientX;
mouseY = e.touches[0].clientY;
}
}, { passive: false });
// Boutons
startBtn.addEventListener('click', () => {
startScreen.classList.add('hidden');
hud.classList.remove('hidden');
init();
gameRunning = true;
mouseX = width / 2; // Réinitialiser la position de la souris virtuelle
mouseY = height / 2;
gameLoop();
});
restartBtn.addEventListener('click', () => {
gameOverScreen.classList.add('hidden');
hud.classList.remove('hidden');
init();
gameRunning = true;
mouseX = width / 2;
mouseY = height / 2;
gameLoop();
});
// Initialisation de l'affichage High Score
highScoreEl.textContent = highScore;
</script>
</body>
</html>