import React, { useState, useEffect, useRef } from 'react'; import { Sun, Moon, Volume2, VolumeX, RotateCcw } from 'lucide-react'; // Game constants const GAME_WIDTH = 400; const GAME_HEIGHT = 600; const GRAVITY = 0.5; const JUMP_POWER = 15; const CLOUD_WIDTH = 80; const CLOUD_HEIGHT = 20; const PLAYER_SIZE = 30; const COLLECTIBLE_SIZE = 20; const INITIAL_CLOUD_COUNT = 10; const CLOUD_TYPES = ['normal', 'moving', 'disappearing', 'bouncy']; const POWER_UPS = ['doubleJump', 'highJump', 'slowFall']; const WEATHER_TYPES = ['clear', 'rainy', 'windy', 'stormy']; export default function CloudJumper() { // Game state const [gameStarted, setGameStarted] = useState(false); const [gameOver, setGameOver] = useState(false); const [score, setScore] = useState(0); const [height, setHeight] = useState(0); const [highScore, setHighScore] = useState(0); const [maxHeight, setMaxHeight] = useState(0); const [timeOfDay, setTimeOfDay] = useState('day'); const [weather, setWeather] = useState('clear'); const [soundEnabled, setSoundEnabled] = useState(true); const [activePowerUp, setActivePowerUp] = useState(null); const [powerUpTimer, setPowerUpTimer] = useState(0); // Game objects const [player, setPlayer] = useState({ x: GAME_WIDTH / 2 - PLAYER_SIZE / 2, y: GAME_HEIGHT - 150, velocityY: 0, velocityX: 0, isJumping: false, canDoubleJump: false, character: 'default' }); const [clouds, setClouds] = useState([]); const [collectibles, setCollectibles] = useState([]); const [weatherEffects, setWeatherEffects] = useState([]); // Refs for animation and game loop const requestRef = useRef(); const lastTimeRef = useRef(); const gameContainerRef = useRef(); const cameraOffsetY = useRef(0); const difficultyLevel = useRef(1); const jumpSound = useRef(null); const collectSound = useRef(null); const powerUpSound = useRef(null); const gameOverSound = useRef(null); // Initialize game useEffect(() => { // Load high scores from local storage const savedHighScore = localStorage.getItem('cloudJumperHighScore'); const savedMaxHeight = localStorage.getItem('cloudJumperMaxHeight'); if (savedHighScore) setHighScore(parseInt(savedHighScore)); if (savedMaxHeight) setMaxHeight(parseInt(savedMaxHeight)); // Initialize audio jumpSound.current = new Audio('https://assets.mixkit.co/sfx/preview/mixkit-quick-jump-arcade-game-239.mp3'); collectSound.current = new Audio('https://assets.mixkit.co/sfx/preview/mixkit-arcade-coin-pickup-217.mp3'); powerUpSound.current = new Audio('https://assets.mixkit.co/sfx/preview/mixkit-bonus-earned-in-video-game-2058.mp3'); gameOverSound.current = new Audio('https://assets.mixkit.co/sfx/preview/mixkit-player-losing-or-failing-2042.mp3'); // Set up event listeners window.addEventListener('keydown', handleKeyDown); window.addEventListener('keyup', handleKeyUp); return () => { window.removeEventListener('keydown', handleKeyDown); window.removeEventListener('keyup', handleKeyUp); cancelAnimationFrame(requestRef.current); }; }, []); // Initialize clouds when game starts useEffect(() => { if (gameStarted && !gameOver) { initializeGame(); lastTimeRef.current = 0; requestRef.current = requestAnimationFrame(gameLoop); } return () => { cancelAnimationFrame(requestRef.current); }; }, [gameStarted, gameOver]); // Handle power-up timer useEffect(() => { if (activePowerUp && powerUpTimer > 0) { const timer = setTimeout(() => { setPowerUpTimer(prev => prev - 1); }, 1000); return () => clearTimeout(timer); } else if (powerUpTimer === 0 && activePowerUp) { setActivePowerUp(null); } }, [activePowerUp, powerUpTimer]); // Change weather periodically useEffect(() => { if (gameStarted && !gameOver) { const weatherInterval = setInterval(() => { const randomWeather = WEATHER_TYPES[Math.floor(Math.random() * WEATHER_TYPES.length)]; setWeather(randomWeather); // Create weather effects if (randomWeather !== 'clear') { createWeatherEffects(randomWeather); } else { setWeatherEffects([]); } }, 30000); // Change weather every 30 seconds return () => clearInterval(weatherInterval); } }, [gameStarted, gameOver]); // Day/night cycle useEffect(() => { if (gameStarted && !gameOver) { const dayNightInterval = setInterval(() => { setTimeOfDay(prev => prev === 'day' ? 'night' : 'day'); }, 60000); // Change every 60 seconds return () => clearInterval(dayNightInterval); } }, [gameStarted, gameOver]); const initializeGame = () => { // Reset game state setScore(0); setHeight(0); setActivePowerUp(null); setPowerUpTimer(0); setWeather('clear'); setWeatherEffects([]); difficultyLevel.current = 1; cameraOffsetY.current = 0; // Reset player setPlayer({ x: GAME_WIDTH / 2 - PLAYER_SIZE / 2, y: GAME_HEIGHT - 150, velocityY: 0, velocityX: 0, isJumping: false, canDoubleJump: false, character: 'default' }); // Generate initial clouds const initialClouds = []; for (let i = 0; i < INITIAL_CLOUD_COUNT; i++) { initialClouds.push(generateCloud(GAME_HEIGHT - (i * (GAME_HEIGHT / INITIAL_CLOUD_COUNT)))); } setClouds(initialClouds); // Generate initial collectibles setCollectibles(generateCollectibles(initialClouds)); }; const gameLoop = (timestamp) => { if (!lastTimeRef.current) lastTimeRef.current = timestamp; const deltaTime = timestamp - lastTimeRef.current; lastTimeRef.current = timestamp; if (gameStarted && !gameOver) { updateGame(deltaTime); requestRef.current = requestAnimationFrame(gameLoop); } }; const updateGame = (deltaTime) => { // Update player setPlayer(prevPlayer => { let newVelocityY = prevPlayer.velocityY + GRAVITY; // Apply slow fall power-up if (activePowerUp === 'slowFall') { newVelocityY = Math.min(newVelocityY, 2); } // Apply wind effect let newVelocityX = prevPlayer.velocityX; if (weather === 'windy') { newVelocityX = Math.sin(Date.now() / 1000) * 2; } let newX = prevPlayer.x + newVelocityX; let newY = prevPlayer.y + newVelocityY; // Wrap around horizontally if (newX < -PLAYER_SIZE) newX = GAME_WIDTH; if (newX > GAME_WIDTH) newX = -PLAYER_SIZE; // Check for cloud collisions let isOnCloud = false; let cloudBounceFactor = 1; clouds.forEach(cloud => { if ( newVelocityY > 0 && newY + PLAYER_SIZE >= cloud.y && prevPlayer.y + PLAYER_SIZE <= cloud.y && newX + PLAYER_SIZE > cloud.x && newX < cloud.x + CLOUD_WIDTH ) { if (cloud.type === 'normal' || cloud.type === 'moving' || (cloud.type === 'disappearing' && !cloud.disappearing)) { isOnCloud = true; newY = cloud.y - PLAYER_SIZE; newVelocityY = 0; // Start disappearing animation for disappearing clouds if (cloud.type === 'disappearing') { setClouds(prevClouds => prevClouds.map(c => c.id === cloud.id ? { ...c, disappearing: true } : c ) ); } } else if (cloud.type === 'bouncy') { isOnCloud = true; newY = cloud.y - PLAYER_SIZE; cloudBounceFactor = 1.5; newVelocityY = -JUMP_POWER * cloudBounceFactor; if (soundEnabled) jumpSound.current.play(); } } }); // Check for collectible collisions setCollectibles(prevCollectibles => { let newScore = score; const remainingCollectibles = prevCollectibles.filter(collectible => { const collision = newX < collectible.x + COLLECTIBLE_SIZE && newX + PLAYER_SIZE > collectible.x && newY < collectible.y + COLLECTIBLE_SIZE && newY + PLAYER_SIZE > collectible.y; if (collision) { newScore += 10; if (soundEnabled) collectSound.current.play(); // Random chance for power-up if (Math.random() < 0.3) { const randomPowerUp = POWER_UPS[Math.floor(Math.random() * POWER_UPS.length)]; setActivePowerUp(randomPowerUp); setPowerUpTimer(10); // 10 seconds if (soundEnabled) powerUpSound.current.play(); } } return !collision; }); setScore(newScore); return remainingCollectibles; }); // Update camera and height if (newY < GAME_HEIGHT / 2 && newVelocityY < 0) { cameraOffsetY.current -= newVelocityY; setHeight(prev => { const newHeight = Math.floor(cameraOffsetY.current / 10); return newHeight > prev ? newHeight : prev; }); // Increase difficulty based on height difficultyLevel.current = 1 + Math.floor(cameraOffsetY.current / 2000); } // Check for game over if (newY > GAME_HEIGHT + cameraOffsetY.current) { if (soundEnabled) gameOverSound.current.play(); endGame(); return prevPlayer; } return { ...prevPlayer, x: newX, y: newY, velocityY: newVelocityY, velocityX: newVelocityX, isJumping: !isOnCloud, canDoubleJump: isOnCloud ? true : prevPlayer.canDoubleJump }; }); // Update clouds setClouds(prevClouds => { // Remove clouds that are too far below let filteredClouds = prevClouds.filter( cloud => cloud.y < GAME_HEIGHT + cameraOffsetY.current + 100 ); // Remove disappearing clouds that have been stepped on filteredClouds = filteredClouds.filter( cloud => !(cloud.type === 'disappearing' && cloud.disappearing && Date.now() - cloud.disappearingTime > 500) ); // Add new clouds as player moves up while (filteredClouds.length < INITIAL_CLOUD_COUNT + difficultyLevel.current) { const highestCloud = filteredClouds.reduce( (highest, cloud) => cloud.y < highest ? cloud.y : highest, Infinity ); const newCloudY = highestCloud === Infinity ? cameraOffsetY.current : highestCloud - Math.random() * 100 - 50; filteredClouds.push(generateCloud(newCloudY)); } // Update moving clouds return filteredClouds.map(cloud => { if (cloud.type === 'moving') { let newX = cloud.x + cloud.speed; if (newX < -CLOUD_WIDTH || newX > GAME_WIDTH) { cloud.speed = -cloud.speed; newX = cloud.x + cloud.speed; } return { ...cloud, x: newX }; } if (cloud.type === 'disappearing' && cloud.disappearing && !cloud.disappearingTime) { return { ...cloud, disappearingTime: Date.now() }; } return cloud; }); }); // Update collectibles setCollectibles(prevCollectibles => { // Remove collectibles that are too far below const filteredCollectibles = prevCollectibles.filter( collectible => collectible.y < GAME_HEIGHT + cameraOffsetY.current + 100 ); // Add new collectibles if needed if (filteredCollectibles.length < 5 + difficultyLevel.current) { return [...filteredCollectibles, ...generateCollectibles(clouds, 2)]; } return filteredCollectibles; }); // Update weather effects if (weather !== 'clear') { setWeatherEffects(prevEffects => { // Remove effects that are out of bounds const filteredEffects = prevEffects.filter( effect => effect.y < GAME_HEIGHT + cameraOffsetY.current + 100 ); // Add new effects if (filteredEffects.length < 20) { return [...filteredEffects, ...createWeatherEffects(weather, 5)]; } // Update effect positions return filteredEffects.map(effect => ({ ...effect, x: effect.x + effect.velocityX, y: effect.y + effect.velocityY })); }); } }; const generateCloud = (y) => { // Weight cloud types based on difficulty let typeWeights = [70, 10, 10, 10]; // normal, moving, disappearing, bouncy if (difficultyLevel.current > 1) { typeWeights = [60, 15, 15, 10]; } if (difficultyLevel.current > 3) { typeWeights = [50, 20, 20, 10]; } if (difficultyLevel.current > 5) { typeWeights = [40, 20, 25, 15]; } // Select cloud type based on weights const randomValue = Math.random() * 100; let cumulativeWeight = 0; let selectedType = 'normal'; for (let i = 0; i < CLOUD_TYPES.length; i++) { cumulativeWeight += typeWeights[i]; if (randomValue <= cumulativeWeight) { selectedType = CLOUD_TYPES[i]; break; } } return { id: Math.random().toString(36).substr(2, 9), x: Math.random() * (GAME_WIDTH - CLOUD_WIDTH), y, type: selectedType, speed: selectedType === 'moving' ? (Math.random() > 0.5 ? 1 : -1) * (1 + Math.random()), disappearing: false, disappearingTime: null }; }; const generateCollectibles = (cloudArray, count = 5) => { const newCollectibles = []; const usedClouds = new Set(); // Try to place collectibles above clouds for (let i = 0; i < count && usedClouds.size < cloudArray.length; i++) { let attempts = 0; let cloudIndex; // Find a cloud that doesn't already have a collectible do { cloudIndex = Math.floor(Math.random() * cloudArray.length); attempts++; } while (usedClouds.has(cloudIndex) && attempts < 10); if (attempts < 10) { const cloud = cloudArray[cloudIndex]; usedClouds.add(cloudIndex); newCollectibles.push({ id: Math.random().toString(36).substr(2, 9), x: cloud.x + CLOUD_WIDTH / 2 - COLLECTIBLE_SIZE / 2, y: cloud.y - COLLECTIBLE_SIZE - 20 - Math.random() * 30 }); } } // If we need more collectibles, place them randomly while (newCollectibles.length < count) { newCollectibles.push({ id: Math.random().toString(36).substr(2, 9), x: Math.random() * (GAME_WIDTH - COLLECTIBLE_SIZE), y: cameraOffsetY.current - Math.random() * GAME_HEIGHT }); } return newCollectibles; }; const createWeatherEffects = (weatherType, count = 10) => { if (weatherType === 'clear') return []; const newEffects = []; for (let i = 0; i < count; i++) { let effect = { id: Math.random().toString(36).substr(2, 9), x: Math.random() * GAME_WIDTH, y: cameraOffsetY.current - Math.random() * GAME_HEIGHT, velocityX: 0, velocityY: 0, type: weatherType }; switch (weatherType) { case 'rainy': effect.velocityX = -1; effect.velocityY = 10; break; case 'windy': effect.velocityX = -5 - Math.random() * 5; effect.velocityY = 2; break; case 'stormy': effect.velocityX = -2 - Math.random() * 3; effect.velocityY = 15; if (Math.random() < 0.05) { effect.type = 'lightning'; } break; } newEffects.push(effect); } return newEffects; }; const handleKeyDown = (e) => { if (e.code === 'Space' || e.code === 'ArrowUp') { jump(); } if (e.code === 'ArrowLeft') { setPlayer(prev => ({ ...prev, velocityX: -3 })); } if (e.code === 'ArrowRight') { setPlayer(prev => ({ ...prev, velocityX: 3 })); } if (e.code === 'KeyR' && gameOver) { restartGame(); } }; const handleKeyUp = (e) => { if (e.code === 'ArrowLeft' || e.code === 'ArrowRight') { setPlayer(prev => ({ ...prev, velocityX: 0 })); } }; const handleTouchStart = (e) => { if (!gameStarted) { startGame(); return; } jump(); e.preventDefault(); }; const handleTouchMove = (e) => { if (gameStarted && !gameOver) { const touch = e.touches[0]; const gameContainer = gameContainerRef.current; const rect = gameContainer.getBoundingClientRect(); const touchX = touch.clientX - rect.left; // Move player based on touch position const centerX = GAME_WIDTH / 2; const moveSpeed = 3; if (touchX < centerX - 50) { setPlayer(prev => ({ ...prev, velocityX: -moveSpeed })); } else if (touchX > centerX + 50) { setPlayer(prev => ({ ...prev, velocityX: moveSpeed })); } else { setPlayer(prev => ({ ...prev, velocityX: 0 })); } e.preventDefault(); } }; const handleTouchEnd = () => { setPlayer(prev => ({ ...prev, velocityX: 0 })); }; const jump = () => { setPlayer(prev => { // Regular jump when on a cloud if (!prev.isJumping) { if (soundEnabled) jumpSound.current.play(); return { ...prev, velocityY: activePowerUp === 'highJump' ? -JUMP_POWER * 1.5 : -JUMP_POWER, isJumping: true, canDoubleJump: activePowerUp === 'doubleJump' }; } // Double jump if power-up is active else if (prev.canDoubleJump) { if (soundEnabled) jumpSound.current.play(); return { ...prev, velocityY: -JUMP_POWER * 0.8, canDoubleJump: false }; } return prev; }); }; const startGame = () => { setGameStarted(true); setGameOver(false); }; const endGame = () => { setGameOver(true); // Update high scores if (score > highScore) { setHighScore(score); localStorage.setItem('cloudJumperHighScore', score.toString()); } if (height > maxHeight) { setMaxHeight(height); localStorage.setItem('cloudJumperMaxHeight', height.toString()); } }; const restartGame = () => { setGameOver(false); initializeGame(); }; const toggleSound = () => { setSoundEnabled(!soundEnabled); }; // Render game elements const renderPlayer = () => { const playerStyle = { position: 'absolute', width: `${PLAYER_SIZE}px`, height: `${PLAYER_SIZE}px`, left: `${player.x}px`, top: `${player.y - cameraOffsetY.current}px`, backgroundColor: '#FF6B6B', borderRadius: '50%', boxShadow: '0 2px 4px rgba(0,0,0,0.2)', zIndex: 10, transition: 'transform 0.1s ease-out' }; // Add jumping animation if (player.isJumping) { playerStyle.transform = 'scaleY(0.9)'; } // Add power-up visual effect if (activePowerUp) { playerStyle.boxShadow = '0 0 10px 5px #FFD700'; } return
; }; const renderClouds = () => { return clouds.map(cloud => { const cloudStyle = { position: 'absolute', width: `${CLOUD_WIDTH}px`, height: `${CLOUD_HEIGHT}px`, left: `${cloud.x}px`, top: `${cloud.y - cameraOffsetY.current}px`, backgroundColor: '#FFFFFF', borderRadius: '20px', boxShadow: '0 2px 4px rgba(0,0,0,0.1)', opacity: cloud.type === 'disappearing' && cloud.disappearing ? 0.5 : 1, transition: cloud.type === 'disappearing' ? 'opacity 0.5s ease-out' : 'none' }; // Different styles for different cloud types switch (cloud.type) { case 'moving': cloudStyle.backgroundColor = '#E0F7FA'; break; case 'disappearing': cloudStyle.backgroundColor = '#F5F5F5'; break; case 'bouncy': cloudStyle.backgroundColor = '#B3E5FC'; cloudStyle.boxShadow = '0 0 8px #29B6F6'; break; } return
; }); }; const renderCollectibles = () => { return collectibles.map(collectible => { const collectibleStyle = { position: 'absolute', width: `${COLLECTIBLE_SIZE}px`, height: `${COLLECTIBLE_SIZE}px`, left: `${collectible.x}px`, top: `${collectible.y - cameraOffsetY.current}px`, backgroundColor: '#FFD700', borderRadius: '50%', boxShadow: '0 0 10px #FFD700', animation: 'float 1s infinite alternate ease-in-out' }; return
; }); }; const renderWeatherEffects = () => { return weatherEffects.map(effect => { let effectStyle = { position: 'absolute', left: `${effect.x}px`, top: `${effect.y - cameraOffsetY.current}px`, zIndex: 5 }; switch (effect.type) { case 'rainy': effectStyle = { ...effectStyle, width: '2px', height: '15px', backgroundColor: '#BBDEFB', transform: 'rotate(15deg)' }; break; case 'windy': effectStyle = { ...effectStyle, width: '20px', height: '2px', backgroundColor: '#E0E0E0', opacity: 0.7 }; break; case 'stormy': effectStyle = { ...effectStyle, width: '3px', height: '20px', backgroundColor: '#BBDEFB', transform: 'rotate(20deg)' }; break; case 'lightning': effectStyle = { ...effectStyle, width: '100px', height: '200px', backgroundColor: 'transparent', boxShadow: '0 0 30px 10px rgba(255, 255, 150, 0.8)', opacity: 0.7 }; break; } return
; }); }; // Background gradient based on time of day const getBackgroundStyle = () => { if (timeOfDay === 'day') { return { background: 'linear-gradient(to bottom, #87CEEB, #1E88E5)' }; } else { return { background: 'linear-gradient(to bottom, #1A237E, #000080)' }; } }; // Render UI components const renderUI = () => { return (
Score: {score}
Height: {height}m
{activePowerUp && (
{activePowerUp === 'doubleJump' ? 'Double Jump' : activePowerUp === 'highJump' ? 'High Jump' : 'Slow Fall'} {powerUpTimer}s
)}
); }; // Render start screen const renderStartScreen = () => { return (

Cloud Jumper

Jump between clouds and climb as high as you can!
Collect coins for points and power-ups.

{highScore > 0 && (

High Score: {highScore}

Max Height: {maxHeight}m

)}
); }; // Render game over screen const renderGameOverScreen = () => { return (

Game Over

Score: {score}

Height: {height}m

{score === highScore && score > 0 && (

New High Score!

)} {height === maxHeight && height > 0 && (

New Height Record!

)}
); }; return (
{renderWeatherEffects()} {renderClouds()} {renderCollectibles()} {renderPlayer()} {gameStarted && !gameOver && renderUI()} {!gameStarted && renderStartScreen()} {gameOver && renderGameOverScreen()}

How to Play

• Press Space or ↑ to jump

• Use ← and → to move

• On mobile, tap to jump and tilt to move

• Collect coins for points and power-ups

Cloud Types:

• White - Normal clouds

• Light Blue - Moving clouds

• Gray - Disappearing clouds

• Blue with glow - Bouncy clouds

); }