How I created a fully functional Flappy Bird game and learned valuable lessons about game development, debugging, and problem-solving along the way pair with AmazonQ CLI
🎮 The Challenge
Ever wondered what it takes to recreate one of the most addictive mobile games of all time? I recently embarked on a journey to build Flappy Bird from scratch using modern web technologies, and let me tell you - it was quite an adventure!
Tech Stack:
- 🎯 Phaser 3 - Game engine
- 📝 TypeScript - Type safety and better DX
- 📦 Rollup - Module bundling
- 🎨 HTML5 Canvas - Rendering
- 🔊 Web Audio API - Sound effects
🚀 The Development Journey
Phase 1: Setting Up the Foundation
The project started with a clean architecture approach:
flappy-bird-game/ ├── src/ │ ├── scenes/ # Game scenes (Menu, Game, GameOver) │ ├── objects/ # Game entities (Bird, PipeManager, Background) │ ├── config/ # Game configuration │ └── assets/ # Images and sounds
Key decisions:
- TypeScript for better code maintainability
- Scene-based architecture for clean separation of concerns
- Component-based game objects for reusability
Phase 2: The Physics and Animation
Creating the bird was surprisingly fun! Here's what made it special:
export class Bird { private sprite: Phaser.Physics.Arcade.Sprite; flap(): void { this.sprite.setVelocityY(GameConfig.bird.flapStrength); // -350 px/s this.flapSound.play(); } update(): void { // Realistic rotation based on velocity if (this.sprite.body.velocity.y < 0) { this.sprite.setRotation(-0.5); // Upward tilt } else { this.sprite.setRotation(Math.min(0.5, this.sprite.rotation + 0.05)); } } }
The magic details:
- ✨ Smooth rotation that follows physics
- 🎵 Sound feedback on every flap
- 🎭 3-frame animation for wing flapping
Phase 3: The Great Scoring System Debugging Saga 🐛
This is where things got really interesting. What seemed like a simple feature turned into a fascinating debugging adventure!
The Problem: Score Wasn't Working! 😱
// ❌ This approach FAILED miserably this.physics.add.overlap(bird, pipes, (bird, object) => { if (object.getData('isScoreTrigger')) { score++; // Never executed! } });
The symptoms:
- Game loaded perfectly ✅
- Bird physics worked ✅
- Pipes generated correctly ✅
- Score remained stubbornly at 0 ❌
The Investigation 🔍
Through extensive console logging, I discovered:
- Overlap detection wasn't firing for invisible score triggers
- Mixed object types in physics groups caused conflicts
- Phaser's collision system was more complex than expected
// Debug logs revealed the truth: console.log('Bird position:', birdX, birdY); // ✅ Working console.log('Score trigger created:', triggerX); // ✅ Working console.log('Overlap detected:', object); // ❌ Never appeared!
The Breakthrough: Manual Position Tracking 💡
Instead of fighting with Phaser's physics system, I implemented a custom scoring algorithm:
private checkScoreManually(): void { const birdX = 50; // Bird stays at fixed X position this.scoreTriggers.forEach(trigger => { // Pipes move left: X(t) = X₀ + velocity × deltaTime trigger.x += GameConfig.pipes.speed * deltaTime; // -200 px/s // Score when pipe passes bird (left movement) if (!trigger.scored && trigger.x <= birdX && trigger.x > birdX - 50) { trigger.scored = true; this.passedPipes++; this.scene.events.emit('score-updated', this.passedPipes); } }); }
The key insight: In Flappy Bird, the bird doesn't move horizontally - the pipes move toward the bird! 🤯
🎯 Technical Highlights
1. Smart Asset Management
// Automated asset copying during build const copyAssets = () => { copyDir('./src/assets', './dist/src/assets'); console.log('Assets copied successfully!'); };
2. Persistent High Score System
// Simple but effective localStorage implementation const highScore = localStorage.getItem('flappyHighScore') || 0; if (currentScore > highScore) { localStorage.setItem('flappyHighScore', String(currentScore)); showNewRecordAnimation(); // ✨ Visual feedback }
3. Performance Optimizations
- Object pooling for pipes
- Manual cleanup of off-screen objects
- Efficient collision detection only where needed
- Optimized sprite animations
🏆 The Final Result
After solving the scoring system puzzle, everything clicked into place:
Game Features:
- ✅ Smooth 60fps gameplay
- ✅ Accurate collision detection
- ✅ Persistent high scores
- ✅ Sound effects and animations
- ✅ Responsive controls (mouse + keyboard)
- ✅ Mobile-friendly design
🎓 Lessons Learned
1. Sometimes Simple Solutions Win
Complex framework features aren't always the answer. My manual position tracking turned out to be more reliable than Phaser's built-in collision system.
2. Debug Logging is Your Best Friend
console.log('🎯 Manual score detection! Pipe passed bird at X:', triggerX); console.log('✅ New score:', this.passedPipes);
Those emoji-filled logs made debugging actually enjoyable!
3. Understanding Game Mechanics Matters
I initially misunderstood how Flappy Bird works. The bird doesn't move horizontally - this insight was crucial for fixing the scoring system.
4. TypeScript + Game Development = ❤️
Type safety caught numerous bugs before runtime and made refactoring much safer.
🔧 The Architecture That Worked
graph TB A[GameScene] --> B[Bird] A --> C[PipeManager] A --> D[Background] C --> E[Manual Score Detection] B --> F[Physics & Animation] A --> G[Collision System] H[GameConfig] --> A
Key Components:
- GameScene: Orchestrates everything
- Bird: Physics-based player character
- PipeManager: Obstacle generation + custom scoring
- Background: Scrolling environment
- GameConfig: Centralized configuration
🚀 Try It Yourself!
Want to build your own version? Here's the quick start:
# Clone and setup git clone <your-repo> cd flappy-bird-game npm install # Build and run npm run build npm run serve # Open http://localhost:8080
Pro tips for aspiring game developers:
- Start with a simple game loop
- Add one feature at a time
- Debug with extensive logging
- Don't be afraid to try different approaches
- Test frequently on different devices
🎮 What's Next?
The game is fully playable, but there's always room for improvement:
- 📱 Mobile touch controls
- 🎨 Particle effects for enhanced visuals
- 🏆 Online leaderboards
- 🎵 Background music
- 🔧 Level editor
💭 Final Thoughts
Building Flappy Bird taught me that game development is as much about problem-solving as it is about coding. The scoring system bug that initially frustrated me became the most educational part of the entire project.
The real victory wasn't just creating a working game - it was learning to debug systematically, think creatively about solutions, and persist through challenging problems.
Whether you're a seasoned developer or just starting out, I encourage you to try building a simple game. You'll be surprised by how much you learn about programming, problem-solving, and the satisfaction of creating something interactive and fun!
🔗 Resources & Links
- 🎮 Play the Game
- 💻 Source Code
- 📚 AmazonQ CLI Docs
- ✨ AmazonQ CLI Challenges and T-shirts
- 📚 Phaser 3 Documentation
- 🎯 TypeScript Handbook
What's your experience with game development? Have you faced similar debugging challenges? Share your stories in the comments below! 👇
Happy coding, and may your birds always flap smoothly! 🐦✨
Tags: #AmazonQCLI #gamedev #typescript #phaser #javascript #webdev #debugging #flappybird #html5games
Top comments (0)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.