2020-07-08 09:05:58
Flappy UPM: JavaFX for a game like Flappy Bird
童夢綺自習スタジオ

Keywords: JavaFX, Java, Flappy Bird, Game Programming,

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:

Alt text for the image

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}