软件工程第二次作业
2048游戏
一、题目内容
题目内容:2048游戏
要求:实现2048游戏
实现方式:windows界面,主要利用Swing技术和事件处理机制编程。
做法:
1、利用Swing技术绘制2048界面。
2、定义Block类,设置数据块的背景颜色及数字的字体,还有不同数据块的颜色。
3、分别编写用户向上、向下、向右、向左滑动式的函数及其中的算法。
4、利用事件处理机制编写用户对键盘操作(上、下、左、右)时2048对应的实现。
二、背景及相关技术
(一)相关背景
2048是比较流行的一款数字游戏。原版2048首先在github上发布,原作者是Gabriele Cirulli。它是基于《1024》和《小3传奇》的玩法开发而成的新型数字游戏。
随后2048便出现各种版本,走各大平台。由Ketchapp公司移植到IOS的版本最为火热,现在约有1000万下载,其名字跟原版一模一样。衍生版中最出名的是《2048六边形》版本,先后在全球81个国家中的board game中排进了前200。安卓版非常火爆的有《挑战2048》,其2.0.0版以后还加入了双人对战。其次比较特别的有2048中国朝代版。更有2048自定义版,可以自己定义文字和图片。《2048~》是IOS中流行的一款。
(二)相关技术
利用Swing技术和事件监听机制来实现2048游戏的编程,用到的数据结构包括数组,类等,用户向上、向下、向左、向右滑动时的算法包括:数组元素赋值,数组元素之间的值相加,以及if条件语句和for循环语句。
三、概要设计
(一)2048游戏实现步骤
1.新建项目javatestà新建包my2048à新建类My2048主类和Block类。
引入程序中可能会用到的包(java.awt.*; java.awt.event.* ; javax.swing.*;)
My2048主类需要继承JFrame类并且实现KeyListener接口以及接口的所有方法。Block类:这个类的主要功能是设置2048界面中值为0的数据块的背景颜色,数字的字体,以及值不为0的数字块的背景颜色。主要用到setBackground()方法和 if---else if--。。。。---else语句。
2.在主类中声明需要的Swing组件以及变量。创建主类的构造函数,在构造函数中定义已经声明的组件和变量,设置窗口的标题、大小、位置和程序关闭操作。
3.给窗口加一个监听器,并把显示窗口的函数值设为true。
4.定义成员方法addBlock,用for语句初始化每个block[i],设置图标和文本的水平对齐方式,设置控件不透明。
5.定义成员方法appearBlock,用while语句实现系统随机给出的数字方块2或者4,while语句判断是否还能继续生成随机数字,其中要用到取随机数的方法Math.random()。在while语句中用双层if条件语句设置在数字块block[index]为空(即其值为0时)时,当Math.random()<0.5时, block[index]=2,否则block[index]=4。
6.定义成员方法judgeAppear,记录2048界面中已经出现的数字块的个数,当个数等于16时,停止随机生成数字。
7. 定义成员方法upBlock,即用户向上滑动时程序对应的操作。用双层for语句循环和if条件语句实现数字块的上移和垂直方向上的相邻数字块上移相加。
8.分别用类似upBlock()的定义方法定义downBlock(),rightBlock()和leftBlock()方法,实现用户向下,向右,向左滑动时程序对应的操作。
9.定义成员方法over(),当数字块在各个方向上都不能移动时在2048界面中出现CAMEOVER。
10.定义成员方法win(),当用户在这16个数字块中凑出2048时在界面中出现YOUWIN。
11.实现接口KeyListener的keyPressed(KeyEvent e)方法,用switch-----case语句分别实现用户对键盘的上、下、左、右触发时程序对应的操作。
对于KeyListener接口的其他方法要在程序中定义,可以没有方法体。
12.在主函数实例化My2048。
(二)逻辑图
四、系统实现
(一)My2048类
这个类的功能是利用Swing技术绘制2048图形界面,实例化每个随机生成的数据块,利用事件监听机制分别实现用户上移,下移,左移,右移时对应的2048界面的操作,游戏运行时出现2048或者不能移动时,利用对某些数据块赋值,实现游戏结束时可能会出现的结果(GAMEOVER或YOU WIN)。大致完成2048游戏的主要功能。
package my2048;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
@SuppressWarnings("serial")
public class My2048 extends JFrame implements KeyListener {
Block[] block;
JPanel panel;
boolean numFlag;
int moveFlag;
public My2048() {
numFlag = true;
moveFlag = 0;
block = new Block[16];
setTitle("2048");
setSize(400, 400);
setLocation(500, 200);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel = (JPanel) getContentPane();
panel.setLayout(new GridLayout(4, 4, 5, 5));
addBlock();
for (int i = 0; i < 2; i++)
appearBlock();
this.addKeyListener(this);
this.setVisible(true);
}
public void addBlock() {
for (int i = 0; i < 16; i++) {
block[i] = new Block();
block[i].setHorizontalAlignment(JLabel.CENTER);
// 不透明
block[i].setOpaque(true);
panel.add(block[i]);
}
}
public void appearBlock() {
while(numFlag) {
int index = (int) (Math.random() * 16);
if (block[index].getValue() == 0) {
if (Math.random() < 0.5)
block[index].setValue(2);
else
block[index].setValue(4);
break;
}
}
}
public void judgeAppear() {
int sum = 0;
for (int i = 0; i < 16; i++) {
if (block[i].getValue() != 0)
sum++;
}
if (sum == 16)
numFlag = false;
}
public void upBlock() {
for (int i = 12; i < 16; i++) {
int index = i;
for (int j = i - 4; j >= i - 12; j -= 4) {
int valueI = block[index].getValue(), valueJ = block[j]
.getValue();
if (valueJ == 0) {
block[index].setValue(0);
block[j].setValue(valueI);
} else {
if (valueI == valueJ) {
block[index].setValue(0);
block[j].setValue(valueI + valueJ);
if (valueI + valueJ == 4096)
win();
numFlag = true;
moveFlag = 0;
} else if (numFlag == false)
moveFlag += 1;
}
index = j;
}
}
}
public void downBlock() {
for (int i = 0; i < 4; i++) {
int index = i;
for (int j = i + 4; j <= i + 12; j += 4) {
int valueI = block[index].getValue(), valueJ = block[j]
.getValue();
if (valueJ == 0) {
block[index].setValue(0);
block[j].setValue(valueI);
} else {
if (valueI == valueJ) {
block[index].setValue(0);
block[j].setValue(valueI + valueJ);
if (valueI + valueJ == 4096)
win();
numFlag = true;
moveFlag = 0;
} else if (numFlag == false)
moveFlag += 1;
}
index = j;
}
}
}
public void rightBlock() {
for (int i = 0; i <= 12; i += 4) {
int index = i;
for (int j = i + 1; j <= i + 3; j++) {
int valueI = block[index].getValue(), valueJ = block[j]
.getValue();
if (valueJ == 0) {
block[index].setValue(0);
block[j].setValue(valueI);
} else {
if (valueI == valueJ) {
block[index].setValue(0);
block[j].setValue(valueI + valueJ);
if (valueI + valueJ == 4096)
win();
numFlag = true;
moveFlag = 0;
} else if (numFlag == false)
moveFlag += 1;
}
index = j;
}
}
}
public void leftBlock() {
for (int i = 3; i <= 15; i += 4) {
int index = i;
for (int j = i - 1; j >= i - 3; j--) {
int valueI = block[index].getValue(), valueJ = block[j]
.getValue();
if (valueJ == 0) {
block[index].setValue(0);
block[j].setValue(valueI);
} else {
if (valueI == valueJ) {
block[index].setValue(0);
block[j].setValue(valueI + valueJ);
if (valueI + valueJ == 4096)
win();
numFlag = true;
moveFlag = 0;
} else if (numFlag == false)
moveFlag += 1;
}
index = j;
}
}
}
@Override
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_UP:
upBlock();
judgeAppear();
appearBlock();
over();
break;
case KeyEvent.VK_DOWN:
downBlock();
judgeAppear();
appearBlock();
over();
break;
case KeyEvent.VK_LEFT:
leftBlock();
judgeAppear();
appearBlock();
over();
break;
case KeyEvent.VK_RIGHT:
rightBlock();
judgeAppear();
appearBlock();
over();
break;
}
}
public void over() {
if (!numFlag && moveFlag >= 36) {
block[4].setText("G");
block[5].setText("A");
block[6].setText("M");
block[7].setText("E");
block[8].setText("O");
block[9].setText("V");
block[10].setText("E");
block[11].setText("R");
block[11].addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
reStart();
}
});
}
}
public void win() {
block[0].setText("Y");
block[1].setText("O");
block[2].setText("U");
block[13].setText("W");
block[14].setText("I");
block[15].setText("N");
block[15].addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
reStart();
}
});
}
public void reStart(){
numFlag=true;
moveFlag=0;
for(int i=0;i<16;i++)
block[i].setValue(0);
for (int i = 0; i < 2; i++)
appearBlock();
}
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
}
public static void main(String args[]) {
try {
UIManager
.setLookAndFeel("org.jvnet.substance.skin.SubstanceRavenGraphiteLookAndFeel");
} catch (ClassNotFoundException | InstantiationException
| IllegalAccessException | UnsupportedLookAndFeelException e) {
e.printStackTrace();
}
JFrame.setDefaultLookAndFeelDecorated(true);
new My2048();
}
}
package my2048;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
@SuppressWarnings("serial")
public class My2048 extends JFrame implements KeyListener {
Block[] block;
JPanel panel;
boolean numFlag;
int moveFlag;
public My2048() {
numFlag = true;
moveFlag = 0;
block = new Block[16];
setTitle("2048");
setSize(400, 400);
setLocation(500, 200);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel = (JPanel) getContentPane();
panel.setLayout(new GridLayout(4, 4, 5, 5));
addBlock();
for (int i = 0; i < 2; i++)
appearBlock();
this.addKeyListener(this);
this.setVisible(true);
}
public void addBlock() {
for (int i = 0; i < 16; i++) {
block[i] = new Block();
block[i].setHorizontalAlignment(JLabel.CENTER);
// 不透明
block[i].setOpaque(true);
panel.add(block[i]);
}
}
public void appearBlock() {
while(numFlag) {
int index = (int) (Math.random() * 16);
if (block[index].getValue() == 0) {
if (Math.random() < 0.5)
block[index].setValue(2);
else
block[index].setValue(4);
break;
}
}
}
public void judgeAppear() {
int sum = 0;
for (int i = 0; i < 16; i++) {
if (block[i].getValue() != 0)
sum++;
}
if (sum == 16)
numFlag = false;
}
public void upBlock() {
for (int i = 12; i < 16; i++) {
int index = i;
for (int j = i - 4; j >= i - 12; j -= 4) {
int valueI = block[index].getValue(), valueJ = block[j]
.getValue();
if (valueJ == 0) {
block[index].setValue(0);
block[j].setValue(valueI);
} else {
if (valueI == valueJ) {
block[index].setValue(0);
block[j].setValue(valueI + valueJ);
if (valueI + valueJ == 4096)
win();
numFlag = true;
moveFlag = 0;
} else if (numFlag == false)
moveFlag += 1;
}
index = j;
}
}
}
public void downBlock() {
for (int i = 0; i < 4; i++) {
int index = i;
for (int j = i + 4; j <= i + 12; j += 4) {
int valueI = block[index].getValue(), valueJ = block[j]
.getValue();
if (valueJ == 0) {
block[index].setValue(0);
block[j].setValue(valueI);
} else {
if (valueI == valueJ) {
block[index].setValue(0);
block[j].setValue(valueI + valueJ);
if (valueI + valueJ == 4096)
win();
numFlag = true;
moveFlag = 0;
} else if (numFlag == false)
moveFlag += 1;
}
index = j;
}
}
}
public void rightBlock() {
for (int i = 0; i <= 12; i += 4) {
int index = i;
for (int j = i + 1; j <= i + 3; j++) {
int valueI = block[index].getValue(), valueJ = block[j]
.getValue();
if (valueJ == 0) {
block[index].setValue(0);
block[j].setValue(valueI);
} else {
if (valueI == valueJ) {
block[index].setValue(0);
block[j].setValue(valueI + valueJ);
if (valueI + valueJ == 4096)
win();
numFlag = true;
moveFlag = 0;
} else if (numFlag == false)
moveFlag += 1;
}
index = j;
}
}
}
public void leftBlock() {
for (int i = 3; i <= 15; i += 4) {
int index = i;
for (int j = i - 1; j >= i - 3; j--) {
int valueI = block[index].getValue(), valueJ = block[j]
.getValue();
if (valueJ == 0) {
block[index].setValue(0);
block[j].setValue(valueI);
} else {
if (valueI == valueJ) {
block[index].setValue(0);
block[j].setValue(valueI + valueJ);
if (valueI + valueJ == 4096)
win();
numFlag = true;
moveFlag = 0;
} else if (numFlag == false)
moveFlag += 1;
}
index = j;
}
}
}
@Override
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_UP:
upBlock();
judgeAppear();
appearBlock();
over();
break;
case KeyEvent.VK_DOWN:
downBlock();
judgeAppear();
appearBlock();
over();
break;
case KeyEvent.VK_LEFT:
leftBlock();
judgeAppear();
appearBlock();
over();
break;
case KeyEvent.VK_RIGHT:
rightBlock();
judgeAppear();
appearBlock();
over();
break;
}
}
public void over() {
if (!numFlag && moveFlag >= 36) {
block[4].setText("G");
block[5].setText("A");
block[6].setText("M");
block[7].setText("E");
block[8].setText("O");
block[9].setText("V");
block[10].setText("E");
block[11].setText("R");
block[11].addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
reStart();
}
});
}
}
public void win() {
block[0].setText("Y");
block[1].setText("O");
block[2].setText("U");
block[13].setText("W");
block[14].setText("I");
block[15].setText("N");
block[15].addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
reStart();
}
});
}
public void reStart(){
numFlag=true;
moveFlag=0;
for(int i=0;i<16;i++)
block[i].setValue(0);
for (int i = 0; i < 2; i++)
appearBlock();
}
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
}
public static void main(String args[]) {
try {
UIManager
.setLookAndFeel("org.jvnet.substance.skin.SubstanceRavenGraphiteLookAndFeel");
} catch (ClassNotFoundException | InstantiationException
| IllegalAccessException | UnsupportedLookAndFeelException e) {
e.printStackTrace();
}
JFrame.setDefaultLookAndFeelDecorated(true);
new My2048();
}
}
(二)Block类
这个类的主要功能是设置2048界面中值为0的数据块的背景颜色,数字的字体,以及值不为0的数字块的背景颜色。主要用到setBackground()方法和if—else if。。。else条件语句。
package my2048;
import javax.swing.*;
import java.awt.*;
@SuppressWarnings("serial")
public class Block extends JLabel {
private int value;
public Block() {
value = 0;
setFont(new Font("font", Font.PLAIN, 40));
setBackground(Color.gray);
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
String text = String.valueOf(value);
if (value != 0)
setText(text);
else
setText("");
setColor();
}
public void setColor() {
if (this.value == 0)
setBackground(Color.gray);
else if (this.value == 2)
setBackground(new Color(238, 228, 218));
else if (this.value == 4)
setBackground(new Color(238, 224, 198));
else if (this.value == 8)
setBackground(new Color(243, 177, 116));
else if (this.value == 16)
setBackground(new Color(243, 177, 116));
else if (this.value == 32)
setBackground(new Color(248, 149, 90));
else if (this.value == 64)
setBackground(new Color(249, 94, 50));
else if (this.value == 128)
setBackground(new Color(239, 207, 108));
else if (this.value == 256)
setBackground(new Color(239, 207, 99));
else if (this.value == 512)
setBackground(new Color(239, 203, 82));
else if (this.value == 1024)
setBackground(new Color(239, 199, 57));
else if (this.value == 2048)
setBackground(new Color(239, 195, 41));
else if (this.value == 4096)
setBackground(new Color(255, 60, 57));
}
}
五、结果测试
(一)运行环境
运行环境:windows7。
开发工具:Eclipse
(二)运行结果
六、性能分析
性能测试工具 :JDK自带的 Visualvm插件
(一)CPU
(二)堆
(三)类
(四)线程
七、心得体会
经过对程序反复调试以及不断完善,基本能实现2048游戏的功能,例如,用户每次可以选择上、下、左、右其中一个方向去滑动,每滑动一次,大部分的数字方块都会往滑动的方向靠拢,同时在空白的地方随机出现一个数字方块,相同数字的方块在靠拢、相撞时会相加。随机出现的数字方块不是2就是4,当用户凑出2048是就胜利,并且会出现YOU WIN 字样,当各个方向都不能滑动时,游戏失败,界面会出现GAMEOVER字样。2048的界面,一开始整体16个方格有14个都是灰色的(即是说有14个空的数据块),当用户滑动出现数字之后就会改变颜色。程序的不足之处是,每滑动一次,并不是所有的数字方块都会往滑动的方向靠拢。这点以后在程序中的上下左右滑动方法中加以改进。
通过对该游戏的设计,我对Java程序设计知识有了更进一步的认识;并且通过上机实践提高了我的动手能力。加深了我对Swing技术的理解,以及对事件监听机制的掌握。