-
-
Save shricodev/493fe9e8fe2b725e5c43ff88a9cd62d0 to your computer and use it in GitHub Desktop.
Geometry Dash (Developed by Kimi K2 AI Model) - Blog Demo
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Audio System for Rhythm-Based Gameplay | |
| class AudioManager { | |
| constructor() { | |
| this.audioContext = null; | |
| this.masterGain = null; | |
| this.musicGain = null; | |
| this.sfxGain = null; | |
| this.isInitialized = false; | |
| this.beatInterval = 500; // 120 BPM | |
| this.lastBeatTime = 0; | |
| this.beatCount = 0; | |
| // Sound effects using Web Audio API | |
| this.sounds = { | |
| jump: null, | |
| death: null, | |
| collect: null, | |
| complete: null | |
| }; | |
| // Background music pattern | |
| this.musicPattern = [ | |
| { freq: 440, duration: 0.25, type: 'sine' }, // A4 | |
| { freq: 554, duration: 0.25, type: 'sine' }, // C#5 | |
| { freq: 659, duration: 0.25, type: 'sine' }, // E5 | |
| { freq: 440, duration: 0.25, type: 'sine' }, // A4 | |
| { freq: 523, duration: 0.5, type: 'sine' }, // C5 | |
| { freq: 659, duration: 0.5, type: 'sine' }, // E5 | |
| { freq: 784, duration: 0.25, type: 'sine' }, // G5 | |
| { freq: 659, duration: 0.25, type: 'sine' }, // E5 | |
| { freq: 523, duration: 0.5, type: 'sine' } // C5 | |
| ]; | |
| } | |
| async initialize() { | |
| try { | |
| this.audioContext = new (window.AudioContext || window.webkitAudioContext)(); | |
| // Create gain nodes | |
| this.masterGain = this.audioContext.createGain(); | |
| this.musicGain = this.audioContext.createGain(); | |
| this.sfxGain = this.audioContext.createGain(); | |
| // Connect gain nodes | |
| this.musicGain.connect(this.masterGain); | |
| this.sfxGain.connect(this.masterGain); | |
| this.masterGain.connect(this.audioContext.destination); | |
| // Set initial volumes | |
| this.masterGain.gain.value = 0.3; | |
| this.musicGain.gain.value = 0.2; | |
| this.sfxGain.gain.value = 0.4; | |
| this.isInitialized = true; | |
| console.log('Audio system initialized'); | |
| } catch (error) { | |
| console.warn('Audio initialization failed:', error); | |
| } | |
| } | |
| playSound(type, frequency = 440, duration = 0.1, waveType = 'sine') { | |
| if (!this.isInitialized) return; | |
| try { | |
| const oscillator = this.audioContext.createOscillator(); | |
| const gainNode = this.audioContext.createGain(); | |
| oscillator.connect(gainNode); | |
| gainNode.connect(this.sfxGain); | |
| oscillator.type = waveType; | |
| oscillator.frequency.setValueAtTime(frequency, this.audioContext.currentTime); | |
| // ADSR envelope | |
| gainNode.gain.setValueAtTime(0, this.audioContext.currentTime); | |
| gainNode.gain.linearRampToValueAtTime(0.3, this.audioContext.currentTime + 0.01); | |
| gainNode.gain.exponentialRampToValueAtTime(0.01, this.audioContext.currentTime + duration); | |
| oscillator.start(this.audioContext.currentTime); | |
| oscillator.stop(this.audioContext.currentTime + duration); | |
| } catch (error) { | |
| console.warn('Sound playback failed:', error); | |
| } | |
| } | |
| playJumpSound() { | |
| this.playSound('jump', 523.25, 0.15, 'sine'); // C5 | |
| } | |
| playDeathSound() { | |
| this.playSound('death', 220, 0.3, 'sawtooth'); // A3 | |
| } | |
| playCollectSound() { | |
| this.playSound('collect', 659.25, 0.2, 'triangle'); // E5 | |
| } | |
| playCompleteSound() { | |
| // Victory fanfare | |
| const melody = [523.25, 659.25, 783.99, 1046.50]; // C5, E5, G5, C6 | |
| melody.forEach((freq, index) => { | |
| setTimeout(() => { | |
| this.playSound('complete', freq, 0.3, 'sine'); | |
| }, index * 150); | |
| }); | |
| } | |
| playBackgroundMusic() { | |
| if (!this.isInitialized) return; | |
| // Create a simple looping pattern | |
| const playPattern = () => { | |
| this.musicPattern.forEach((note, index) => { | |
| setTimeout(() => { | |
| this.playSound('music', note.freq, note.duration, note.type); | |
| }, index * note.duration * 1000); | |
| }); | |
| // Loop the pattern | |
| setTimeout(playPattern, this.musicPattern.reduce((sum, note) => sum + note.duration, 0) * 1000); | |
| }; | |
| playPattern(); | |
| } | |
| getBeatTiming() { | |
| const now = this.audioContext ? this.audioContext.currentTime : Date.now() / 1000; | |
| const beat = Math.floor(now / (this.beatInterval / 1000)); | |
| const nextBeat = (beat + 1) * (this.beatInterval / 1000); | |
| const timeToNextBeat = nextBeat - now; | |
| return { | |
| beat: beat, | |
| timeToNextBeat: timeToNextBeat, | |
| isOnBeat: timeToNextBeat < 0.1 | |
| }; | |
| } | |
| resume() { | |
| if (this.audioContext && this.audioContext.state === 'suspended') { | |
| this.audioContext.resume(); | |
| } | |
| } | |
| } | |
| // Global audio manager | |
| const audioManager = new AudioManager(); | |
| // Initialize audio on first user interaction | |
| document.addEventListener('click', () => { | |
| audioManager.resume(); | |
| if (!audioManager.isInitialized) { | |
| audioManager.initialize(); | |
| } | |
| }, { once: true }); | |
| document.addEventListener('keydown', () => { | |
| audioManager.resume(); | |
| if (!audioManager.isInitialized) { | |
| audioManager.initialize(); | |
| } | |
| }, { once: true }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Fixed Geometry Dash Game - No Audio, Functional | |
| // This version fixes the screen positioning and interaction issues | |
| // Game configuration | |
| const CONFIG = { | |
| GRAVITY: 0.8, | |
| JUMP_FORCE: -15, | |
| PLAYER_SIZE: 30, | |
| GROUND_Y: 500, | |
| GAME_SPEED: 6, | |
| LEVEL_LENGTH: 8000, | |
| CAMERA_OFFSET: 100, | |
| SCREEN_SHAKE: 20 | |
| }; | |
| // Simple level design | |
| const LEVEL = { | |
| obstacles: [ | |
| { x: 300, type: 'spike', height: 40 }, | |
| { x: 500, type: 'spike', height: 50 }, | |
| { x: 700, type: 'gap', width: 80 }, | |
| { x: 1000, type: 'spike', height: 60 }, | |
| { x: 1200, type: 'spike', height: 70 }, | |
| { x: 1400, type: 'spike', height: 80 }, | |
| { x: 1600, type: 'gap', width: 100 }, | |
| { x: 2000, type: 'spike', height: 90 }, | |
| { x: 2200, type: 'spike', height: 60 }, | |
| { x: 2400, type: 'spike', height: 100 }, | |
| { x: 2600, type: 'gap', width: 120 }, | |
| { x: 3200, type: 'spike', height: 110 }, | |
| { x: 3400, type: 'spike', height: 70 }, | |
| { x: 3600, type: 'spike', height: 90 }, | |
| { x: 3800, type: 'gap', width: 150 }, | |
| { x: 4200, type: 'spike', height: 120 }, | |
| { x: 4400, type: 'spike', height: 80 }, | |
| { x: 4600, type: 'spike', height: 100 }, | |
| { x: 4800, type: 'spike', height: 90 }, | |
| { x: 5000, type: 'gap', width: 180 }, | |
| { x: 5400, type: 'spike', height: 130 }, | |
| { x: 5600, type: 'spike', height: 100 }, | |
| { x: 5800, type: 'spike', height: 110 }, | |
| { x: 6000, type: 'spike', height: 120 }, | |
| { x: 6200, type: 'gap', width: 200 }, | |
| { x: 6800, type: 'spike', height: 100 }, | |
| { x: 7000, type: 'spike', height: 90 }, | |
| { x: 7200, type: 'spike', height: 110 }, | |
| { x: 7400, type: 'spike', height: 100 }, | |
| { x: 7600, type: 'spike', height: 120 } | |
| ] | |
| }; | |
| // Player class | |
| class Player { | |
| constructor() { | |
| this.x = 100; | |
| this.y = CONFIG.GROUND_Y - CONFIG.PLAYER_SIZE; | |
| this.velocityY = 0; | |
| this.isGrounded = true; | |
| this.rotation = 0; | |
| } | |
| update() { | |
| this.velocityY += CONFIG.GRAVITY; | |
| this.y += this.velocityY; | |
| if (this.y >= CONFIG.GROUND_Y - CONFIG.PLAYER_SIZE) { | |
| this.y = CONFIG.GROUND_Y - CONFIG.PLAYER_SIZE; | |
| this.velocityY = 0; | |
| this.isGrounded = true; | |
| this.rotation = 0; | |
| } else { | |
| this.isGrounded = false; | |
| this.rotation += 0.3; | |
| } | |
| } | |
| jump() { | |
| if (this.isGrounded) { | |
| this.velocityY = CONFIG.JUMP_FORCE; | |
| this.isGrounded = false; | |
| } | |
| } | |
| display() { | |
| push(); | |
| translate(this.x + CONFIG.PLAYER_SIZE/2, this.y + CONFIG.PLAYER_SIZE/2); | |
| rotate(this.rotation); | |
| // Player body | |
| fill(255, 100, 150); | |
| stroke(255, 255, 255); | |
| strokeWeight(2); | |
| rect(-CONFIG.PLAYER_SIZE/2, -CONFIG.PLAYER_SIZE/2, CONFIG.PLAYER_SIZE, CONFIG.PLAYER_SIZE, 8); | |
| // Eyes | |
| fill(0); | |
| ellipse(-8, -8, 4, 4); | |
| ellipse(8, -8, 4, 4); | |
| pop(); | |
| } | |
| checkCollision(obstacle) { | |
| if (obstacle.type === 'spike') { | |
| return (this.x < obstacle.x + obstacle.width && | |
| this.x + CONFIG.PLAYER_SIZE > obstacle.x && | |
| this.y < obstacle.y + obstacle.height && | |
| this.y + CONFIG.PLAYER_SIZE > obstacle.y); | |
| } else if (obstacle.type === 'gap') { | |
| return (this.x < obstacle.x + obstacle.width && | |
| this.x + CONFIG.PLAYER_SIZE > obstacle.x && | |
| this.y + CONFIG.PLAYER_SIZE > CONFIG.GROUND_Y); | |
| } | |
| return false; | |
| } | |
| } | |
| // Obstacle class | |
| class Obstacle { | |
| constructor(x, type, height = 40, width = 40) { | |
| this.x = x; | |
| this.type = type; | |
| this.height = height; | |
| this.width = width; | |
| this.y = type === 'spike' ? CONFIG.GROUND_Y - height : CONFIG.GROUND_Y; | |
| } | |
| display() { | |
| push(); | |
| if (this.type === 'spike') { | |
| fill(255, 80, 80); | |
| stroke(255, 255, 255); | |
| strokeWeight(2); | |
| triangle(this.x, CONFIG.GROUND_Y, | |
| this.x + this.width/2, this.y, | |
| this.x + this.width, CONFIG.GROUND_Y); | |
| } else if (this.type === 'gap') { | |
| fill(255, 80, 80); | |
| rect(this.x, CONFIG.GROUND_Y, this.width, 10); | |
| } | |
| pop(); | |
| } | |
| } | |
| // Game state management | |
| let gameState = 'start'; | |
| let player, obstacles, cameraX = 0; | |
| let attempts = 0; | |
| let startTime = 0; | |
| let currentTime = 0; | |
| let progress = 0; | |
| function setup() { | |
| let canvas = createCanvas(1200, 600); | |
| canvas.parent('gameContainer'); | |
| // Initialize game objects | |
| player = new Player(); | |
| // Create obstacles | |
| obstacles = []; | |
| for (let obs of LEVEL.obstacles) { | |
| obstacles.push(new Obstacle(obs.x, obs.type, obs.height, obs.width)); | |
| } | |
| textAlign(CENTER, CENTER); | |
| frameRate(60); | |
| } | |
| function draw() { | |
| // Clear background | |
| background(100, 150, 200); | |
| switch (gameState) { | |
| case 'start': | |
| drawStartScreen(); | |
| break; | |
| case 'playing': | |
| updateGame(); | |
| displayGame(); | |
| break; | |
| case 'dead': | |
| drawDeathScreen(); | |
| break; | |
| case 'completed': | |
| drawCompletionScreen(); | |
| break; | |
| } | |
| // Update UI | |
| updateUI(); | |
| } | |
| function updateGame() { | |
| // Move player forward automatically | |
| player.x += CONFIG.GAME_SPEED; | |
| // Update camera to follow player | |
| cameraX = player.x - CONFIG.CAMERA_OFFSET; | |
| // Update player physics | |
| player.update(); | |
| // Check collisions | |
| for (let obstacle of obstacles) { | |
| if (player.checkCollision(obstacle)) { | |
| gameOver(); | |
| return; | |
| } | |
| } | |
| // Check level completion | |
| if (player.x >= CONFIG.LEVEL_LENGTH) { | |
| gameCompleted(); | |
| return; | |
| } | |
| } | |
| function displayGame() { | |
| push(); | |
| translate(-cameraX, 0); | |
| // Draw ground | |
| fill(100, 200, 100); | |
| rect(0, CONFIG.GROUND_Y, CONFIG.LEVEL_LENGTH + width, height - CONFIG.GROUND_Y); | |
| // Draw obstacles | |
| for (let obstacle of obstacles) { | |
| obstacle.display(); | |
| } | |
| // Draw player | |
| player.display(); | |
| // Draw finish line | |
| fill(255, 255, 0); | |
| rect(CONFIG.LEVEL_LENGTH, 0, 10, CONFIG.GROUND_Y); | |
| pop(); | |
| } | |
| function drawStartScreen() { | |
| push(); | |
| fill(255); | |
| textSize(48); | |
| text("GEOMETRY DASH", width/2, height/2 - 50); | |
| fill(200); | |
| textSize(24); | |
| text("Press SPACE or CLICK to start", width/2, height/2); | |
| text("Avoid the obstacles and reach the end!", width/2, height/2 + 30); | |
| pop(); | |
| } | |
| function drawDeathScreen() { | |
| push(); | |
| fill(0, 0, 0, 150); | |
| rect(0, 0, width, height); | |
| fill(255, 80, 80); | |
| textSize(64); | |
| text("GAME OVER", width/2, height/2 - 50); | |
| fill(255); | |
| textSize(24); | |
| text(`Time: ${currentTime.toFixed(1)}s`, width/2, height/2); | |
| text(`Progress: ${progress}%`, width/2, height/2 + 30); | |
| text("Press SPACE or CLICK to restart", width/2, height/2 + 60); | |
| pop(); | |
| } | |
| function drawCompletionScreen() { | |
| push(); | |
| fill(0, 100, 0, 100); | |
| rect(0, 0, width, height); | |
| fill(100, 255, 100); | |
| textSize(48); | |
| text("LEVEL COMPLETE!", width/2, height/2 - 30); | |
| fill(255); | |
| textSize(24); | |
| text(`Final Time: ${currentTime.toFixed(1)}s`, width/2, height/2); | |
| text("Press SPACE or CLICK to play again", width/2, height/2 + 30); | |
| pop(); | |
| } | |
| function updateUI() { | |
| if (gameState === 'playing') { | |
| currentTime = (millis() - startTime) / 1000; | |
| progress = floor((player.x / CONFIG.LEVEL_LENGTH) * 100); | |
| // Update DOM elements | |
| document.getElementById('attempts').textContent = attempts; | |
| document.getElementById('time').textContent = currentTime.toFixed(1); | |
| document.getElementById('progress').textContent = progress; | |
| } | |
| } | |
| function gameOver() { | |
| gameState = 'dead'; | |
| } | |
| function gameCompleted() { | |
| gameState = 'completed'; | |
| } | |
| function startGame() { | |
| gameState = 'playing'; | |
| attempts++; | |
| startTime = millis(); | |
| cameraX = 0; | |
| // Reset player position | |
| player.x = 100; | |
| player.y = CONFIG.GROUND_Y - CONFIG.PLAYER_SIZE; | |
| player.velocityY = 0; | |
| player.isGrounded = true; | |
| player.rotation = 0; | |
| } | |
| function keyPressed() { | |
| if (key === ' ') { | |
| if (gameState === 'playing') { | |
| player.jump(); | |
| } else { | |
| startGame(); | |
| } | |
| return false; // Prevent default space behavior | |
| } | |
| } | |
| function mousePressed() { | |
| if (gameState === 'playing') { | |
| player.jump(); | |
| } else { | |
| startGame(); | |
| } | |
| return false; // Prevent default mouse behavior | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Geometry Dash Ultra - Enhanced Version | |
| let player; | |
| let obstacles = []; | |
| let particles = []; | |
| let backgroundParticles = []; | |
| let gameState = 'start'; // 'start', 'playing', 'dead', 'completed' | |
| let attempts = 0; | |
| let startTime = 0; | |
| let currentTime = 0; | |
| let cameraX = 0; | |
| let levelProgress = 0; | |
| let screenShake = 0; | |
| let bgHue = 0; | |
| // Game settings | |
| const GRAVITY = 0.8; | |
| const JUMP_FORCE = -15; | |
| const PLAYER_SIZE = 30; | |
| const GROUND_Y = 500; | |
| const GAME_SPEED = 6; | |
| // Level design - obstacles placed at specific positions | |
| const LEVEL_LENGTH = 8000; | |
| const OBSTACLE_PATTERN = [ | |
| { x: 400, type: 'spike', height: 40 }, | |
| { x: 600, type: 'spike', height: 60 }, | |
| { x: 800, type: 'gap', width: 100 }, | |
| { x: 1100, type: 'spike', height: 50 }, | |
| { x: 1300, type: 'spike', height: 70 }, | |
| { x: 1500, type: 'spike', height: 40 }, | |
| { x: 1700, type: 'gap', width: 120 }, | |
| { x: 2000, type: 'spike', height: 80 }, | |
| { x: 2200, type: 'spike', height: 60 }, | |
| { x: 2400, type: 'spike', height: 50 }, | |
| { x: 2600, type: 'spike', height: 70 }, | |
| { x: 2800, type: 'gap', width: 150 }, | |
| { x: 3200, type: 'spike', height: 90 }, | |
| { x: 3400, type: 'spike', height: 60 }, | |
| { x: 3600, type: 'spike', height: 80 }, | |
| { x: 3800, type: 'gap', width: 180 }, | |
| { x: 4200, type: 'spike', height: 100 }, | |
| { x: 4400, type: 'spike', height: 70 }, | |
| { x: 4600, type: 'spike', height: 90 }, | |
| { x: 4800, type: 'spike', height: 60 }, | |
| { x: 5000, type: 'gap', width: 200 }, | |
| { x: 5400, type: 'spike', height: 110 }, | |
| { x: 5600, type: 'spike', height: 80 }, | |
| { x: 5800, type: 'spike', height: 100 }, | |
| { x: 6000, type: 'spike', height: 70 }, | |
| { x: 6200, type: 'gap', width: 220 }, | |
| { x: 6600, type: 'spike', height: 120 }, | |
| { x: 6800, type: 'spike', height: 90 }, | |
| { x: 7000, type: 'spike', height: 110 }, | |
| { x: 7200, type: 'spike', height: 80 }, | |
| { x: 7400, type: 'spike', height: 100 }, | |
| { x: 7600, type: 'spike', height: 90 }, | |
| { x: 7800, type: 'spike', height: 120 } | |
| ]; | |
| class Player { | |
| constructor() { | |
| this.x = 100; | |
| this.y = GROUND_Y - PLAYER_SIZE; | |
| this.velocityY = 0; | |
| this.isGrounded = true; | |
| this.trail = []; | |
| this.glow = 0; | |
| } | |
| update() { | |
| this.velocityY += GRAVITY; | |
| this.y += this.velocityY; | |
| if (this.y >= GROUND_Y - PLAYER_SIZE) { | |
| this.y = GROUND_Y - PLAYER_SIZE; | |
| this.velocityY = 0; | |
| this.isGrounded = true; | |
| } else { | |
| this.isGrounded = false; | |
| } | |
| // Add trail effect | |
| this.trail.push({ x: this.x, y: this.y, alpha: 255 }); | |
| if (this.trail.length > 10) { | |
| this.trail.shift(); | |
| } | |
| // Update trail alpha | |
| for (let i = 0; i < this.trail.length; i++) { | |
| this.trail[i].alpha -= 25; | |
| } | |
| this.glow += 0.1; | |
| } | |
| jump() { | |
| if (this.isGrounded) { | |
| this.velocityY = JUMP_FORCE; | |
| this.isGrounded = false; | |
| // Jump particles | |
| for (let i = 0; i < 8; i++) { | |
| particles.push(new Particle(this.x, this.y + PLAYER_SIZE, | |
| random(-3, 3), random(-8, -2), color(255, 200, 100))); | |
| } | |
| } | |
| } | |
| display() { | |
| push(); | |
| // Draw trail | |
| for (let i = 0; i < this.trail.length; i++) { | |
| let t = this.trail[i]; | |
| let alpha = t.alpha; | |
| fill(255, 100, 150, alpha); | |
| noStroke(); | |
| ellipse(t.x, t.y + PLAYER_SIZE/2, PLAYER_SIZE * 0.8, PLAYER_SIZE * 0.8); | |
| } | |
| // Draw glow | |
| for (let i = 3; i > 0; i--) { | |
| fill(255, 100, 150, 30); | |
| noStroke(); | |
| ellipse(this.x + PLAYER_SIZE/2, this.y + PLAYER_SIZE/2, | |
| PLAYER_SIZE + i * 10, PLAYER_SIZE + i * 10); | |
| } | |
| // Draw player | |
| fill(255, 100, 150); | |
| stroke(255, 255, 255); | |
| strokeWeight(2); | |
| rect(this.x, this.y, PLAYER_SIZE, PLAYER_SIZE, 8); | |
| // Inner detail | |
| fill(255, 255, 255, 100); | |
| noStroke(); | |
| rect(this.x + 5, this.y + 5, PLAYER_SIZE - 10, PLAYER_SIZE - 10, 4); | |
| pop(); | |
| } | |
| checkCollision(obstacle) { | |
| if (obstacle.type === 'spike') { | |
| return (this.x < obstacle.x + obstacle.width && | |
| this.x + PLAYER_SIZE > obstacle.x && | |
| this.y + PLAYER_SIZE > obstacle.y); | |
| } else if (obstacle.type === 'gap') { | |
| return (this.x < obstacle.x + obstacle.width && | |
| this.x + PLAYER_SIZE > obstacle.x && | |
| this.y + PLAYER_SIZE > GROUND_Y); | |
| } | |
| return false; | |
| } | |
| } | |
| class Obstacle { | |
| constructor(x, type, height = 40, width = 40) { | |
| this.x = x; | |
| this.type = type; | |
| this.height = height; | |
| this.width = width; | |
| this.y = type === 'spike' ? GROUND_Y - height : GROUND_Y; | |
| this.pulse = 0; | |
| } | |
| update() { | |
| this.pulse += 0.1; | |
| } | |
| display() { | |
| push(); | |
| if (this.type === 'spike') { | |
| // Draw spike with gradient | |
| let spikeColor = color(255, 80, 80); | |
| let spikeHighlight = color(255, 150, 150); | |
| // Glow effect | |
| for (let i = 3; i > 0; i--) { | |
| fill(255, 80, 80, 20); | |
| noStroke(); | |
| triangle(this.x - 5, GROUND_Y, | |
| this.x + this.width/2, this.y - 10 - sin(this.pulse) * 3, | |
| this.x + this.width + 5, GROUND_Y); | |
| } | |
| // Main spike | |
| fill(spikeColor); | |
| stroke(255, 255, 255); | |
| strokeWeight(2); | |
| triangle(this.x, GROUND_Y, | |
| this.x + this.width/2, this.y - sin(this.pulse) * 3, | |
| this.x + this.width, GROUND_Y); | |
| // Highlight | |
| fill(spikeHighlight); | |
| noStroke(); | |
| triangle(this.x + 5, GROUND_Y - 5, | |
| this.x + this.width/2, this.y - sin(this.pulse) * 3 + 5, | |
| this.x + this.width - 5, GROUND_Y - 5); | |
| } else if (this.type === 'gap') { | |
| // Draw gap indicator | |
| stroke(255, 80, 80); | |
| strokeWeight(3); | |
| line(this.x, GROUND_Y, this.x, GROUND_Y + 20); | |
| line(this.x + this.width, GROUND_Y, this.x + this.width, GROUND_Y + 20); | |
| // Warning stripes | |
| for (let i = 0; i < this.width; i += 20) { | |
| fill(255, 80, 80, 100); | |
| noStroke(); | |
| rect(this.x + i, GROUND_Y, 10, 5); | |
| } | |
| } | |
| pop(); | |
| } | |
| } | |
| class Particle { | |
| constructor(x, y, vx, vy, col) { | |
| this.x = x; | |
| this.y = y; | |
| this.vx = vx; | |
| this.vy = vy; | |
| this.col = col; | |
| this.life = 255; | |
| this.size = random(3, 8); | |
| } | |
| update() { | |
| this.x += this.vx; | |
| this.y += this.vy; | |
| this.vy += 0.2; | |
| this.life -= 5; | |
| this.size *= 0.98; | |
| } | |
| display() { | |
| push(); | |
| fill(red(this.col), green(this.col), blue(this.col), this.life); | |
| noStroke(); | |
| ellipse(this.x, this.y, this.size); | |
| pop(); | |
| } | |
| isDead() { | |
| return this.life <= 0 || this.size <= 0.5; | |
| } | |
| } | |
| class BackgroundParticle { | |
| constructor() { | |
| this.x = random(width); | |
| this.y = random(height); | |
| this.size = random(1, 3); | |
| this.speed = random(0.5, 2); | |
| this.opacity = random(50, 150); | |
| } | |
| update() { | |
| this.x -= this.speed; | |
| if (this.x < -10) { | |
| this.x = width + 10; | |
| this.y = random(height); | |
| } | |
| } | |
| display() { | |
| push(); | |
| fill(255, 255, 255, this.opacity); | |
| noStroke(); | |
| ellipse(this.x, this.y, this.size); | |
| pop(); | |
| } | |
| } | |
| function setup() { | |
| let canvas = createCanvas(1200, 600); | |
| canvas.parent('gameContainer'); | |
| player = new Player(); | |
| // Create obstacles | |
| obstacles = []; | |
| for (let obs of OBSTACLE_PATTERN) { | |
| obstacles.push(new Obstacle(obs.x, obs.type, obs.height, obs.width)); | |
| } | |
| // Create background particles | |
| for (let i = 0; i < 50; i++) { | |
| backgroundParticles.push(new BackgroundParticle()); | |
| } | |
| textAlign(CENTER, CENTER); | |
| frameRate(60); | |
| } | |
| function draw() { | |
| // Dynamic background | |
| bgHue = (bgHue + 0.2) % 360; | |
| colorMode(HSB); | |
| background(bgHue, 30, 95); | |
| colorMode(RGB); | |
| // Apply screen shake | |
| if (screenShake > 0) { | |
| translate(random(-screenShake, screenShake), random(-screenShake, screenShake)); | |
| screenShake *= 0.9; | |
| } | |
| // Update and display background particles | |
| for (let particle of backgroundParticles) { | |
| particle.update(); | |
| particle.display(); | |
| } | |
| if (gameState === 'start') { | |
| drawStartScreen(); | |
| return; | |
| } | |
| if (gameState === 'dead') { | |
| drawDeathScreen(); | |
| return; | |
| } | |
| if (gameState === 'completed') { | |
| drawCompletionScreen(); | |
| return; | |
| } | |
| if (gameState === 'playing') { | |
| updateGame(); | |
| displayGame(); | |
| } | |
| } | |
| function updateGame() { | |
| // Move player forward | |
| player.x += GAME_SPEED; | |
| // Update camera | |
| cameraX = player.x - 100; | |
| // Update player | |
| player.update(); | |
| // Update obstacles | |
| for (let obstacle of obstacles) { | |
| obstacle.update(); | |
| } | |
| // Update particles | |
| for (let i = particles.length - 1; i >= 0; i--) { | |
| particles[i].update(); | |
| if (particles[i].isDead()) { | |
| particles.splice(i, 1); | |
| } | |
| } | |
| // Check collisions | |
| for (let obstacle of obstacles) { | |
| if (player.checkCollision(obstacle)) { | |
| gameOver(); | |
| return; | |
| } | |
| } | |
| // Check level completion | |
| if (player.x >= LEVEL_LENGTH) { | |
| gameCompleted(); | |
| return; | |
| } | |
| // Update time and progress | |
| currentTime = (millis() - startTime) / 1000; | |
| levelProgress = floor((player.x / LEVEL_LENGTH) * 100); | |
| // Update UI | |
| document.getElementById('attempts').textContent = attempts; | |
| document.getElementById('time').textContent = currentTime.toFixed(1); | |
| document.getElementById('progress').textContent = levelProgress; | |
| } | |
| function displayGame() { | |
| push(); | |
| translate(-cameraX, 0); | |
| // Draw ground with gradient | |
| for (let i = 0; i < LEVEL_LENGTH + width; i += 20) { | |
| let groundColor = lerpColor(color(150, 200, 255), color(200, 150, 255), | |
| sin(i * 0.01 + frameCount * 0.02) * 0.5 + 0.5); | |
| fill(groundColor); | |
| noStroke(); | |
| rect(i, GROUND_Y, 20, height - GROUND_Y); | |
| } | |
| // Draw grid pattern on ground | |
| stroke(255, 255, 255, 50); | |
| strokeWeight(1); | |
| for (let i = 0; i < LEVEL_LENGTH + width; i += 40) { | |
| line(i, GROUND_Y, i, height); | |
| } | |
| // Draw obstacles | |
| for (let obstacle of obstacles) { | |
| obstacle.display(); | |
| } | |
| // Draw player | |
| player.display(); | |
| // Draw particles | |
| for (let particle of particles) { | |
| particle.display(); | |
| } | |
| // Draw finish line | |
| fill(100, 255, 100); | |
| stroke(255, 255, 255); | |
| strokeWeight(3); | |
| rect(LEVEL_LENGTH, 0, 10, GROUND_Y); | |
| // Finish flag | |
| fill(255, 255, 100); | |
| triangle(LEVEL_LENGTH + 20, 50, LEVEL_LENGTH + 20, 100, LEVEL_LENGTH + 60, 75); | |
| pop(); | |
| } | |
| function drawStartScreen() { | |
| push(); | |
| fill(255, 255, 255, 200); | |
| textSize(48); | |
| textAlign(CENTER, CENTER); | |
| text("GEOMETRY DASH ULTRA", width/2, height/2 - 50); | |
| textSize(24); | |
| fill(255, 255, 255, 150); | |
| text("Press SPACE or CLICK to start", width/2, height/2 + 20); | |
| text("Avoid the spikes and gaps!", width/2, height/2 + 50); | |
| pop(); | |
| } | |
| function drawDeathScreen() { | |
| push(); | |
| fill(255, 80, 80, 200); | |
| textSize(64); | |
| textAlign(CENTER, CENTER); | |
| text("GAME OVER", width/2, height/2 - 50); | |
| textSize(24); | |
| fill(255, 255, 255, 150); | |
| text(`Time survived: ${currentTime.toFixed(1)}s`, width/2, height/2); | |
| text(`Progress: ${levelProgress}%`, width/2, height/2 + 30); | |
| text("Press SPACE or CLICK to restart", width/2, height/2 + 60); | |
| pop(); | |
| } | |
| function drawCompletionScreen() { | |
| push(); | |
| fill(100, 255, 100, 200); | |
| textSize(64); | |
| textAlign(CENTER, CENTER); | |
| text("LEVEL COMPLETE!", width/2, height/2 - 50); | |
| textSize(24); | |
| fill(255, 255, 255, 150); | |
| text(`Final time: ${currentTime.toFixed(1)}s`, width/2, height/2); | |
| text(`Attempts: ${attempts}`, width/2, height/2 + 30); | |
| text("Press SPACE or CLICK to play again", width/2, height/2 + 60); | |
| pop(); | |
| // Celebration particles | |
| if (frameCount % 5 === 0) { | |
| for (let i = 0; i < 5; i++) { | |
| particles.push(new Particle(random(width), random(height), | |
| random(-5, 5), random(-10, -5), | |
| color(random(100, 255), random(100, 255), random(100, 255)))); | |
| } | |
| } | |
| } | |
| function gameOver() { | |
| gameState = 'dead'; | |
| screenShake = 20; | |
| // Death particles | |
| for (let i = 0; i < 20; i++) { | |
| particles.push(new Particle(player.x - cameraX, player.y, | |
| random(-8, 8), random(-8, 8), color(255, 80, 80))); | |
| } | |
| } | |
| function gameCompleted() { | |
| gameState = 'completed'; | |
| } | |
| function startGame() { | |
| gameState = 'playing'; | |
| attempts++; | |
| startTime = millis(); | |
| currentTime = 0; | |
| cameraX = 0; | |
| levelProgress = 0; | |
| // Reset player | |
| player = new Player(); | |
| // Reset obstacles | |
| obstacles = []; | |
| for (let obs of OBSTACLE_PATTERN) { | |
| obstacles.push(new Obstacle(obs.x, obs.type, obs.height, obs.width)); | |
| } | |
| // Clear particles | |
| particles = []; | |
| } | |
| function keyPressed() { | |
| if (key === ' ') { | |
| if (gameState === 'playing') { | |
| player.jump(); | |
| } else { | |
| startGame(); | |
| } | |
| } | |
| } | |
| function mousePressed() { | |
| if (gameState === 'playing') { | |
| player.jump(); | |
| } else { | |
| startGame(); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Geometry Dash Ultra</title> | |
| <style> | |
| body { | |
| margin: 0; | |
| padding: 0; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| font-family: 'Arial', sans-serif; | |
| overflow: hidden; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| height: 100vh; | |
| } | |
| #gameContainer { | |
| position: relative; | |
| border-radius: 20px; | |
| box-shadow: 0 20px 40px rgba(0,0,0,0.3); | |
| overflow: hidden; | |
| } | |
| #ui { | |
| position: absolute; | |
| top: 20px; | |
| left: 20px; | |
| color: white; | |
| font-size: 18px; | |
| font-weight: bold; | |
| text-shadow: 2px 2px 4px rgba(0,0,0,0.5); | |
| z-index: 100; | |
| } | |
| #instructions { | |
| position: absolute; | |
| bottom: 20px; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| color: white; | |
| font-size: 14px; | |
| text-align: center; | |
| opacity: 0.8; | |
| } | |
| .particle { | |
| position: absolute; | |
| pointer-events: none; | |
| border-radius: 50%; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="gameContainer"> | |
| <div id="ui"> | |
| <div>Attempts: <span id="attempts">0</span></div> | |
| <div>Time: <span id="time">0.0</span>s</div> | |
| <div>Progress: <span id="progress">0</span>%</div> | |
| </div> | |
| <div id="instructions"> | |
| Press SPACE or CLICK to jump • Avoid obstacles • Reach the end! | |
| </div> | |
| </div> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.7.0/p5.min.js"></script> | |
| <script src="particles.js"></script> | |
| <script src="audio.js"></script> | |
| <script src="enhanced-game.js"></script> | |
| </body> | |
| </html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Particle System for Epic Visual Effects | |
| class ParticleSystem { | |
| constructor() { | |
| this.particles = []; | |
| this.trails = []; | |
| } | |
| addParticle(x, y, type = 'jump') { | |
| const colors = { | |
| jump: ['#FF6B9D', '#C44569', '#F8B500'], | |
| death: ['#FF4757', '#FF3838', '#FF9500'], | |
| collect: ['#00D9FF', '#0099CC', '#48CAE4'], | |
| trail: ['#A8E6CF', '#7FCDCD', '#5DADE2'] | |
| }; | |
| const color = colors[type][Math.floor(Math.random() * colors[type].length)]; | |
| for (let i = 0; i < (type === 'death' ? 15 : 5); i++) { | |
| this.particles.push({ | |
| x: x + random(-10, 10), | |
| y: y + random(-10, 10), | |
| vx: random(-8, 8), | |
| vy: random(-12, -2), | |
| size: random(3, 8), | |
| color: color, | |
| life: 1.0, | |
| decay: random(0.02, 0.05), | |
| type: type | |
| }); | |
| } | |
| } | |
| addTrail(x, y) { | |
| this.trails.push({ | |
| x: x, | |
| y: y, | |
| size: random(8, 12), | |
| alpha: 0.6, | |
| decay: 0.08 | |
| }); | |
| } | |
| update() { | |
| // Update particles | |
| for (let i = this.particles.length - 1; i >= 0; i--) { | |
| const p = this.particles[i]; | |
| p.x += p.vx; | |
| p.y += p.vy; | |
| p.vy += 0.3; // gravity | |
| p.life -= p.decay; | |
| p.size *= 0.98; | |
| if (p.life <= 0 || p.size < 0.5) { | |
| this.particles.splice(i, 1); | |
| } | |
| } | |
| // Update trails | |
| for (let i = this.trails.length - 1; i >= 0; i--) { | |
| const t = this.trails[i]; | |
| t.alpha -= t.decay; | |
| t.size *= 0.95; | |
| if (t.alpha <= 0) { | |
| this.trails.splice(i, 1); | |
| } | |
| } | |
| } | |
| render() { | |
| // Render trails | |
| for (const trail of this.trails) { | |
| push(); | |
| noStroke(); | |
| fill(168, 230, 207, trail.alpha * 255); | |
| ellipse(trail.x, trail.y, trail.size); | |
| pop(); | |
| } | |
| // Render particles | |
| for (const p of this.particles) { | |
| push(); | |
| noStroke(); | |
| const c = color(p.color); | |
| c.setAlpha(p.life * 255); | |
| fill(c); | |
| if (p.type === 'death') { | |
| star(p.x, p.y, p.size, p.size * 0.5, 5); | |
| } else { | |
| ellipse(p.x, p.y, p.size); | |
| } | |
| pop(); | |
| } | |
| } | |
| } | |
| function star(x, y, radius1, radius2, npoints) { | |
| let angle = TWO_PI / npoints; | |
| let halfAngle = angle / 2.0; | |
| beginShape(); | |
| for (let a = 0; a < TWO_PI; a += angle) { | |
| let sx = x + cos(a) * radius2; | |
| let sy = y + sin(a) * radius2; | |
| vertex(sx, sy); | |
| sx = x + cos(a + halfAngle) * radius1; | |
| sy = y + sin(a + halfAngle) * radius1; | |
| vertex(sx, sy); | |
| } | |
| endShape(CLOSE); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: 'Arial', sans-serif; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| min-height: 100vh; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| overflow: hidden; | |
| } | |
| .game-container { | |
| position: relative; | |
| border-radius: 20px; | |
| box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3); | |
| overflow: hidden; | |
| } | |
| canvas { | |
| display: block; | |
| border-radius: 20px; | |
| } | |
| .ui-overlay { | |
| position: absolute; | |
| top: 20px; | |
| left: 20px; | |
| right: 20px; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| z-index: 10; | |
| pointer-events: none; | |
| } | |
| .attempts-counter, .time-counter { | |
| background: rgba(255, 255, 255, 0.2); | |
| backdrop-filter: blur(10px); | |
| padding: 10px 20px; | |
| border-radius: 25px; | |
| color: white; | |
| font-weight: bold; | |
| font-size: 16px; | |
| border: 1px solid rgba(255, 255, 255, 0.3); | |
| } | |
| .start-screen, .death-screen, .completion-screen { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| display: flex; | |
| flex-direction: column; | |
| justify-content: center; | |
| align-items: center; | |
| background: rgba(0, 0, 0, 0.8); | |
| backdrop-filter: blur(10px); | |
| color: white; | |
| z-index: 20; | |
| } | |
| .start-screen h1, .death-screen h1, .completion-screen h1 { | |
| font-size: 48px; | |
| margin-bottom: 20px; | |
| text-shadow: 0 0 20px rgba(255, 255, 255, 0.5); | |
| } | |
| .start-screen p, .death-screen p, .completion-screen p { | |
| font-size: 20px; | |
| margin-bottom: 30px; | |
| opacity: 0.8; | |
| } | |
| .start-button, .restart-button, .continue-button { | |
| background: linear-gradient(135deg, #667eea, #764ba2); | |
| border: none; | |
| padding: 15px 40px; | |
| font-size: 18px; | |
| color: white; | |
| border-radius: 30px; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); | |
| } | |
| .start-button:hover, .restart-button:hover, .continue-button:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 8px 25px rgba(0, 0, 0, 0.4); | |
| } | |
| .hidden { | |
| display: none !important; | |
| } | |
| @keyframes shake { | |
| 0%, 100% { transform: translateX(0); } | |
| 25% { transform: translateX(-5px); } | |
| 75% { transform: translateX(5px); } | |
| } | |
| .shake { | |
| animation: shake 0.3s ease-in-out; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment