diff --git a/README.md b/README.md index 44840e8..8585ff6 100644 --- a/README.md +++ b/README.md @@ -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. - - - ## Run 1. Clone or download this repository. diff --git a/flappy-bird.js b/flappy-bird.js index ba10f7d..3c27a54 100644 --- a/flappy-bird.js +++ b/flappy-bird.js @@ -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 = { @@ -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( @@ -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"), }), @@ -113,6 +113,33 @@ 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() { @@ -120,6 +147,9 @@ export class Bird extends Scene { 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); } @@ -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})); } @@ -168,9 +200,9 @@ 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)})); } @@ -178,9 +210,9 @@ export class Bird extends Scene { 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})); } @@ -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})); } @@ -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); @@ -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})); } @@ -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"); @@ -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; @@ -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 } @@ -438,16 +482,23 @@ 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 { @@ -455,8 +506,8 @@ export class Bird extends 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)); @@ -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 @@ -490,4 +611,4 @@ export class Bird extends Scene { program_state.set_camera(transition); } } -} +} \ No newline at end of file