Introduction
For my first-year Java programming coursework, I took on the challenge of creating a simplified version of Flappy Bird using JavaFX. The project focused on developing a straightforward user interface, where I incorporated the iconic red color associated with UPM (assuming it's my university or a symbol). I replaced the bird with the UPM icon, because I hoped to get a good score in my final if I done with this hahaha. This project allowed me to practice fundamental Java programming skills while exploring basic game development principles and UI design with JavaFX.
The Screenshots:
The Original Code of Flappy Bird:
ソースコード
1package FB;
2
3import javafx.animation.Animation;
4import javafx.animation.KeyFrame;
5import javafx.animation.Timeline;
6import javafx.application.Application;
7import javafx.geometry.Pos;
8import javafx.scene.Group;
9import javafx.scene.Scene;
10import javafx.scene.control.Label;
11import javafx.scene.image.Image;
12import javafx.scene.input.KeyCode;
13import javafx.scene.paint.Color;
14import javafx.scene.paint.ImagePattern;
15import javafx.scene.shape.Ellipse;
16import javafx.scene.shape.Rectangle;
17import javafx.scene.text.Font;
18import javafx.stage.Stage;
19import javafx.util.Duration;
20
21import java.util.ArrayList;
22
23/**
24 * @Author MA ZHIYUAN 201464
25 * @Date 2020/06/30
26 */
27
28public class AssignmentFXBird extends Application {
29
30 /** Containers */
31 private Label scoreLabel; //used to display the score all the time in game
32 private Label beginningLabel; //the label of the beginning and it will be changed when game begins
33 private Label gameOverLabel;
34 private Label gameWinLabel;
35 private Ellipse bird; //the bird object
36 private Rectangle ground; //the ground
37 private ArrayList<Rectangle> columns; //the columns group
38 private Group root;
39 private Scene scene;
40 private Timeline timeline;
41 /** Variables */
42 private int score;
43 private int maxScore; //the score of user
44 private int HEIGHT = 700;
45 private int WIDTH = 800; //the width and height of stage (the whole window of the game)
46 private int number_of_columns = 2;
47 private int bird_speed;
48 private int timer_action;
49 private boolean gameWin;
50 private boolean gameOver;
51
52 /**
53 * the function of setting beginning label
54 */
55 public void beginningLabelSetting(){
56 beginningLabel.setText("< Press UP key to start >");
57 beginningLabel.setFont(new Font("Arial",40));
58 beginningLabel.setLayoutX(WIDTH/2-220);
59 beginningLabel.setLayoutY(HEIGHT/2-100);
60 beginningLabel.setTextFill(Color.INDIANRED);
61 }
62
63 /**
64 * the function of setting game over label
65 */
66 public void gameOverLabelSetting(){
67 gameOverLabel.setText("UPM DIED\n Score:" + score);
68 gameOverLabel.setFont(new Font("Arial",80));
69 gameOverLabel.setLayoutX(WIDTH / 2 - 210);
70 gameOverLabel.setLayoutY(HEIGHT / 2 - 140);
71 gameOverLabel.setTextFill(Color.DARKRED);
72 }
73
74 /**
75 * the judgement of game over
76 */
77 public void gameOverJudgement(){
78 for(Rectangle column:columns) {
79 if (score < columns.size())
80 if((column.getBoundsInParent().intersects(bird.getBoundsInParent()))) {
81 //when border conflict, the game over
82 gameOver = true;
83 if(bird.getCenterX() <= column.getX())
84 /*
85 when the centre X small than column X means that
86 centre of bird didn't pass the x position of column
87 */
88 bird.setCenterX(column.getX() - 2 * bird.getRadiusX() + 5);
89 /*
90 let the bird die on the left side of the last column
91 */
92 }
93 }
94 if (score < columns.size())
95 if(bird.getCenterY() > HEIGHT - 120 || bird.getCenterY() < 0) {
96 gameOver = true; //die over the bound
97 }
98 if(gameOver) {
99 gameOverLabelSetting();
100 }
101 }
102
103 /**
104 * the function of setting game win label
105 */
106 public void gameWinLabelSetting(){
107 gameWinLabel.setText("UPM WIN!\n Score:" + score);
108 gameWinLabel.setFont(new Font("Arial", 80));
109 gameWinLabel.setLayoutX(WIDTH / 2 - 210);
110 gameWinLabel.setLayoutY(HEIGHT / 2 - 140);
111 gameWinLabel.setTextFill(Color.GOLD);
112 }
113
114 /**
115 * the judgement of game win
116 */
117 public void gameWinJudgement(){
118 System.out.println(score+"\t"+columns.size());
119 if (score == number_of_columns)
120 gameWin = true;
121 if(gameWin) {
122 gameWinLabelSetting();
123 }
124 }
125
126 /**
127 * the function of setting score label
128 */
129 public void scoreLabelSetting(){
130 scoreLabel.setText("Score: " + score + "\nMAX: " + maxScore);
131 scoreLabel.setAlignment(Pos.TOP_CENTER);
132 scoreLabel.setTextFill(Color.DARKRED);
133 scoreLabel.setFont(new Font("Arial",30));
134 scoreLabel.setTranslateX(350);
135 scoreLabel.setTranslateY(20);
136 /*
137 initialize the score
138 display the score on the top of the screen
139 */
140 }
141
142 /**
143 * the function of bird setting
144 */
145 public void birdSetting(){
146 ImagePattern pattern_bird = new ImagePattern(new Image("FB/bird.png"));
147 bird.setFill(pattern_bird);
148 bird.setRadiusX((20));
149 bird.setRadiusY((20));
150 bird.setCenterX(WIDTH / 2 - 10);
151 bird.setCenterY(HEIGHT / 2 - 10);
152 bird_speed = 0;
153 }
154
155 /**
156 * action jump
157 */
158 public void jump(){
159 if(!gameOver){
160 /* When the game isn't over, the operation can continue */
161 if(bird_speed > 0){
162 /* if YMovement larger than 0, it becomes 0 */
163 bird_speed = 0;
164 }
165 bird_speed = bird_speed - 6;
166 }
167 }
168
169 /**
170 * producing columns
171 */
172 public void addColumn() {
173 int space = 300; //the entrance between the upper column and lower column
174 int width = 100; //the width of column
175 int height = 50 + (int) (Math.random() * 300); //the height of column
176
177 columns.add(new Rectangle(WIDTH +width+(columns.size()*200), HEIGHT - height-120, width, height)); //lower column
178 columns.add(new Rectangle(WIDTH +width+(columns.size()-1)*200,0, width, HEIGHT - height - space)); //upper column
179 }
180
181 /**
182 * initialization
183 */
184 public void initialization(){
185
186 /* The initialization of deleting of previous items */
187 root.getChildren().removeAll(columns);
188 columns.clear();
189
190 /* The initialization of game parameters */
191 gameOver = false;
192 gameWin = false;
193 if(score > maxScore) maxScore = score;
194 score = 0;
195
196 /* The initialization of bird, color red, centre location */
197 bird_speed = 0;
198 bird.setCenterX(WIDTH / 2 - 10);
199 bird.setCenterY(HEIGHT / 2 - 10);
200
201 /* The initialization of columns */
202 for(int i = 0; i < number_of_columns; i++)
203 addColumn();
204
205 /* The initialization of score label */
206 scoreLabelSetting();
207
208 /* The initialization of beginning label */
209 beginningLabelSetting();
210 timeline.pause();
211 root.getChildren().add(beginningLabel);
212
213 /* transition */
214 scene.setOnKeyReleased(k -> {
215 String code = k.getCode().toString();
216 if(code == "UP") {
217 root.getChildren().addAll(columns);
218 root.getChildren().remove(beginningLabel);
219 timeline.play();
220 }
221 });
222 }
223
224
225
226 @Override
227 public void start(Stage primaryStage){
228 root = new Group(); //initialize root
229 gameOverLabel = new Label();
230 beginningLabel = new Label();
231 gameWinLabel = new Label();
232 bird = new Ellipse(); //initialize bird
233 scoreLabel = new Label();
234 birdSetting();
235
236 /**
237 * the score label
238 */
239 scoreLabelSetting();
240
241 /**
242 * the columns
243 */
244 columns = new ArrayList<Rectangle>();
245
246 /**
247 * the ground
248 */
249 ground = new Rectangle(0,HEIGHT-120,WIDTH,120);
250 ground.setFill(Color.DARKRED);
251
252 /**
253 * the time line
254 */
255 timeline = new Timeline();
256 timeline.setCycleCount(Animation.INDEFINITE);
257
258 gameOver = false;
259 gameWin = false;
260
261 /**
262 * One of two conditions controlled by timeline
263 * Used to check jumps and collisions and check the state of death
264 * If you jump, it will decrease, and if you don't, you will increase Touch to die, die to enter the death interface
265 */
266 KeyFrame keyFrame_of_bird = new KeyFrame(Duration.millis(20), e -> {
267 timer_action++;
268 if(timer_action % 3 == 0 && bird_speed < 15) {
269 bird_speed += 1;
270 }
271
272 int bird_new_centreY = (int)bird.getCenterY() + bird_speed;
273 bird.setCenterY(bird_new_centreY);
274
275 scene.setOnKeyReleased(k -> {
276 String code = k.getCode().toString();
277 if(code == "UP") jump();
278 });
279
280 scene.setOnMouseClicked(k -> {
281 jump();
282 });
283
284 gameOverJudgement();
285
286 if(gameOver) {
287
288 root.getChildren().remove(bird);
289
290 if(!(root.getChildren().contains(gameOverLabel)))
291 root.getChildren().addAll(gameOverLabel);
292
293 scene.setOnKeyReleased(k -> {
294 if(k.getCode() == KeyCode.UP){
295 root.getChildren().remove(gameOverLabel);
296 initialization();
297 root.getChildren().add(bird);
298 }
299 });
300 }
301 });
302
303 KeyFrame keyFrame_of_column = new KeyFrame(Duration.millis(20), e -> {
304 if (!gameOver){
305 for(int i = 0;i < columns.size(); i++) {
306 Rectangle column = columns.get(i);
307 column.setFill(Color.INDIANRED);
308 column.setX((column.getX() - 5));
309
310 if(column.getY() == 0 && bird.getCenterX() + bird.getRadiusX() > column.getX() + column.getWidth() / 2-5
311 &&bird.getCenterX() + bird.getRadiusX() < column.getX() + column.getWidth()/2+5) {
312 score++;
313 scoreLabel.setText("Score: " + score + "\nMAX: " + maxScore);
314 scoreLabel.setTextFill(Color.DARKRED);
315 }
316 }
317 }
318 /*
319 delete the columns which already passed by
320 */
321 for(int i = 0; i < columns.size(); i++) {
322 Rectangle column = columns.get(i);
323 if((column.getX()+column.getWidth())<0) {
324 columns.remove(i);
325 }
326 }
327 });
328
329 KeyFrame keyFrame_of_win = new KeyFrame(Duration.millis(10), e -> {
330
331 gameWinJudgement();
332
333 if(gameWin){
334 root.getChildren().remove(bird);
335
336 if(!(root.getChildren().contains(gameWinLabel)) && columns.size() == 0)
337 root.getChildren().addAll(gameWinLabel);
338 scene.setOnKeyReleased(k -> {
339 if(k.getCode() == KeyCode.UP){
340 root.getChildren().remove(gameWinLabel);
341 initialization();
342 root.getChildren().add(bird);
343 }
344 });
345 }
346 });
347
348 timeline.getKeyFrames().addAll(keyFrame_of_bird, keyFrame_of_column, keyFrame_of_win);
349 //the timeline is made by two key frame
350
351 root.getChildren().addAll(ground, bird, scoreLabel);
352
353 scene = new Scene(root);
354 scene.setFill(Color.WHITE);
355 initialization();
356
357 primaryStage.setScene(scene);
358 primaryStage.setHeight(HEIGHT);
359 primaryStage.setWidth(WIDTH);
360 primaryStage.setTitle("Flappy UPM");
361 primaryStage.setResizable(false); //cannot change the size of the window
362 primaryStage.show();
363 }
364
365 public static void main(String[] args) {
366 launch(args);
367 }
368}