├── .vscode
└── settings.json
├── assets
├── bird.png
├── road.png
├── column.png
├── image.png
└── background.png
├── index.html
├── README.md
└── app.js
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "liveServer.settings.port": 5501
3 | }
--------------------------------------------------------------------------------
/assets/bird.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FaithMutua-code/flappy-bird/HEAD/assets/bird.png
--------------------------------------------------------------------------------
/assets/road.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FaithMutua-code/flappy-bird/HEAD/assets/road.png
--------------------------------------------------------------------------------
/assets/column.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FaithMutua-code/flappy-bird/HEAD/assets/column.png
--------------------------------------------------------------------------------
/assets/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FaithMutua-code/flappy-bird/HEAD/assets/image.png
--------------------------------------------------------------------------------
/assets/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FaithMutua-code/flappy-bird/HEAD/assets/background.png
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Flappy Bird Clone
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Flappy Bird Clone
2 |
3 | A simple Flappy Bird clone built with Phaser 3, featuring basic physics, collision detection, and obstacle avoidance gameplay.
4 |
5 |
6 |
7 |
8 | Gameplay screenshot showing bird and columns
9 |
10 |
11 | ## Features
12 |
13 | - Classic Flappy Bird-style gameplay
14 | - Physics-based movement with gravity
15 | - Randomly generated obstacles (columns)
16 | - Collision detection with ground and obstacles
17 | - Win condition after passing all obstacles
18 | - Responsive controls (keyboard only)
19 | - Game state management (start, play, game over, win)
20 |
21 | ## Technologies Used
22 |
23 | - [Phaser 3](https://phaser.io/) - HTML5 game framework
24 | - JavaScript
25 | - HTML5
26 |
27 | ## How to Play
28 |
29 | 1. Press the **UP ARROW** key to start the game
30 | 2. Keep pressing **UP ARROW** to make the bird fly upward
31 | 3. Navigate through the gaps between the columns
32 | 4. Avoid hitting the columns or the ground
33 | 5. Pass all three columns to win!
34 |
35 | ## Installation
36 |
37 | To run the game locally:
38 |
39 | 1. Clone this repository:
40 | ```bash
41 | git clone https://github.com/your-username/flappy-bird-clone.git
42 |
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | let config = {
2 | type: Phaser.AUTO,
3 | width: 800,
4 | height: 600,
5 | physics: {
6 | default: 'arcade',
7 | arcade: {
8 | gravity: { y: 300 },
9 | debug: false // Change to true to visualize hitboxes
10 | }
11 | },
12 | scene: {
13 | preload: preload,
14 | create: create,
15 | update: update
16 | }
17 | };
18 |
19 | let game = new Phaser.Game(config);
20 |
21 | // Game variables
22 | let bird;
23 | let hasLanded = false;
24 | let hasBumped = false;
25 | let gameActive = false;
26 | let cursors;
27 | let messagesToPlayer;
28 | let birdSpeed = 100;
29 | let topColumns;
30 | let bottomColumns;
31 | const COLUMN_SPACING = 300;
32 | const GAP_SIZE = 150;
33 |
34 | function preload() {
35 | this.load.image('background', 'assets/background.png');
36 | this.load.image('road', 'assets/road.png');
37 | this.load.image('column', 'assets/column.png');
38 | this.load.spritesheet('bird', 'assets/bird.png', {
39 | frameWidth: 64,
40 | frameHeight: 96
41 | });
42 | }
43 |
44 | function create() {
45 | // Background
46 | const background = this.add.image(0, 0, 'background').setOrigin(0, 0);
47 |
48 | // Ground (road)
49 | const roads = this.physics.add.staticGroup();
50 | const road = roads.create(400, 568, 'road').setScale(2).refreshBody();
51 |
52 | // Columns (obstacles)
53 | topColumns = this.physics.add.staticGroup();
54 | bottomColumns = this.physics.add.staticGroup();
55 |
56 | // Generate column pairs with vertical gaps
57 | for (let i = 0; i < 3; i++) {
58 | let x = 400 + i * COLUMN_SPACING;
59 | let gapY = Phaser.Math.Between(150, 400); // vertical center of gap
60 |
61 | // Create top column just above the gap
62 | let top = topColumns.create(x, gapY - GAP_SIZE / 2, 'column');
63 | top.setOrigin(0.5, 1); // bottom aligned
64 | top.refreshBody();
65 |
66 | // Create bottom column just below the gap
67 | let bottom = bottomColumns.create(x, gapY + GAP_SIZE / 2, 'column');
68 | bottom.setOrigin(0.5, 0); // top aligned
69 | bottom.refreshBody();
70 | }
71 |
72 | // Bird setup
73 | bird = this.physics.add.sprite(100, 300, 'bird').setScale(2);
74 | bird.setBounce(0.2);
75 | bird.setCollideWorldBounds(true);
76 |
77 | // Collisions
78 | this.physics.add.collider(bird, road, () => {
79 | hasLanded = true;
80 | if (gameActive) gameOver(this);
81 | });
82 |
83 | this.physics.add.collider(bird, topColumns, () => {
84 | hasBumped = true;
85 | if (gameActive) gameOver(this);
86 | });
87 |
88 | this.physics.add.collider(bird, bottomColumns, () => {
89 | hasBumped = true;
90 | if (gameActive) gameOver(this);
91 | });
92 |
93 | // Controls
94 | cursors = this.input.keyboard.createCursorKeys();
95 |
96 | this.input.keyboard.on('keydown-UP', () => {
97 | if (!gameActive && (hasLanded || hasBumped)) {
98 | resetGame(this);
99 | return;
100 | }
101 |
102 | if (!gameActive && !hasLanded && !hasBumped) {
103 | startGame(this);
104 | }
105 |
106 | if (gameActive) {
107 | bird.setVelocityY(-160);
108 | }
109 | });
110 |
111 | // UI message
112 | messagesToPlayer = this.add.text(0, 0, 'Press UP ARROW to start', {
113 | fontFamily: '"Comic Sans MS", times, serif',
114 | fontSize: "20px",
115 | color: "white",
116 | backgroundColor: "black",
117 | padding: { x: 10, y: 5 }
118 | });
119 |
120 | Phaser.Display.Align.In.BottomCenter(messagesToPlayer, background, 0, 50);
121 | }
122 |
123 | function update() {
124 | if (gameActive && !hasLanded && !hasBumped) {
125 | bird.setVelocityX(birdSpeed);
126 |
127 | // Win condition
128 | if (bird.x > 400 + (COLUMN_SPACING * 3)) {
129 | gameActive = false;
130 | bird.setVelocityX(0);
131 | messagesToPlayer.text = '🎉 Congratulations! You won! 🎉';
132 | }
133 | }
134 | }
135 |
136 | function startGame(scene) {
137 | gameActive = true;
138 | bird.setVelocityX(birdSpeed);
139 | messagesToPlayer.text = 'Press UP to fly!\nAvoid the columns!';
140 | }
141 |
142 | function gameOver(scene) {
143 | gameActive = false;
144 | bird.setVelocityX(0);
145 | messagesToPlayer.text = '💥 Game Over!\nPress UP to try again';
146 | }
147 |
148 | function resetGame(scene) {
149 | bird.setPosition(100, 300);
150 | bird.setVelocity(0);
151 | hasLanded = false;
152 | hasBumped = false;
153 | startGame(scene);
154 | }
155 | // Reset columns
156 | topColumns.clear(true, true);
157 | bottomColumns.clear(true, true);
158 |
159 | // Regenerate columns
160 | for (let i = 0; i < 3; i++) {
161 | let x = 400 + i * COLUMN_SPACING;
162 | let gapY = Phaser.Math.Between(150, 400); // vertical center of gap
163 |
164 | let top = topColumns.create(x, gapY - GAP_SIZE / 2, 'column');
165 | top.setOrigin(0.5, 1);
166 | top.refreshBody();
167 |
168 | let bottom = bottomColumns.create(x, gapY + GAP_SIZE / 2, 'column');
169 | bottom.setOrigin(0.5, 0);
170 | bottom.refreshBody();
171 | }
--------------------------------------------------------------------------------