Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@

In addtion to having the original features of Flappy Bird, we implemented a brand new way to play the game -- instead of sideviewing the bird, you can view it from the back. There's also day time theme and night time theme.

<img src=".github/img/gameplay.png" width="550">
<img src=".github/img/night_theme.png" width="550">
<img src=".github/img/back.png" width="550">

## Run
1. Clone or download this repository.
Expand Down
199 changes: 160 additions & 39 deletions flappy-bird.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export class Bird extends Scene {
text: new Text_Line( 35 ),
cylinder: new defs.Cylindrical_Tube(150, 300, [[0, 2], [1, 2]]),
capped_cylinder: new defs.Rounded_Capped_Cylinder(150, 300, [[0, 2], [1, 2]]),

};

this.textures = {
Expand Down Expand Up @@ -66,13 +66,13 @@ export class Bird extends Scene {
background: new Material(
new defs.Fake_Bump_Map(1), {
color: hex_color("#000000"),
ambient: 1,
ambient: 1,
texture: this.textures.background,
}),
background_night: new Material(
new defs.Fake_Bump_Map(1), {
color: hex_color("#000000"),
ambient: 1,
ambient: 1,
texture: this.textures.background_night,
}),
game_end: new Material(
Expand All @@ -83,8 +83,8 @@ export class Bird extends Scene {
}),
text_image: new Material(
new defs.Textured_Phong(1), {
ambient: 1,
diffusivity: 0,
ambient: 1,
diffusivity: 0,
specularity: 0,
texture: new Texture("assets/text.png"),
}),
Expand Down Expand Up @@ -113,13 +113,43 @@ export class Bird extends Scene {
this.score = 0;
this.highest_score = 0;
this.night_theme = false;


this.power_up_active = false;
this.power_up_duration = 2; // Duration in seconds
this.power_up_timer = 0;
this.normal_acceleration = 20; // Normal gravity
this.reduced_acceleration = 4; // Reduced gravity when power-up is active
this.power_up_position = vec3(0, Math.random() * 10 + 5, 0);


this.last_speed_change_time = 0; // Last time the speed changed
this.total_normal_speed_duration = 0; // Total time spent at normal speed
this.total_increased_speed_duration = 0;


this.power_up_visible = false;
//this.power_up_position = vec3(0, 5, 0);
this.power_up_last_activation_time = 0;
this.power_up_activation_interval = Math.random() * 10 + 5;

this.fastforward_active = false;
this.fastforward_timer = 0;
this.normal_speed = 5;

this.increased_speed = 6;
this.fastforward_position = vec3(Math.random() * 20, Math.random() * 10 + 5, 0);

}

make_control_panel() {
this.key_triggered_button("Up", ["u"], () => {
this.click_time = this.t;
this.base_y = this.y;
if (!this.game_start) {

let adjusted_time = this.total_normal_speed_duration * this.normal_speed + this.total_increased_speed_duration * this.increased_speed;

this.game_start_time = this.t + this.starting_distance / this.game_speed;
console.log(this.game_start_time);
}
Expand All @@ -142,19 +172,21 @@ export class Bird extends Scene {
this.new_line();

const acceleration_controls = this.control_panel.appendChild(document.createElement("span"));



this.new_line();

const initial_v_y_controls = this.control_panel.appendChild(document.createElement("span"));


this.key_triggered_button("Change theme", ["b"], ()=> {
this.night_theme = !this.night_theme;
});
}



draw_box(context, program_state, model_transform, color) {
this.shapes.cube.draw(context, program_state, model_transform, this.materials.plastic.override({color: color}));
}
Expand All @@ -168,19 +200,19 @@ export class Bird extends Scene {

draw_wings(context, program_state, model_transform) {
const left_wing = model_transform.times(Mat4.translation(-0.8, -0.4, -0.4))
.times(Mat4.scale(0.2, 0.6, 0.8));
.times(Mat4.scale(0.2, 0.6, 0.8));
const right_wing = model_transform.times(Mat4.translation(0.8, -0.4, -0.4))
.times(Mat4.scale(0.2, 0.6, 0.8));
.times(Mat4.scale(0.2, 0.6, 0.8));
this.shapes.sun.draw(context, program_state, left_wing, this.materials.plastic.override({color: color(1, 1, 1, 1)}));
this.shapes.sun.draw(context, program_state, right_wing, this.materials.plastic.override({color: color(1, 1, 1, 1)}));
}

draw_mouth(context, program_state, model_transform) {
const lip_color = hex_color("#FE9800");
const upper_lip = model_transform.times(Mat4.translation(0, 0, 1))
.times(Mat4.scale(0.8, 0.2, 0.6));
.times(Mat4.scale(0.8, 0.2, 0.6));
const lower_lip = model_transform.times(Mat4.translation(0, -0.3, 0.5))
.times(Mat4.scale(0.7, 0.2, 0.8));
.times(Mat4.scale(0.7, 0.2, 0.8));
this.shapes.cube.draw(context, program_state, upper_lip, this.materials.plastic.override({color: lip_color}));
this.shapes.cube.draw(context, program_state, lower_lip, this.materials.plastic.override({color: lip_color}));
}
Expand All @@ -190,16 +222,16 @@ export class Bird extends Scene {
const black = hex_color("#000000");
// right eye
const right_bg_transform = model_transform.times(Mat4.translation(-0.75, 0.35, 0.7))
.times(Mat4.scale(0.2, 0.4, 0.4));
.times(Mat4.scale(0.2, 0.4, 0.4));
const right_pupil_transform = model_transform.times(Mat4.translation(-0.8, 0.4, 0.8))
.times(Mat4.scale(0.2, 0.20, 0.15));
.times(Mat4.scale(0.2, 0.20, 0.15));
this.shapes.sun.draw(context, program_state, right_bg_transform, this.materials.plastic.override({color: white}));
this.shapes.sun.draw(context, program_state, right_pupil_transform, this.materials.plastic.override({color: black}));
// left eye
const left_bg_transform = model_transform.times(Mat4.translation(0.75, 0.35, 0.7))
.times(Mat4.scale(0.2, 0.4, 0.4));
.times(Mat4.scale(0.2, 0.4, 0.4));
const left_pupil_transform = model_transform.times(Mat4.translation(0.8, 0.4, 0.8))
.times(Mat4.scale(0.2, 0.2, 0.15));
.times(Mat4.scale(0.2, 0.2, 0.15));
this.shapes.sun.draw(context, program_state, left_bg_transform, this.materials.plastic.override({color: white}));
this.shapes.sun.draw(context, program_state, left_pupil_transform, this.materials.plastic.override({color: black}));
}
Expand Down Expand Up @@ -270,7 +302,7 @@ export class Bird extends Scene {

//draw top pipe
const top_pipe_model_transform = model_transform.times(Mat4.translation(0, this.pipe_gap - (9 - pipe_len), i * this.pipe_distance))
.times(Mat4.rotation(Math.PI, 1, 0, 0));
.times(Mat4.rotation(Math.PI, 1, 0, 0));
this.draw_pipe(context, program_state, top_pipe_model_transform, 9 - pipe_len);

this.check_bird_collision(top_pipe_model_transform, bottom_pipe_model_transform, pipe_len);
Expand Down Expand Up @@ -365,7 +397,7 @@ export class Bird extends Scene {

draw_ground(context, program_state, model_transform) {
const ground_model_transform = model_transform.times(Mat4.scale(40, 1, 60))
.times(Mat4.translation(0, -1, 0));
.times(Mat4.translation(0, -1, 0));
const green = hex_color(this.night_theme ? "53873d" : "#82C963");
this.shapes.cube.draw(context, program_state, ground_model_transform, this.materials.pure_color.override({color: green}));
}
Expand All @@ -377,18 +409,24 @@ export class Bird extends Scene {
const translation_z = type === "r" ? 60: (type === "l" ? -60 : Math.sin(-t/3)*25);

const background_transform = model_transform.times(Mat4.translation(translation_x, 65 - 1 / 5 * this.y, translation_z))
.times(Mat4.rotation(rotation_angle, 0, 1, 0))
.times(Mat4.scale(85, 85, 1));
.times(Mat4.rotation(rotation_angle, 0, 1, 0))
.times(Mat4.scale(85, 85, 1));
this.shapes.square.draw(
context,
program_state,
background_transform,
this.night_theme ?
this.materials.background_night :
this.materials.background,
this.night_theme ?
this.materials.background_night :
this.materials.background,
);
}

draw_power_up(context, program_state) {
// Only draw the power-up if it's visible
if (this.power_up_visible) {
const power_up_transform = Mat4.translation(...this.power_up_position).times(Mat4.scale(1, 1, 1));
this.shapes.sun.draw(context, program_state, power_up_transform, this.materials.pure_color.override({color: color(1, 1, 1, 1)}));
}
}
draw_all_backgrounds(context, program_state, model_transform, t) {
this.draw_background(context, program_state, model_transform, t, "f");
this.draw_background(context, program_state, model_transform, t, "b");
Expand All @@ -398,9 +436,9 @@ export class Bird extends Scene {

draw_score(context, program_state, model_transform) {
const sideview_transform = model_transform.times(Mat4.translation(-3, 25, 5))
.times(Mat4.rotation(3 * Math.PI / 2, 0, 1, 0));
.times(Mat4.rotation(3 * Math.PI / 2, 0, 1, 0));
const backview_transform = model_transform.times(Mat4.translation(-3, 25, 5))
.times(Matrix.of([-1, 0, 0, 0],[0, 1, 0, 0],[0, 0, 1, 0],[0, 0, 0, 1]));
.times(Matrix.of([-1, 0, 0, 0],[0, 1, 0, 0],[0, 0, 1, 0],[0, 0, 0, 1]));
const scoreboard_model_transform = this.sideview ? sideview_transform : backview_transform;

const time_per_pipe = this.pipe_distance / this.game_speed;
Expand All @@ -410,7 +448,13 @@ export class Bird extends Scene {

const score_string = "Score: " + this.score.toString();
this.shapes.text.set_string(score_string, context.context);
this.shapes.text.draw(context, program_state, scoreboard_model_transform, this.materials.text_image);
this.shapes.text.draw(context, program_state, scoreboard_model_transform, this.materials.text_image);
}
check_collision_with_power_up() {
// Implement collision detection logic here
// Example: Check distance between bird and power-up for simplicity
const distance = this.power_up_position.minus(vec3(0, this.y, 0)).norm();
return distance < 2; // Assuming a simple radius check for collision
}


Expand Down Expand Up @@ -438,25 +482,32 @@ export class Bird extends Scene {
this.update_y(t_after_click);
this.update_angle(t_after_click);
const model_transform = matrix_transform.times(Mat4.translation(0, this.y, 0))
.times(Mat4.rotation(this.angle, 1, 0, 0));
.times(Mat4.rotation(this.angle, 1, 0, 0));

if(!this.game_end) {
this.draw_bird(context, program_state, model_transform);

// Draw power-up if it's currently visible
if (this.power_up_visible) {
this.draw_power_up(context, program_state);
}


this.draw_ground(context, program_state, matrix_transform);
this.draw_all_backgrounds(context, program_state, matrix_transform, t);

// draw three sets of pipes, one before the bird, one after the bird, and one with the bird
this.draw_three_sets_of_pipe(context, program_state, matrix_transform, t);

this.draw_score(context, program_state, matrix_transform);
}
else {
//draw game end scene
program_state.set_camera(this.sideview_cam_pos);
this.sideview = true;
this.shapes.square.draw(context, program_state, matrix_transform.times(Mat4.translation(0, 10, 0))
.times(Mat4.rotation(Math.PI / 2 * 3, 0, 1, 0))
.times(Mat4.scale(28, 28, 1)), this.materials.game_end);
.times(Mat4.rotation(Math.PI / 2 * 3, 0, 1, 0))
.times(Mat4.scale(28, 28, 1)), this.materials.game_end);

const score_model_transform = matrix_transform.times(Mat4.translation(-3, 8, -12))
.times(Mat4.rotation(3 * Math.PI / 2, 0, 1, 0));
Expand All @@ -465,18 +516,88 @@ export class Bird extends Scene {
this.shapes.text.draw(context, program_state, score_model_transform, this.materials.text_image);

const highscore_model_transform = matrix_transform.times(Mat4.translation(-3, 6, -12))
.times(Mat4.rotation(3 * Math.PI / 2, 0, 1, 0));
.times(Mat4.rotation(3 * Math.PI / 2, 0, 1, 0));
const highscore_string = "Highest score: " + this.highest_score.toString();
this.shapes.text.set_string(highscore_string, context.context);
this.shapes.text.draw(context, program_state, highscore_model_transform, this.materials.text_image);
this.shapes.text.draw(context, program_state, highscore_model_transform, this.materials.text_image);

const replay_model_transform = matrix_transform.times(Mat4.translation(-3, 4, -12))
.times(Mat4.rotation(3 * Math.PI / 2, 0, 1, 0));
.times(Mat4.rotation(3 * Math.PI / 2, 0, 1, 0));
const replay_string = "Replay with \"n\"";
this.shapes.text.set_string(replay_string, context.context);
this.shapes.text.draw(context, program_state, replay_model_transform, this.materials.text_image);
this.shapes.text.draw(context, program_state, replay_model_transform, this.materials.text_image);
}





const time_since_last_activation = program_state.animation_time / 1000 - this.power_up_last_activation_time;

if (!this.power_up_visible && time_since_last_activation > this.power_up_activation_interval) {
// Activate power-up
this.power_up_visible = true;
this.power_up_position = vec3(0, Math.random() * 10 + 5, 0);
this.power_up_activation_interval = Math.random() * 10 + 8; // Reset activation interval
}

// Check for collision with the bird if power-up is visible
if (this.power_up_visible && this.check_collision_with_power_up()) {
// Collided with power-up
this.power_up_visible = false; // Hide power-up
this.power_up_last_activation_time = program_state.animation_time / 1000; // Record activation time

// Set initial and final values for pipe gap
const initialPipeGap = 25;
const finalPipeGap = 20;

// Set the duration for increasing the pipe gap
const increaseDuration = 5 * 1000; // 5 seconds

// Set the duration for decreasing the pipe gap
const decreaseDuration = 5 * 1000; // 5 seconds

// Variable to keep track of elapsed time
let elapsedTime = 0;

// Use setInterval to gradually increase the pipe gap
const increaseIntervalId = setInterval(() => {
// Update the pipe gap by 1 unit
this.pipe_gap += 1;

// Increment the elapsed time
elapsedTime += 500; // 0.5 seconds

// Check if the increase duration has elapsed
if (elapsedTime >= increaseDuration) {
// Clear the interval for increasing
clearInterval(increaseIntervalId);

// Use setInterval to gradually decrease the pipe gap
const decreaseIntervalId = setInterval(() => {
// Update the pipe gap by -0.5 units
this.pipe_gap -= 0.5;

// Increment the elapsed time
elapsedTime += 500; // 0.5 seconds

// Check if the decrease duration has elapsed
if (elapsedTime >= increaseDuration + decreaseDuration) {
// Clear the interval for decreasing
clearInterval(decreaseIntervalId);

// Ensure the pipe gap is set to the final value
this.pipe_gap = finalPipeGap;
}
}, 400); // Update every 0.5 seconds for decreasing
}
}, 400); // Update every 0.5 seconds for increasing
}





const blending_factor = 0.1;
if (!this.sideview) {
// change to back cam position
Expand All @@ -490,4 +611,4 @@ export class Bird extends Scene {
program_state.set_camera(transition);
}
}
}
}