Build a Flappy Bird Clone with TCJSGame in 30 Minutes
Ready to create your first game with TCJSGame? In this tutorial, we'll build a complete Flappy Bird clone from scratch in just 30 minutes. No prior game development experience requiredโjust basic JavaScript knowledge!
What We'll Build
By the end of this tutorial, you'll have a fully functional Flappy Bird game with:
- Bird character with gravity and flapping mechanics
- Randomly generated pipes that scroll toward the bird
- Collision detection to end the game when you hit pipes
- Score system that increases as you pass through pipes
- Game over screen with restart functionality
Prerequisites
- Basic HTML and JavaScript knowledge
- A text editor (VS Code, Sublime Text, etc.)
- A modern web browser
Step 1: Project Setup (2 minutes)
Create a new HTML file and set up the basic TCJSGame structure:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flappy Bird Clone - TCJSGame</title>
<style>
body {
margin: 0;
padding: 20px;
background: linear-gradient(to bottom, #87CEEB, #E0F7FA);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
font-family: Arial, sans-serif;
}
#game-container {
text-align: center;
}
canvas {
border: 3px solid #2c3e50;
border-radius: 10px;
box-shadow: 0 4px 15px rgba(0,0,0,0.3);
}
.instructions {
margin-top: 15px;
color: #2c3e50;
font-size: 18px;
}
</style>
</head>
<body>
<div id="game-container">
<h1>๐ฆ Flappy Bird Clone</h1>
<div class="instructions">Press SPACE or CLICK to flap!</div>
<!-- Game canvas will be inserted here -->
</div>
<!-- Include TCJSGame v3 -->
<script src="https://tcjsgame.vercel.app/mat/tcjsgame-v3.js"></script>
<script>
// Our game code will go here!
</script>
</body>
</html>
Step 2: Initialize the Game (3 minutes)
Add the TCJSGame initialization code inside the <script>
tags:
// Initialize game display
const display = new Display();
display.start(400, 600, document.getElementById('game-container'));
// Game state variables
let gameStarted = false;
let gameOver = false;
let score = 0;
// Create bird character
const bird = new Component(30, 30, "#FFD700", 100, 250, "rect");
bird.physics = true;
bird.gravity = 0.5;
bird.bounce = 0.3;
display.add(bird);
// Create score display
const scoreText = new Component(24, 24, "white", 10, 10, "text");
scoreText.text = `Score: ${score}`;
display.add(scoreText);
console.log("๐ฎ Flappy Bird initialized!");
Step 3: Bird Mechanics (5 minutes)
Implement the bird's flapping movement and input handling:
// Flap function - makes the bird jump upward
function flap() {
if (!gameOver) {
bird.speedY = -8;
gameStarted = true;
}
}
// Input handling
window.addEventListener('keydown', (e) => {
if (e.code === 'Space') {
flap();
}
});
display.canvas.addEventListener('click', () => {
flap();
});
// Add visual feedback for the bird
function updateBirdAppearance() {
// Make bird "tilt" based on vertical speed
bird.angle = bird.speedY * 0.05;
// Color change for visual feedback
if (bird.speedY < -5) {
bird.color = "#FF6B6B"; // Red when flapping hard
} else {
bird.color = "#FFD700"; // Gold normally
}
}
Step 4: Pipe System (8 minutes)
Create the pipe generation and movement system:
const pipes = [];
const pipeGap = 150; // Space between top and bottom pipes
const pipeWidth = 60;
let pipeTimer = 0;
// Create a new pipe pair
function createPipe() {
const pipeX = display.canvas.width;
const gapPosition = Math.random() * 200 + 150; // Random gap position
// Top pipe
const topPipe = new Component(pipeWidth, gapPosition - pipeGap, "#27ae60", pipeX, 0, "rect");
topPipe.speedX = -3;
topPipe.name = "pipe";
display.add(topPipe);
// Bottom pipe
const bottomPipe = new Component(pipeWidth, display.canvas.height - gapPosition, "#27ae60",
pipeX, gapPosition, "rect");
bottomPipe.speedX = -3;
bottomPipe.name = "pipe";
display.add(bottomPipe);
pipes.push({ top: topPipe, bottom: bottomPipe, scored: false });
}
// Update pipes and check for scoring
function updatePipes() {
pipeTimer++;
// Create new pipes every 100 frames
if (pipeTimer > 100 && gameStarted && !gameOver) {
createPipe();
pipeTimer = 0;
}
// Update pipe positions and check scoring
pipes.forEach((pipePair, index) => {
// Remove pipes that are off-screen
if (pipePair.top.x + pipeWidth < 0) {
display.add(pipePair.top, 1); // Remove from display
display.add(pipePair.bottom, 1);
pipes.splice(index, 1);
}
// Score when bird passes through pipe
if (!pipePair.scored && pipePair.top.x + pipeWidth < bird.x) {
pipePair.scored = true;
score++;
scoreText.text = `Score: ${score}`;
}
});
}
Step 5: Collision Detection (5 minutes)
Implement game over conditions and collision checking:
function checkCollisions() {
// Check if bird hits the ground or ceiling
if (bird.y >= display.canvas.height - bird.height || bird.y <= 0) {
endGame();
return;
}
// Check collisions with pipes
for (let pipePair of pipes) {
if (bird.crashWith(pipePair.top) || bird.crashWith(pipePair.bottom)) {
endGame();
return;
}
}
}
function endGame() {
if (!gameOver) {
gameOver = true;
console.log("๐ Game Over! Final score:", score);
// Show game over message
const gameOverText = new Component(28, 28, "#e74c3c", 120, 250, "text");
gameOverText.text = `Game Over! Score: ${score}`;
display.add(gameOverText);
// Add restart instruction
const restartText = new Component(18, 18, "white", 140, 290, "text");
restartText.text = "Refresh to play again!";
display.add(restartText);
}
}
Step 6: Main Game Loop (5 minutes)
Bring everything together in the update function:
function update() {
if (!gameStarted) return;
updateBirdAppearance();
updatePipes();
if (!gameOver) {
checkCollisions();
}
// Add some visual effects to pipes
pipes.forEach(pipePair => {
// Make pipes pulse slightly for visual interest
const pulse = Math.sin(display.frameNo * 0.1) * 2;
pipePair.top.width = pipeWidth + pulse;
pipePair.bottom.width = pipeWidth + pulse;
});
}
// Add some starting instructions
const startText = new Component(20, 20, "#2c3e50", 120, 200, "text");
startText.text = "Press SPACE or CLICK to start!";
display.add(startText);
// Remove start text when game begins
function checkGameStart() {
if (gameStarted && startText) {
display.add(startText, 1); // Remove from display
}
}
Step 7: Enhanced Update Function (2 minutes)
Update the main update function to include all features:
function finalUpdate() {
checkGameStart();
updateBirdAppearance();
updatePipes();
if (gameStarted && !gameOver) {
checkCollisions();
}
}
// Replace the original update function
// Note: In practice, you'd integrate this step by step
๐ฎ Complete Source Code
Here's the complete code for your Flappy Bird clone:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flappy Bird Clone - TCJSGame</title>
<style>
body { margin: 0; padding: 20px; background: linear-gradient(to bottom, #87CEEB, #E0F7FA);
display: flex; justify-content: center; align-items: center; min-height: 100vh;
font-family: Arial, sans-serif; }
#game-container { text-align: center; }
canvas { border: 3px solid #2c3e50; border-radius: 10px; box-shadow: 0 4px 15px rgba(0,0,0,0.3); }
.instructions { margin-top: 15px; color: #2c3e50; font-size: 18px; }
</style>
</head>
<body>
<div id="game-container">
<h1>๐ฆ Flappy Bird Clone</h1>
<div class="instructions">Press SPACE or CLICK to flap!</div>
</div>
<script src="https://tcjsgame.vercel.app/mat/tcjsgame-v3.js"></script>
<script>
const display = new Display();
display.start(400, 600, document.getElementById('game-container'));
let gameStarted = false, gameOver = false, score = 0;
const pipes = [];
const pipeGap = 150;
let pipeTimer = 0;
// Bird
const bird = new Component(30, 30, "#FFD700", 100, 250, "rect");
bird.physics = true; bird.gravity = 0.5; bird.bounce = 0.3;
display.add(bird);
// UI
const scoreText = new Component(24, 24, "white", 10, 10, "text");
scoreText.text = `Score: ${score}`;
display.add(scoreText);
const startText = new Component(20, 20, "#2c3e50", 120, 200, "text");
startText.text = "Press SPACE or CLICK to start!";
display.add(startText);
// Input
function flap() {
if (!gameOver) { bird.speedY = -8; gameStarted = true; }
}
window.addEventListener('keydown', (e) => { if (e.code === 'Space') flap(); });
display.canvas.addEventListener('click', flap);
// Pipes
function createPipe() {
const pipeX = display.canvas.width;
const gapPosition = Math.random() * 200 + 150;
const topPipe = new Component(60, gapPosition - pipeGap, "#27ae60", pipeX, 0, "rect");
const bottomPipe = new Component(60, display.canvas.height - gapPosition, "#27ae60",
pipeX, gapPosition, "rect");
topPipe.speedX = bottomPipe.speedX = -3;
display.add(topPipe); display.add(bottomPipe);
pipes.push({ top: topPipe, bottom: bottomPipe, scored: false });
}
function update() {
// Start check
if (gameStarted && startText) display.add(startText, 1);
// Bird effects
bird.angle = bird.speedY * 0.05;
bird.color = bird.speedY < -5 ? "#FF6B6B" : "#FFD700";
// Pipes
pipeTimer++;
if (pipeTimer > 100 && gameStarted && !gameOver) {
createPipe(); pipeTimer = 0;
}
pipes.forEach((pipePair, index) => {
pipePair.top.x += pipePair.top.speedX;
pipePair.bottom.x += pipePair.bottom.speedX;
if (pipePair.top.x + 60 < 0) {
display.add(pipePair.top, 1); display.add(pipePair.bottom, 1);
pipes.splice(index, 1);
}
if (!pipePair.scored && pipePair.top.x + 60 < bird.x) {
pipePair.scored = true; score++;
scoreText.text = `Score: ${score}`;
}
if (!gameOver && (bird.crashWith(pipePair.top) || bird.crashWith(pipePair.bottom))) {
endGame();
}
});
// Boundaries
if (!gameOver && (bird.y >= display.canvas.height - bird.height || bird.y <= 0)) {
endGame();
}
}
function endGame() {
gameOver = true;
const gameOverText = new Component(28, 28, "#e74c3c", 120, 250, "text");
gameOverText.text = `Game Over! Score: ${score}`;
display.add(gameOverText);
}
</script>
</body>
</html>
๐ Next Steps
Congratulations! You've built a complete Flappy Bird clone with TCJSGame. Here are some ways to extend your game:
- Add sound effects for flapping, scoring, and game over
- Implement particle effects when the bird crashes
- Add different pipe types with varying difficulties
- Create a high score system using localStorage
- Add power-ups that give temporary abilities
๐ What You Learned
- TCJSGame basics: Display setup, component creation, physics
- Game loop management: Update functions and frame-based logic
- Collision detection: Using TCJSGame's built-in crash detection
- Game state management: Tracking game started/over states
- User input handling: Keyboard and mouse events
๐ฏ Challenge Yourself
Try implementing these additional features:
- Add a countdown before the game starts
- Create moving background elements
- Implement different bird characters
- Add difficulty progression (faster pipes over time)
What will you build next with TCJSGame? Share your Flappy Bird variations in the comments below! If you got stuck or have questions, feel free to askโI'm here to help you on your game development journey.
Follow me for more TCJSGame tutorials and game development tips!
Top comments (0)