赞
踩
刚刚完成的一个小游戏,写文章记录一下,如果有任何错误或者可以改进的代码请提出
另一方面也是方便自己几个月或几年后忘记时,来这里翻一翻回顾思路
目录
首先放一下效果图:
所有卡片
分数统计
尺寸为5x5或6x6
五个类,最基础的是_CardPane,继承自BorderPane,作为数字卡片。它里面有一个Rectangle,用来表示卡片的圆角矩形背景,以及一个Label来显示数字
然后是由数字卡片组成的矩阵_CardMatrixPane,继承自StackPane,它包含一个GridPane
_CardColor,里面只有一个静态的Color数组,用来搞卡片的背景颜色
_GameMenuBar作为游戏的菜单栏,继承自MenuBar
最后是_2048Demo,相当于控制器
这里类名前面加下划线是个人习惯,因为我的Eclipse项目名、包名、类名等等都会与图标重合一些,加下划线可以看的方便,如下:
下面放代码:
- package _2048._node;
-
- import _2048._model._CardColor;
- import javafx.scene.control.Label;
- import javafx.scene.layout.BorderPane;
- import javafx.scene.paint.Color;
- import javafx.scene.shape.Rectangle;
-
- /**
- * 节点类——数字卡片
- * @author 邦邦拒绝魔抗
- *
- */
- //若继承自Pane类,缺少需要的setAlignment()方法
- //若继承自StackPane类,会出现一些绘制错误
- public class _CardPane extends BorderPane {
- private static final int RC=5;//矩形的圆角
- private int type;
- /* 类型
- * type=0 number=0
- * type=1 number=2
- * type=2 number=4
- * type=3 number=8
- * ...
- */
- private boolean merge=false;//是否被合并过,如果合并了,则不能继续合并,针对当前轮
- private Rectangle r;//圆角矩形
- private Label l;//数字标签
-
- /**无参构造方法*/
- public _CardPane() {
- this(0);
- }
-
- /**构造方法,通过下标和类型生成数字卡片*/
- public _CardPane(int type) {
- this.type=type;
- //圆角矩形
- r=new Rectangle();
- r.widthProperty().bind(this.widthProperty());//矩形的宽度绑定单元格宽度
- r.heightProperty().bind(this.heightProperty());//矩形的高度绑定单元格高度
- r.setArcWidth(RC);//圆角宽度
- r.setArcHeight(RC);//圆角高度
- r.setStroke(Color.BLACK);//边框颜色
- r.setStrokeWidth(3);//边框宽度
- getChildren().add(r);
- //数字标签
- l=new Label("65536");//65536是4*4情况下可能出现的最大数字
- setCenter(l);
- //绘制变化的部分
- draw();
- }
-
- /**获取数字标签对象*/
- public Label getLabel() {
- return l;
- }
-
- /**设置卡片类型*/
- public void setType(int type) {
- this.type=type;
- }
-
- /**获取卡片类型*/
- public int getType() {
- return type;
- }
-
- /**设置合并记录*/
- public void setMerge(boolean merge) {
- this.merge=merge;
- }
-
- /**获取合并记录*/
- public boolean isMerge() {
- return merge;
- }
-
- /**绘制单次操作中卡片变化的部分,包括颜色和显示的数字*/
- public void draw() {
- if(merge) {//突出显示已合并的卡片
- r.setStroke(Color.RED);//此次操作中合并,显示红色
- }else {
- r.setStroke(Color.BLACK);//此次操作中没有合并,显示黑色
- }
- r.setFill(_CardColor.CB[type]);
- drawNumber();
- }
-
- /**判断此卡片能否向调用者所给出的卡片移动或合并*/
- public boolean canMergeOrMove(_CardPane card) {
- if(type==0) {//空卡片不能移动或合并
- return false;
- }
- if(card.type==0) {//可以向空卡片移动
- return true;
- }
- return type==card.getType()&&!merge&&!card.isMerge();//不能二次合并
- }
-
- /**尝试向调用者所给出的卡片移动或合并,这一函数可能会修改两个卡片的属性*/
- public boolean tryMergeOrMoveInto(_CardPane card) {
- boolean canMergeOrMove=canMergeOrMove(card);
- if(canMergeOrMove) {//可以移动或合并
- if(card.getType()==0) {//移动
- card.setType(type);//移动数字
- card.setMerge(merge);//移动合并记录
- this.toVoid();//this成为空卡片
- }else {//合并
- card.setType(card.getType()+1);//合并数字
- card.setMerge(true);//设置合并记录
- this.toVoid();//this成为空卡片
- }
- }
- return canMergeOrMove;
- }
-
- /**刷新为空卡片*/
- private void toVoid() {
- type=0;
- merge=false;
- }
-
- private void drawNumber() {
- if(type==0) {//空卡片
- l.setText("");
- }else {//非空卡片需要显示数字
- l.setText(""+getNumber());
- }
- }
-
- /**计算需显示的数字*/
- public int getNumber() {
- return (int)Math.pow(2,type);
- }
-
- @Override
- public String toString() {
- return "[type="+type+", merge="+merge+"]";
- }
- }
- package _2048._node;
-
- import java.util.ArrayList;
- import java.util.List;
-
- import javafx.application.Application;
- import javafx.scene.control.Alert;
- import javafx.scene.control.Alert.AlertType;
- import javafx.scene.input.KeyCode;
- import javafx.scene.layout.GridPane;
- import javafx.scene.layout.StackPane;
- import javafx.scene.text.Font;
-
- /**
- * 节点类——卡片矩阵
- * @author 邦邦拒绝魔抗
- *
- */
- //若继承自Pane类,缺少需要的setAlignment()方法
- public class _CardMatrixPane extends StackPane {
- private Callbacks mCallbacks;
- private int cols;//卡片矩阵列数
- private int rows;//卡片矩阵行数
- private GridPane gridPane;//卡片矩阵容器
- private _CardPane[][] cps;//卡片矩阵
- private long score=0;//分数,初始为0
- private int[] mcQuantities=new int[15];//合并过的卡片数字数量,包括4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536
-
- /**回调接口*/
- public interface Callbacks {
- void afterScoreChange();//分数变化
- }
-
- public _CardMatrixPane(Application application) {
- this(4,4,application);//默认4*4
- }
-
- public _CardMatrixPane(int cols,int rows,Application application) {//application供回调方法使用
- mCallbacks=(Callbacks)application;
- this.cols=cols;
- this.rows=rows;
- // this.setBackground(new Background(new BackgroundFill(Color.BLUE,CornerRadii.EMPTY,Insets.EMPTY)));//测试用
- init();
- getChildren().add(gridPane);
- }
-
- /**获取分数*/
- public long getScore() {
- return score;
- }
-
- /**获取合并过的卡片数字数量*/
- public int[] getMcQuantities() {
- return mcQuantities;
- }
-
- private void init() {
- initGridPane();//初始化GridPane
- createRandomNumber();//在随机的空卡片上生成数字,这个方法会返回布尔值,但这里不需要
- }
-
- /**初始化GridPane*/
- private void initGridPane() {
- gridPane=new GridPane();
- // gridPane.setBackground(new Background(new BackgroundFill(Color.YELLOW,CornerRadii.EMPTY,Insets.EMPTY)));//测试用
- // gridPane.setGridLinesVisible(true);//单元格边框可见,测试用
-
- //对this尺寸监听
- widthProperty().addListener(ov->setGridSizeAndCardFont());//宽度变化,更新边长和字号
- heightProperty().addListener(ov->setGridSizeAndCardFont());//高度变化,更新边长和字号
- //单元格间隙
- gridPane.setHgap(5);
- gridPane.setVgap(5);
- //绘制每个单元格
- cps=new _CardPane[cols][rows];
- for(int i=0;i<cols;i++) {//遍历卡片矩阵的列
- for(int j=0;j<rows;j++) {//遍历卡片矩阵的行
- _CardPane card=new _CardPane(0);
- gridPane.add(card, i, j);
- cps[i][j]=card;
- }
- }
- }
-
- /**设置GridPane的边长,其内部单元格的尺寸和CardPane的字号*/
- private void setGridSizeAndCardFont(){
- double w=widthProperty().get();
- double h=heightProperty().get();
- double min=w<h?w:h;
- gridPane.setMaxWidth(min);
- gridPane.setMaxHeight(min);
- for(int i=0;i<cols;i++) {//遍历卡片矩阵的列
- for(int j=0;j<rows;j++) {//遍历卡片矩阵的行
- _CardPane card=cps[i][j];
- card.getLabel().setFont(new Font((min/14)/cols*4));//设置显示数字的尺寸
- //由于下面两行代码主动设置了每个单元格内cardPane的尺寸,gridPane不需要自动扩张
- card.setPrefWidth(min-5*(cols-1));//设置单元格内cardPane的宽度,否则它会随其内容变化,进而影响单元格宽度
- card.setPrefHeight(min-5*(rows-1));//设置单元格内cardPane的高度,否则它会随其内容变化,进而影响单元格高度
- }
- }
- }
-
- /**添加键盘监听*/
- public void createKeyListener() {
- setOnKeyPressed(e->{//键盘按下事件
- _CardPane maxCard=getMaxCard();//最大卡片
- if(maxCard.getType()==16) {//出现最大数字
- Alert alert=new Alert(AlertType.INFORMATION);
- alert.setTitle(alert.getAlertType().toString());
- alert.setContentText("恭喜你,游戏的最大数字为"+maxCard.getNumber()+",可在菜单栏选择重新开始\n"+
- "事实上,我们还尚未准备比"+maxCard.getNumber()+"更大的数字卡片,终点已至");
- alert.show();
- return;
- }
- KeyCode kc=e.getCode();
- switch(kc) {
- case UP:
- case W:
- goUp();//↑
- break;
- case DOWN:
- case S:
- goDown();//↓
- break;
- case LEFT:
- case A:
- goLeft();//←
- break;
- case RIGHT:
- case D:
- goRight();//→
- break;
- default:
- return;//未定义的操作
- }
- redrawAllCardsAndResetIsMergeAndSetScore();//重绘所有的卡片,并重设合并记录,更新分数
- boolean isFull=!createRandomNumber();//生成新的随机数字卡片,并判满,这包含了生成数字后满的情况
- if(isFull) {//矩阵已满,可能已经游戏结束
- boolean testOpe=false;//是否还能进行横向或竖向操作
- testOpe|=testUp();//还能进行竖向操作
- testOpe|=testLeft();//还能进行横向操作
- if(!testOpe) {//游戏结束
- Alert alert=new Alert(AlertType.INFORMATION);
- alert.setTitle(alert.getAlertType().toString());
- alert.setContentText("游戏结束,本次最大数字为"+maxCard.getNumber()+",可在菜单栏选择重新开始\n");
- alert.show();
- }
- }
- });
- }
-
- /**向上操作*/
- private void goUp() {
- boolean mergeOrMoveExist;//矩阵的这次操作的一次遍历中是否存在移动或合并
- do {
- mergeOrMoveExist=false;//初始为false
- for(int i=0;i<cols;i++) {//遍历卡片矩阵的列
- for(int j=1;j<rows;j++) {//从第二行起向下,遍历卡片矩阵的行
- _CardPane card=cps[i][j];
- _CardPane preCard=cps[i][j-1];//前一个卡片
- boolean isChanged=card.tryMergeOrMoveInto(preCard);//记录两张卡片间是否进行了移动或合并
- mergeOrMoveExist|=isChanged;//只要有一次移动或合并记录,就记存在为true
- }
- }
- }while(mergeOrMoveExist);//如果存在移动或合并,就可能需要再次遍历,继续移动或合并
- }
-
- /**测试是否能向上操作*/
- private boolean testUp() {
- for(int i=0;i<cols;i++) {//遍历卡片矩阵的列
- for(int j=1;j<rows;j++) {//从第二行起向下,遍历卡片矩阵的行
- _CardPane card=cps[i][j];
- _CardPane preCard=cps[i][j-1];//前一个卡片
- if(card.canMergeOrMove(preCard)) {
- return true;//能
- }
- }
- }
- return false;//不能
- }
-
- /**向下操作*/
- private void goDown() {
- boolean mergeOrMoveExist;//矩阵的这次操作的一次遍历中是否存在移动或合并
- do {
- mergeOrMoveExist=false;//初始为false
- for(int i=0;i<cols;i++) {//遍历卡片矩阵的列
- for(int j=rows-2;j>=0;j--) {//从倒数第二行起向上,遍历卡片矩阵的行
- _CardPane card=cps[i][j];
- _CardPane preCard=cps[i][j+1];//前一个卡片
- boolean isChanged=card.tryMergeOrMoveInto(preCard);//记录两张卡片间是否进行了移动或合并
- mergeOrMoveExist|=isChanged;//只要有一次移动或合并记录,就记存在为true
- }
- }
- }while(mergeOrMoveExist);//如果存在移动或合并,就可能需要再次遍历,继续移动或合并
- }
-
- /**向左操作*/
- private void goLeft() {
- boolean mergeOrMoveExist;//矩阵的这次操作的一次遍历中是否存在移动或合并
- do {
- mergeOrMoveExist=false;//初始为false
- for(int i=1;i<cols;i++) {//从第二列起向右,遍历卡片矩阵的列
- for(int j=0;j<rows;j++) {//遍历卡片矩阵的行
- _CardPane card=cps[i][j];
- _CardPane preCard=cps[i-1][j];//前一个卡片
- boolean isChanged=card.tryMergeOrMoveInto(preCard);//记录两张卡片间是否进行了移动或合并
- mergeOrMoveExist|=isChanged;//只要有一次移动或合并记录,就记存在为true
- }
- }
- }while(mergeOrMoveExist);//如果存在移动或合并,就可能需要再次遍历,继续移动或合并
- }
-
- /**测试是否能向左操作*/
- private boolean testLeft() {
- for(int i=1;i<cols;i++) {//从第二列起向右,遍历卡片矩阵的列
- for(int j=0;j<rows;j++) {//遍历卡片矩阵的行
- _CardPane card=cps[i][j];
- _CardPane preCard=cps[i-1][j];//前一个卡片
- if(card.canMergeOrMove(preCard)) {
- return true;//能
- }
- }
- }
- return false;//不能
- }
-
- /**向右操作*/
- private void goRight() {
- boolean mergeOrMoveExist;//矩阵的这次操作的一次遍历中是否存在移动或合并
- do {
- mergeOrMoveExist=false;//初始为false
- for(int i=cols-2;i>=0;i--) {//从倒数第二列起向左,遍历卡片矩阵的列
- for(int j=0;j<rows;j++) {//遍历卡片矩阵的行
- _CardPane card=cps[i][j];
- _CardPane preCard=cps[i+1][j];//前一个卡片
- boolean isChanged=card.tryMergeOrMoveInto(preCard);//记录两张卡片间是否进行了移动或合并
- mergeOrMoveExist|=isChanged;//只要有一次移动或合并记录,就记存在为true
- }
- }
- }while(mergeOrMoveExist);//如果存在移动或合并,就可能需要再次遍历,继续移动或合并
- }
-
- /**重绘所有的卡片,并重设合并记录,并设置分数*/
- private void redrawAllCardsAndResetIsMergeAndSetScore() {
- for(int i=0;i<cols;i++) {//遍历卡片矩阵的列
- for(int j=0;j<rows;j++) {//遍历卡片矩阵的行
- _CardPane card=cps[i][j];
- card.draw();
- if(card.isMerge()) {//这张卡片合并过
- score+=card.getNumber();//计入分数
- mcQuantities[card.getType()-2]++;//相应的合并过的卡片数字数量+1
- card.setMerge(false);
- }
- }
- }
- mCallbacks.afterScoreChange();
- }
-
- /**获取卡片矩阵中的最大卡片*/
- private _CardPane getMaxCard() {
- _CardPane maxCard=new _CardPane();//type=0的新卡片
- for(int i=0;i<cols;i++) {//遍历卡片矩阵的列
- for(int j=0;j<rows;j++) {//遍历卡片矩阵的行
- _CardPane card=cps[i][j];
- if(card.getType()>maxCard.getType()) {
- maxCard=card;
- }
- }
- }
- return maxCard;
- }
-
- /**在随机的空卡片上生成新的数字,若矩阵已满,或生成数字后满,则返回false*/
- public boolean createRandomNumber() {
- List<_CardPane> voidCards=new ArrayList<>();//空卡片列表
-
- for(int i=0;i<cols;i++) {//遍历卡片矩阵的列
- for(int j=0;j<rows;j++) {//遍历卡片矩阵的行
- _CardPane card=cps[i][j];
- if(card.getType()==0) {//是空卡片
- voidCards.add(card);//添加到列表中
- }
- }
- }
- int len=voidCards.size();
- if(len==0) {//没有空卡片了,返回
- return false;//判满
- }
- int type;
- int index=(int)(Math.random()*5);//0,1,2,3,4
- if(index!=0) {//4/5概率
- type=1;//number=2
- // type=7;//number=128
- }else {//1/5概率
- type=2;//number=4
- // type=8;//number=256
- }
- int voidCardIndex=(int)(Math.random()*len);
- _CardPane card=voidCards.get(voidCardIndex);
- card.setType(type);//更新type,生成数字
- card.draw();//重绘此卡片
- if(len==1) {//只有一个空卡片,矩阵生成数字后满
- return false;
- }
- return true;
- }
-
- /**重启卡片矩阵,并在随机的空卡片上生成数字*/
- public void restartMatrix() {
- for(int i=0;i<cols;i++) {//遍历卡片矩阵的列
- for(int j=0;j<rows;j++) {//遍历卡片矩阵的行
- _CardPane card=cps[i][j];
- card.setType(0);
- card.draw();//重绘
- }
- }
- score=0;//重设分数
- mcQuantities=new int[15];//重设合并过的卡片数字数量
- mCallbacks.afterScoreChange();
- createRandomNumber();//在随机的空卡片上生成数字,这个方法会返回布尔值,但这里不需要
- }
-
- /**进行颜色测试,可在4*4矩阵中显示2至65536*/
- public void testColors() {
- for(int i=0;i<cols;i++) {//遍历卡片矩阵的列
- for(int j=0;j<rows;j++) {//遍历卡片矩阵的行
- _CardPane card=cps[i][j];
- int type=i*4+j+1;
- if(type>16) {
- return;
- }
- card.setType(i*4+j+1);
- card.draw();//重绘
- }
- }
- }
- }
- package _2048._model;
-
- import javafx.scene.paint.Color;
-
- public class _CardColor {
- public static Color[] CB= {//卡片颜色
- Color.rgb(255,255,255),//0
- //235
- //195*2
- Color.rgb(235,195,195),//2
- Color.rgb(195,235,195),//4
- Color.rgb(195,195,235),//8
- //195
- //215*2
- Color.rgb(195,215,215),//16
- Color.rgb(215,195,215),//32
- Color.rgb(215,215,195),//64
- //175
- //225*2
- Color.rgb(175,225,225),//128
- Color.rgb(225,175,225),//256
- Color.rgb(225,225,175),//512
- //235
- //155*2
- Color.rgb(235,155,155),//1024
- Color.rgb(155,235,155),//2048
- Color.rgb(155,155,235),//4096
- //115
- //255*2
- Color.rgb(115,255,255),//8192
- Color.rgb(255,115,255),//16384
- Color.rgb(255,255,115),//32768
-
- Color.rgb(195,195,195),//65536
- };
- public static Color[] CF= {//数字颜色
-
- };
- }
- package _2048._node;
-
- import javafx.application.Application;
- import javafx.application.Platform;
- import javafx.scene.control.Alert;
- import javafx.scene.control.Menu;
- import javafx.scene.control.MenuBar;
- import javafx.scene.control.MenuItem;
- import javafx.scene.control.RadioMenuItem;
- import javafx.scene.control.ToggleGroup;
- import javafx.scene.control.Alert.AlertType;
-
- /**
- * 节点类——2048游戏菜单栏
- * @author 邦邦拒绝魔抗
- *
- */
- public class _GameMenuBar extends MenuBar {
- private Callbacks mCallbacks;
- private Menu scoreMenu;
-
- /**回调接口*/
- public interface Callbacks {
- void afterRestart();//重新开始
- void afterResetGridSize(int cols,int rows);//重设表格尺寸
- void afterGetMoreScoreInfo();//获取更详细的分数信息
- }
-
- public _GameMenuBar(Application application) {//application供回调方法使用
- mCallbacks=(Callbacks)application;
- //Game菜单
- Menu gameMenu=new Menu("游戏");//游戏
- MenuItem restartMenuItem=new MenuItem("重新开始");//重新开始
- restartMenuItem.setOnAction(e->mCallbacks.afterRestart());
- MenuItem exitMenuItem=new MenuItem("退出");//退出
- exitMenuItem.setOnAction(e->Platform.exit());
- gameMenu.getItems().addAll(restartMenuItem,exitMenuItem);
-
- //Setting菜单
- Menu settingMenu=new Menu("设置");//设置
- ToggleGroup tg=new ToggleGroup();//组
-
- RadioMenuItem r44MenuItem=new RadioMenuItem("尺寸:4x4");
- r44MenuItem.setOnAction(e->mCallbacks.afterResetGridSize(4,4));
- RadioMenuItem r55MenuItem=new RadioMenuItem("尺寸:5x5");
- r55MenuItem.setOnAction(e->mCallbacks.afterResetGridSize(5,5));
- RadioMenuItem r66MenuItem=new RadioMenuItem("尺寸:6x6");
- r66MenuItem.setOnAction(e->mCallbacks.afterResetGridSize(6 ,6));
-
- r44MenuItem.setToggleGroup(tg);
- r55MenuItem.setToggleGroup(tg);
- r66MenuItem.setToggleGroup(tg);
- settingMenu.getItems().addAll(r44MenuItem,r55MenuItem,r66MenuItem);
- r44MenuItem.setSelected(true);//默认选中4x4
-
- //Info菜单
- Menu infoMenu=new Menu("信息");//信息
- MenuItem helpMenuItem=new MenuItem("帮助");//帮助
- helpMenuItem.setOnAction(e->{
- Alert alert=new Alert(AlertType.INFORMATION);
- alert.setTitle(alert.getAlertType().toString());
- alert.setContentText("操作方式:\n"+
- "向上滑动:方向键↑或键W\n"+
- "向下滑动:方向键↓或键S\n"+
- "向左滑动:方向键←或键A\n"+
- "向右滑动:方向键→或键D\n"+
- "\n游戏规则:\n"+
- "相同数字的卡片在靠拢、相撞时会合并\n"+
- "在操作中合并的卡片会以红色边框凸显\n尽可能获得更大的数字!");
- alert.show();
- });
- MenuItem aboutUsMenuItem=new MenuItem("关于我们");//关于我们
- aboutUsMenuItem.setOnAction(e->{
- Alert alert=new Alert(AlertType.INFORMATION);
- alert.setTitle(alert.getAlertType().toString());
- alert.setContentText("游戏作者:邦邦拒绝魔抗\n他的邮箱:842748156@qq.com\n\n感谢你的游玩!");
- alert.show();
- });
- infoMenu.getItems().addAll(helpMenuItem,aboutUsMenuItem);
-
- //Record菜单
- Menu recordMenu=new Menu("记录");//记录
- MenuItem historyScoreMenuItem=new MenuItem("历史分数");//历史分数
- historyScoreMenuItem.setOnAction(e->{
- Alert alert=new Alert(AlertType.INFORMATION);
- alert.setTitle(alert.getAlertType().toString());
- alert.setContentText("还没有制作喵");
- alert.show();
- });
- recordMenu.getItems().addAll(historyScoreMenuItem);
-
-
- //Score菜单
- scoreMenu=new Menu("分数");//分数
- MenuItem moreScoreInfo=new MenuItem("更多分数信息");//更多分数信息
- moreScoreInfo.setOnAction(e->mCallbacks.afterGetMoreScoreInfo());
- scoreMenu.getItems().addAll(moreScoreInfo);
- getMenus().addAll(gameMenu,settingMenu,infoMenu,recordMenu,scoreMenu);
- }
-
- /**获取分数菜单*/
- public Menu getScoreMenu() {
- return scoreMenu;
- }
- }
- import _2048._node._CardMatrixPane;
- import _2048._node._GameMenuBar;
- import javafx.application.Application;
- import javafx.geometry.Insets;
- import javafx.scene.Scene;
- import javafx.scene.control.Alert;
- import javafx.scene.control.Alert.AlertType;
- import javafx.scene.layout.BorderPane;
- import javafx.stage.Stage;
-
- /**
- * 2048运行类
- * @author 邦邦拒绝魔抗
- *
- */
- public class _2048Demo extends Application implements _GameMenuBar.Callbacks,_CardMatrixPane.Callbacks {
- private BorderPane borderPane;
- private _GameMenuBar menuBar;
- private _CardMatrixPane cardMatrixPane;
-
- @Override
- public void start(Stage primaryStage) {
- borderPane=new BorderPane();
- Scene scene=new Scene(borderPane,1000,600);
-
- //Top菜单栏
- menuBar=new _GameMenuBar(this);//创建菜单栏,并传入Application供调用
- borderPane.setTop(menuBar);//顶部
-
- //Center2048卡片矩阵
- cardMatrixPane=new _CardMatrixPane(this);
- cardMatrixPane.setPadding(new Insets(5,5,5,5));//外边距
- borderPane.setCenter(cardMatrixPane);//中心
-
- primaryStage.setTitle("2048");
- primaryStage.setScene(scene);
- primaryStage.show();
-
- startGame();
- // cardMatrixPane.testColors();//颜色测试
- }
-
- public static void main(String[] args) {
- Application.launch(args);
- }
-
- /**开始游戏*/
- private void startGame() {
- cardMatrixPane.requestFocus();//添加焦点
- cardMatrixPane.createKeyListener();//添加键盘监听
- afterScoreChange();
- }
-
- @Override
- public void afterRestart() {
- cardMatrixPane.restartMatrix();
- }
-
- @Override
- public void afterResetGridSize(int cols,int rows) {
- cardMatrixPane=new _CardMatrixPane(cols,rows,this);
- cardMatrixPane.setPadding(new Insets(5,5,5,5));//外边距
- borderPane.setCenter(cardMatrixPane);
- startGame();
- // cardMatrixPane.testColors();//颜色测试
- }
-
- @Override
- public void afterScoreChange() {
- menuBar.getScoreMenu().setText("分数: "+cardMatrixPane.getScore());
- }
-
- @Override
- public void afterGetMoreScoreInfo() {
- int[] temp=cardMatrixPane.getMcQuantities();
- Alert alert=new Alert(AlertType.INFORMATION);
- alert.setTitle(alert.getAlertType().toString());
- alert.setContentText(
- "4的合并次数: "+temp[0]+"\n"+
- "8的合并次数: "+temp[1]+"\n"+
- "16的合并次数: "+temp[2]+"\n"+
- "32的合并次数: "+temp[3]+"\n"+
- "64的合并次数: "+temp[4]+"\n"+
- "128的合并次数: "+temp[5]+"\n"+
- "256的合并次数: "+temp[6]+"\n"+
- "512的合并次数: "+temp[7]+"\n"+
- "1024的合并次数: "+temp[8]+"\n"+
- "2048的合并次数: "+temp[9]+"\n"+
- "4096的合并次数: "+temp[10]+"\n"+
- "8192的合并次数: "+temp[11]+"\n"+
- "16384的合并次数: "+temp[12]+"\n"+
- "32768的合并次数: "+temp[13]+"\n"+
- "65536的合并次数: "+temp[14]+"\n");
- alert.show();
- }
- }
一共700多行吧,里面有几行是注释掉的,它们在测试程序时候用过,方便调试,所以没删
_CardMatrixPane的testColors()方法正常流程是用不到的,测试程序时候查看所有卡片的颜色和字号用,可以删掉
代码里注释已经比较详细了,所以这里并不会涉及到很多的细节
卡片的类型和显示的数字间关系很简单,在换算方法getNumber()中也有体现,数字就是2的type次方。其中的例外是2的0次方,这时候类型0即空卡片,不显示数字
为什么要加一个类型呢,举例的话用类型作为数组的下标取颜色就很方便,数字就不够紧凑了。当然,显示时的换算是一个开销,可以搞一个专门的存储数字的数组,用类型作为下标取数字,那就不用换算了
merge用来记录在一次操作中(向上、向下、向左、向右)已经合并了的卡片,逻辑上不希望卡片间连续地合并,这也符合2048游戏的基本规则
数据绑定,矩形的尺寸和GridPane的单元格尺寸绑定,缩放页面时候会跟随变化
用红色边框突出显示已合并的卡片,这里就是方便看的,可以删掉
重写了Object类的toString()方法,它是为了debug时方便看而设置的,可以删掉,用不到
考虑到5x5和6x6的情况,卡片矩阵的行列数是变量而非常量
写了回调方法afterScoreChange(),这是因为卡片矩阵越职去修改菜单栏的分数是不好的,所以把这项工作交给了控制器来完成
对于卡片矩阵的宽高变化设有监听器,它随之修改卡片矩阵中GridPane的尺寸,还有单元格的尺寸和显示数字的尺寸。因为GridPane需要是正方形的,它的边长便取卡片矩阵宽高中的最小值。而卡片矩阵的宽高接近于窗口的宽高
这里有一个逻辑上的问题,按照2048游戏的基本规则,如果一次操作中没有出现任何卡片的移动或合并(矩阵中还有空卡片),就不应该生成新的2或4了,但这个程序的表现是会生成的,大家可以自行修改
createRandomNumber()方法会返回一个布尔值来表示矩阵里还有没有空卡片,有时并不需要这个返回值,是因为我们认为矩阵里肯定还有空卡片,比如重新开始游戏的时候
一开始考虑了做卡片的数字颜色,后来偷懒都用黑色了
同样,菜单栏越职去访问和修改卡片矩阵是不好的,用回调方法把这些工作交给了控制器来完成
实现回调接口中的各个回调方法,在恰当的时机控制各个节点
更新:对部分代码优化,修改了分数统计的形式(改为了用表格来展示)
更新:最近在对代码进行重构,并尝试加入ai,完成之后会补充到这里(也可能另外写一篇)
……
更新:补图
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。