JavaFx实现Flappybird
《flappy bird》是一款由来自越南的独立游戏开发者Dong Nguyen所开发的作品。游戏中玩家必须控制一只小鸟,跨越由各种不同长度水管所组成的障碍。现在用Java还原了这款游戏。
运行截图
首页
游戏中
结算
项目文件
项目源码
入口
main.java
package pika.game.flappybird;
/*--------------------------------------
Flappy Bird
Code by Pikachu
Java课程设计
任浩龙2018141461344
--------------------------------------*/
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.text.Text;
public class Main extends Application {
//----------------------------------------------全局游戏设置参数----------------------------------------------
public static final int game_wind_high = 960; //全局窗口高度
public static final int game_wind_wedh = 864; //全局窗口宽度
public static final int game_fram_fpst = 17; //全局同步间隔
public static final int game_dbug_imgs = 0; //图像调试标识
public static final int game_dbug_taps = 1; //水管调试标识
public static final double bird_data_datg =0.45; //记录小鸟重力
public static double bird_data_high = 330; //记录小鸟位置
public static double bird_data_spee = 0; //记录小鸟速度,每一帧执行后自增的速度
public static final double bird_data_addq = 10; //小鸟上升速度,每次按下鼠标设置的速度
public static double taps_spee_init = 6; //记录水管速度
public static final double taps_spee_adda = 0; //水管递增速度,每隔多少帧增加多少速度
public static double taps_spee_addb = 140; //水管递增间隔,每隔多少帧增加一根水管
public static final double taps_spee_addc = 2; //水管递增递减,每通过一根水管减多少帧
public static double taps_spee_addd = 0; //水管递增计数,统计当前执行的是多少帧
public static final double taps_spee_adde = 30; //水管递增下界,记录最少间隔多少帧自增
public static double game_scor_data = 0; //存储游戏分数
public static Imgs main_ctrl = new Imgs(); //全局渲染对象
public static Imgs menu_bgmp = new Imgs(); //菜单背景图片
public static Imgs menu_titl = new Imgs(); //菜单标题图片
public static Imgs menu_star = new Imgs(); //菜单开始图片
public static Imgs menu_bars = new Imgs(); //菜单底纹图片
public static Imgs menu_bart = new Imgs(); //菜单底纹图片
public static Imgs game_bird = new Imgs(); //游戏小鸟图像
public static Imgs ends_titl = new Imgs(); //游戏结束标题
public static Imgs ends_pans = new Imgs(); //游戏结束标题
public static Imgs ends_dold = new Imgs(); //游戏结束标题
public static Push menu_butt = new Push(); //存储按钮数据
public static Loop menu_loop = new Loop(); //存储菜单循环
public static Loop bird_loop = new Loop(); //小鸟下坠循环
public static Loop taps_loop = new Loop(); //水管移动循环
public static Taps taps_maps = new Taps(); //水管图像数据
public static Text text_scor = new Text(); //存储文字信息
public void start(Stage views) {
Func.main_init(views);
Func.main_menu_init();
Func.main_menu_show();
Func.main_menu_loop();
Func.menu_loop.play();
Func.main_push_init();
Func.main_mous_loop();
Func.main_game_loop();
}
}
功能实现
游戏渲染Func.java
package pika.game.flappybird;
/*--------------------------------------
渲染事件类
CODE BY PIKACHUIM
作用:执行渲染操作
--------------------------------------*/
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;
import javafx.scene.text.Font;
import javafx.scene.paint.Paint;
public class Func extends Main{
public static void main_init(Stage inpu) {
main_ctrl.init(game_wind_high, game_wind_wedh);main_ctrl.show(inpu);
}
public static void main_menu_init() {
//----------------------------------------------主菜单图片初始化----------------------------------------------
menu_bgmp.setp("Imgs", "PICTS_BGPBT");menu_bgmp.setr(000, 000, 864, 960);
menu_titl.setp("Imgs", "PICTS_TITLE");menu_titl.setr(170, 100, 534, 144);
menu_star.setp("Imgs", "PICTS_START");menu_star.setr(315, 330, 242, 146);
menu_bars.setp("Imgs", "PICTS_LANGS");menu_bars.setr(000, 852, 864, 224);
menu_bart.setp("Imgs", "PICTS_LANGS");menu_bart.setr(864, 852, 864, 224);
ends_titl.setp("Imgs", "PICTS_OVERD");ends_titl.setr(130, 140, 612, 108);
ends_pans.setp("Imgs", "PICTS_PANEL");ends_pans.setr(150, 500, 595, 315);
ends_dold.setp("icon", "PICTS_MEDA3");ends_dold.setr(219, 625, 110, 110);
menu_butt = new Push("", menu_star); menu_butt.setr(295, 300);menu_butt.tran();
}
public static void main_game_init(){
//----------------------------------------------游戏中图片初始化----------------------------------------------
game_bird.setp("icon", "PICTS_BIRD1");game_bird.setr(340,330, 96, 96);
main_bird_loop();bird_loop.play();Func.taps_loop.play();
}
public static void main_game_loop(){
//----------------------------------------------游戏中水管主循环----------------------------------------------
EventHandler<ActionEvent> main_taps_even = r -> {
Taps.move();
if(taps_spee_addd==0){
int temp_addu=(int)System.currentTimeMillis()%150;
int temp_flag=(int)(Math.random()*101)%2;
if(Taps.taps_lens>0){
int temp_posy=(int)(-Taps.taps_dat1.get(Taps.taps_lens-1).imgs_posy);
if(temp_flag==0)
Taps.addt(temp_posy+temp_addu);
else
Taps.addt(temp_posy-temp_addu);
System.out.println("[增加水管]"+temp_flag+"[增加位置]"+temp_posy);
}
else
Taps.addt(700);
taps_spee_init+=taps_spee_adda;
taps_spee_addd=taps_spee_addb;
}
else{
taps_spee_addd--;
}
};
taps_loop.setb(game_fram_fpst, main_taps_even);
}
public static void main_menu_show(){
//----------------------------------------------载入菜单图像数据----------------------------------------------
main_ctrl.clea();/*清空*/menu_bgmp.radd();menu_titl.radd();menu_bars.radd();menu_bart.radd();menu_butt.radd();
}
public static void main_game_show(){
//----------------------------------------------载入游戏图像数据----------------------------------------------
main_ctrl.clea();/*清空*/menu_bgmp.radd();menu_bars.radd();menu_bart.radd();game_bird.radd();
text_scor.setFont(Font.font ("Verdana", 42));
text_scor.setText("SCORE: 0");
text_scor.setTranslateX(20);
text_scor.setTranslateY(70);
text_scor.setFill(Paint.valueOf("#ffffff"));
main_ctrl.imgs_pane.getChildren().add(text_scor);
}
public static void main_push_init(){
//----------------------------------------------载入菜单按钮事件----------------------------------------------
EventHandler<ActionEvent> menu_burr_acte=new EventHandler<ActionEvent>(){
@Override
public void handle(ActionEvent t){ main_game_init();main_game_show(); }};
menu_butt.seta(menu_burr_acte);
}
public static void main_menu_loop(){
//----------------------------------------------载入菜单底栏事件----------------------------------------------
EventHandler<ActionEvent> main_menu_even = r -> {
menu_bars.setr(menu_bars.imgs_posx -8, menu_bars.imgs_posy);
menu_bart.setr(menu_bart.imgs_posx -8, menu_bart.imgs_posy);
if(menu_bars.imgs_posx<=game_wind_wedh*(-1)) menu_bars.setr( 0,menu_bars.imgs_posy);
if(menu_bart.imgs_posx<=0) menu_bart.setr(864,menu_bart.imgs_posy);};
menu_loop.setb(game_fram_fpst,main_menu_even);
}
public static void main_bird_loop(){
//----------------------------------------------载入小鸟下坠线程----------------------------------------------
EventHandler<ActionEvent> main_bird_even = r -> {
bird_data_spee+=bird_data_datg;bird_data_high+=bird_data_spee;
if(bird_data_high>=778)bird_data_high=778;game_bird.setr(game_bird.imgs_posx,bird_data_high);};
bird_loop.setb(game_fram_fpst,main_bird_even);
}
public static void main_mous_loop(){
//----------------------------------------------载入小鸟上升线程----------------------------------------------
main_ctrl.imgs_rend.setOnMousePressed(new EventHandler<MouseEvent>() {
@Override public void handle(MouseEvent m) { bird_data_spee=-bird_data_addq; }});
}
public static void main_game_rset(){
//----------------------------------------------重置游戏状态函数----------------------------------------------
bird_data_high = 330; //记录小鸟位置
bird_data_spee = 0; //记录小鸟速度
taps_spee_init = 6; //记录水管速度
taps_spee_addb = 140; //水管递增间隔
taps_spee_addd = 0; //水管递增计数
game_scor_data = 0; //存储游戏分数
for ( int i=Taps.taps_dat1.size()-1; i>=0;i--) {
Taps.taps_dat1.remove(i);
Taps.taps_dat2.remove(i);
}
}
public static void main_endl_show(){
//----------------------------------------------显示结尾标题函数----------------------------------------------
bird_loop.stop();
taps_loop.stop();
main_ctrl.clea();
menu_bgmp.radd();
ends_titl.radd();
ends_pans.radd();
ends_dold.radd();
text_scor.setTranslateX(620);
text_scor.setTranslateY(620);
main_ctrl.imgs_pane.getChildren().add(text_scor);
Taps.clen();
text_scor.setText(Integer.toString((int)game_scor_data));
main_game_rset();
menu_butt.radd();
}
}
图片载入Imgs.java
package pika.game.flappybird;
/*--------------------------------------
图片渲染类
CODE BY PIKACHUIM
作用:载入图片并执行渲染,并绑定面板布局
用法:Imgs->setp->setr->load->uppo->radd
全局:init传入场景->show一次性绑定节点值
--------------------------------------*/
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
public class Imgs {
public static Pane imgs_pane; //存储全局布局
public static Scene imgs_rend; //存储全局场景
public String imgs_path; //存储图片位置
public Image imgs_imgs; //存储图形数据
public ImageView imgs_view; //存储图片参数
public double imgs_posx; //存储垂直坐标
public double imgs_posy; //存储水平坐标
public double imgs_posw; //存储水平宽度
public double imgs_posh; //存储垂直宽度
//--------------------------构造函数--------------------------
public Imgs() { this.news(); } //默认构造函数
public Imgs(String dirs, String name){ //只设置文件名
this.setp(dirs,name);this.news();}
public Imgs(String dirs, String name, //同时设置状态
double datx, double daty,
double datw, double dath){
this.setp(dirs,name);
this.setr(datx,daty, datw,dath);}
public void news() { //默认参数初始
this.imgs_posx = 0;
this.imgs_posw = 100;
this.imgs_posy = 0;
this.imgs_posh = 100; }
//--------------------------设置函数--------------------------
public void setp(String dirs,String name) {//设置文件路径
imgs_path = "file:" +
System.getProperty("user.dir")
+ "\\" + dirs
+ "\\" + name + ".PNG";
this.load(); }
public void setr(double datx, double daty,
double datw, double dath) {//设置大小位置
this.imgs_posw = datw;
this.imgs_posh = dath;
this.setr(datx, daty); }
public void setr(double datx, double daty) {//改变图片位置
this.imgs_posx = datx;
this.imgs_posy = daty;
this.uppo(); }
//--------------------------载入函数--------------------------
public void load() {
this.imgs_imgs = new Image(imgs_path); //载入图片文件
this.imgs_view = new ImageView();
this.imgs_view.setImage(imgs_imgs);
if(Main.game_dbug_imgs==1)
System.out.println(imgs_path);
this.uppo(); }
public void radd() { //向面板加节点
this.imgs_pane.getChildren().add(imgs_view); }
public void uppo() { //更新图片位置
this.imgs_view.setFitHeight(imgs_posh);
this.imgs_view.setFitWidth(imgs_posw);
this.imgs_view.setX(imgs_posx);
this.imgs_view.setY(imgs_posy); }
//--------------------------全局函数--------------------------
public void init(int high, int widh) { //初始窗口界面
this.imgs_pane = new Pane();
this.imgs_rend = new Scene(
imgs_pane,widh,high); }
public void show(Stage runt) { //更新窗口画面
runt.setScene(imgs_rend);
runt.show(); }
public void clea() { //清空渲染内容
this.imgs_pane.getChildren().clear(); }
//------------------------------------------------------------
}
事件载入Loop.java
package pika.game.flappybird;
/*--------------------------------------
事件循环类
CODE BY PIKACHUIM
作用:读写事件操作
--------------------------------------*/
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.util.Duration;
public class Loop {
public EventHandler<ActionEvent> loop_even;
public Timeline loop_line;
public double loop_time;
public void setb(double inpu_time,EventHandler<ActionEvent>inpu_even){ //绑定事件
loop_time=inpu_time;
loop_even=inpu_even;
}
public void play(){
loop_line=new Timeline(new KeyFrame(Duration.millis(loop_time),loop_even)); //执行动画
loop_line.setCycleCount(Timeline.INDEFINITE);
loop_line.play();
}
public void stop(){ //停止执行
loop_line.stop();
}
}
按钮事件Push.java
package pika.game.flappybird;
/*--------------------------------------
按钮事件类
CODE BY PIKACHUIM
作用:显示按钮并绑定触发事件
用法:Push->setr->radd->seta
--------------------------------------*/
import javafx.scene.control.Button;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
public class Push extends Imgs {
public Button push_push;
public Push(){}
public Push(String text, Imgs imga){
push_push=new Button(text,imga.imgs_view);
}
public void setr(double datx,double daty){
push_push.setLayoutX(datx);
push_push.setLayoutY(daty);
}
public void radd(){
this.imgs_pane.getChildren().add(push_push);
}
public void seta(EventHandler<ActionEvent> inpu){
push_push.setOnAction(inpu);
}
public void tran(){
push_push.setStyle("-fx-background-color: #4ec0ca");
}
}
水管事件Taps.java
package pika.game.flappybird;
import java.util.ArrayList;
import java.util.List;
/*--------------------------------------
水管移动类
CODE BY PIKACHUIM
作用:操作水管移动并判断
--------------------------------------*/
public class Taps extends Main{
public static List<Imgs> taps_dat1;
public static List<Imgs> taps_dat2;
public static int taps_lens;
public Taps(){
taps_lens=0;
taps_dat1= new ArrayList<Imgs>();
taps_dat2= new ArrayList<Imgs>();
}
public static void clen(){
taps_lens=0;
taps_dat1= new ArrayList<Imgs>();
taps_dat2= new ArrayList<Imgs>();
}
public static void addt(int high){
if(high<300) high=300;
if(high>600) high=600;
taps_lens++;
Imgs tmp1=new Imgs("icon","PICTS_PIPUP",
820, -high, 52,768);
Imgs tmp2=new Imgs("icon","PICTS_PIPDO",
820,-high+768+300,52,768);
taps_dat1.add(tmp1);taps_dat1.get(taps_dat1.size()-1).radd();
taps_dat2.add(tmp2);taps_dat2.get(taps_dat2.size()-1).radd();
}
public static void delt(int inde){
if(taps_dat1.size()>=1){
main_ctrl.imgs_pane.getChildren().remove(taps_dat1.get(inde).imgs_view);
main_ctrl.imgs_pane.getChildren().remove(taps_dat2.get(inde).imgs_view);
taps_dat1.remove(taps_dat1.get(inde));
taps_dat2.remove(taps_dat2.get(inde));
taps_lens--;
}
}
public static void move(){
if(taps_lens>0)
for(int lotp=0;lotp<taps_lens;lotp++){
taps_dat1.get(lotp).setr(taps_dat1.get(lotp).imgs_posx-taps_spee_init,taps_dat1.get(lotp).imgs_posy);
taps_dat2.get(lotp).setr(taps_dat2.get(lotp).imgs_posx-taps_spee_init,taps_dat2.get(lotp).imgs_posy);
if(taps_dat1.get(lotp).imgs_posx<=-64) {
game_scor_data++;
String temp="SCORE: "+Integer.toString((int)game_scor_data);
text_scor.setText(temp);
if(Main.game_dbug_taps==1)
System.out.println("[当前得分]"+game_scor_data
+"[帧率间隔]"+taps_spee_addb
+"[水管速度]"+taps_spee_init
+"[水管数量]"+taps_lens);
taps_spee_addb-=taps_spee_addc;
if(taps_spee_addb<=taps_spee_adde)taps_spee_addb=taps_spee_adde;
}
if(taps_dat1.get(lotp).imgs_posx<=-64){
delt(lotp);
}
if(taps_dat1.get(lotp).imgs_posx>=244&&taps_dat1.get(lotp).imgs_posx<=436){
if(bird_data_high<= taps_dat1.get(lotp).imgs_posy+750
|| bird_data_high>= taps_dat2.get(lotp).imgs_posy-80){
System.out.println("[撞到柱子]"+lotp);
Func.main_endl_show();
}
}
}
}
}