目录
运行环境:
文档要求:
参考:
基础版本:
源代码部分:
进阶功能:
演示效果:
java jdk1.8
选题9:2048游戏
【问题描述】
2048游戏是一款比较流行的数字游戏,每次可以选择上下左右滑动,每滑动一次,所有的数字方块都会往滑动的方向靠拢,系统也会在空白的地方乱数出现一个数字方块,相同数字的方块在靠拢、相撞时会相加。不断的叠加最终拼凑出2048这个数字就算成功。
【任务要求】
1) 基本功能:方向控制、计分等。
2) 利用图形化插件实现界面,至少包括游戏介绍、游戏开始、游戏结束三部分。
3) 设计关卡、音效等其他功能。
【测试数据】
自行设定。
【成绩评定】
1) 基本完成任务要求1、2,评定为“及格”或“中等”;
2) 正确完成任务要求1、2,算法优化、报告规范,评定为“良好”或“优秀”;
3) 正确完成任务要求1、2、3,评定为“优秀”。
参考视频:
用Java实现2048游戏完整版
参考文档:
Java进阶之菜单栏---JMenuBar
java实现actionlistener_ActionListener的三种实现方法
【Java】 Java 实现 2048 小游戏
JAVA实现2048小游戏
java颜色代码对照表
RGB颜色对照表
四个Class
Main.java
package new2048;
public class Main {
public static void main(String[] args) {
GameWindow frame = new GameWindow();//创建窗口对象
GamePanel panel = new GamePanel(frame);//创建画布对象变量为frame
frame.add(panel);//在窗口中加入画布
frame.setVisible(true);//设置为可见
}
}
GameWindow.java
package new2048;
import javax.swing.*;
import java.awt.*;
public class GameWindow extends JFrame {
public GameWindow(){
setTitle("2048小游戏");//标题
setSize(370, 520);//设置窗体大小
Color blue=new Color(99,184,255);
getContentPane().setBackground(blue);//设置默认背景颜色
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//关闭后进程退出
setLocationRelativeTo(null);//居中
setResizable(false);//设置窗体不允许改变
}
}
GamePanel.java
package new2048;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.*;
import javax.swing.event.AncestorListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.security.PublicKey;
public class GamePanel extends JPanel implements ActionListener{
private JFrame frame=null;//创建一个JFrame变量
private GamePanel panel=null;//创建一个画布变量
private static final int Long =4;
private static final int High =4;
private Card[][] cards = new Card [Long][High] ;
private String gameFlag = "start";
private boolean isadd = true; // 是否新增数字
JLabel lscore = new JLabel("0", JLabel.CENTER);
public GamePanel(JFrame frame){
this.setLayout(null);//设置布局为空
this.setOpaque(false);//设置不透明
this.frame=frame;//使frame为传入的参数
this.panel=this;//设panel为new生成的对象
createMenu();//创建菜单
createLapel();//创建标签
createCard();//创建卡片
createRandomNum();//随机生成数字
createKeyListener();//键盘监听
}
//键盘监听方法
private void createKeyListener() {
KeyAdapter l = new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {//监听按下键盘
if(!"start".equals(gameFlag)) //如果游戏结束则返回
return ;
int key = e.getKeyCode();
switch (key) {
//向上
case KeyEvent.VK_UP:
case KeyEvent.VK_W:
moveCard(1);
break;
//向下
case KeyEvent.VK_DOWN:
case KeyEvent.VK_S:
moveCard(2);
break;
//向左
case KeyEvent.VK_LEFT:
case KeyEvent.VK_A:
moveCard(3);
break;
//向右
case KeyEvent.VK_RIGHT:
case KeyEvent.VK_D:
moveCard(4);
break;
}
}
};
frame.addKeyListener(l);
}
//按下键盘移动卡片
private void moveCard(int fx){
clearCard();
sumScore();
if(fx==1){
moveCardTop(true);
}
else if(fx==2){
moveCardBottom(true);
}
else if(fx==3){
moveCardLeft(true);
}
else if(fx==4){
moveCardRight(true);
}
createRandomNum();//移动后再随机生成数字
repaint();//重绘卡片
gameOverOrNot();//判断游戏结束
}
//计算累计分数
/*分数算法,自己瞎想的,大概就是遍历每个卡片的数字,然后加起来*/
int sum=0;
public int sumScore() {
Card ovo;
System.out.println(sum);
//遍历所有卡片
sum=0;
for (int i = 0; i < Long; i++) {
for (int j = 0; j < High; j++) {
ovo = cards[i][j];
sum=sum+ovo.getNum();
}
}
return sum;
}
//判断游戏是否结束
private void gameOverOrNot() {
/*结束条件:
1.达到2048
2.卡片已满
3.没有可以合并的卡片*/
lscore.setText(sum+"");
if(isWin()){//胜利
gameWin();
}
//位置已满
else if(cardFull())
{
if(
//只要有一个方向可以移动或者合并,表示没结束
moveCardTop(false)||
moveCardRight(false)||
moveCardBottom(false)||
moveCardLeft(false))
{
return ;
//游戏失败
}else{
gameOver();
}
}
}
/*自己写的笨办法,通过给win和over赋值判断,因为布尔值有点混*/
int pd=0;
//游戏胜利
private int gameWin() {
return pd=1;
}
//游戏结束
private int gameOver() {
return pd=2;
}
//判断是否胜利
private boolean isWin(){
Card card;
//遍历所有卡片
for (int i = 0; i < Long; i++) {
for (int j = 0; j < High; j++) {
card = cards[i][j];
//如果有2048
if(card.getNum()==2048){
return true;
}
}
}
return false;
}
//清理卡片的合并标记
private void clearCard() {
Card card;
for (int i = 0; i < Long; i++) {
for (int j = 0; j < High; j++) {
card = cards[i][j];
card.setMerge(false);
}
}
}
//向上移动
private boolean moveCardTop(boolean bool) {
boolean res = false;
Card card;
for (int i = 1; i < Long; i++) {
for (int j = 0; j < High; j++) {
card = cards[i][j];
if(card.getNum()!=0){//数字不为0
if(card.moveTop(cards,bool)){
res = true;
}
}
}
}
return res;
}
//向下移动
private boolean moveCardBottom(boolean bool) {
boolean res = false;
Card card;
for (int i = Long-1; i >=0; i--) {
for (int j = 0; j < High; j++) {
card = cards[i][j];
if(card.getNum()!=0){
if(card.moveBottom(cards,bool)){
res = true;
}
}
}
}
return res;
}
//向左移动
private boolean moveCardLeft(boolean bool) {
boolean res = false;
Card card;
for (int i = 0; i < Long; i++) {
for (int j = 1; j < High ; j++) {
card = cards[i][j];
if(card.getNum()!=0){
if(card.moveLeft(cards,bool)){
res = true;
}
}
}
}
return res;
}
//向右移动
private boolean moveCardRight(boolean bool) {
boolean res = false;
Card card;
for (int i = 0; i =0 ; j--) {
card = cards[i][j];
if(card.getNum()!=0){
if(card.moveRight(cards,bool)){
res = true;
}
}
}
}
return res;
}
//随机好显示的数字是2或者4
private void createRandomNum() {
int num=0;
Random r = new Random();
int n = r.nextInt(5);//随机取出0-4
if(n == 0) {
num = 4;
}
else{
num = 2;
}
if(cardFull()){
return ;
}
Card card = getRandomCard(r);//随机一张card
if(card!=null){
card.setNum(num);
}
}
private Card getRandomCard(Random r) {//获取随机卡片
int i = r.nextInt(Long);//i,j坐标随机
int j = r.nextInt(High);
Card kp = cards[i][j];
if(kp.getNum()==0){//如果找到空白卡片,就返回该卡片
return kp;
}
else {
return getRandomCard(r);
}
}
//判断卡片是否满了
private boolean cardFull() {
Card card;
for (int i = 0; i < Long; i++) {
for (int j = 0; j < High; j++) {
card = cards[i][j];
if(card.getNum()==0){
return false;
}
}
}
return true;
}
//创建卡片
private void createCard() {
Card card;
for (int i = 0; i < Long; i++) {
for (int j = 0; j < High; j++) {
card = new Card(i,j);
cards[i][j]=card;
}
}
}
//绘制面板
@Override
public void paint(Graphics g){
super.paint(g);
drawCard(g);
Font title = new Font("微软雅黑", Font.BOLD, 30);
Font title2 = new Font("微软雅黑", Font.BOLD, 30);
//如果游戏成功
if (pd==1) {
g.setColor(new Color(64, 64, 64, 150));
g.fillRect(0, 0, getWidth(), getHeight()); //画矩形
g.setColor(Color.RED); // 画笔颜色为红色
g.setFont(title);
FontMetrics fm = getFontMetrics(title);
String value = "恭喜你达到了2048!";
g.drawString(value,
(getWidth() - fm.stringWidth(value)) / 2,
getHeight() / 2);
//位置
}
//游戏失败
if (pd==2){
g.setColor(new Color(64, 64, 64, 150));
g.fillRect(0, 0, getWidth(), getHeight()); // 画矩形
g.setColor(Color.WHITE); // 画笔颜色为白色
g.setFont(title2);
FontMetrics fm = getFontMetrics(title);
String value = "下次加油!"; /*不知道写什么比较好,可以改*/
g.drawString(value,
(getWidth() - fm.stringWidth(value)) / 2,
getHeight() / 2);
}
//游戏重新开始
if(pd==3) {
g.setColor(new Color(64, 64, 64, 0));
g.fillRect(0, 0, getWidth(), getHeight()); //画矩形
}
}
//绘制卡片
private void drawCard(Graphics g) {
Card card;
for (int i=0;i
Card.java
package new2048;
import java.awt.Graphics;
import java.awt.Font;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
public class Card {
private int x=0; //x坐标
private int y=0; //y坐标
private int w=80;//宽
private int h=80;//高
private int i=0;//下标i
private int j=0;//下标j
private int xstart=5;//x方向偏移量
private int ystart=105;//y方向偏移量
private int num=0;//数字
private boolean merge = false;//是否合并,如果已经合并则不能再合并
public Card(int i,int j){
this.i=i;
this.j=j;
}
//计算坐标
private void sol() {
this.x = xstart + j*w +(j+1)*5;//偏移量+第几个*宽度+中间间隔
this.y = ystart + i*h +(i+1)*5;
}
//绘制卡片
public void draw(Graphics g) {
sol();
//根据数字获取对应的颜色
Color newcolor = getColor();
Color oldcolor= g.getColor();
//设置新颜色
g.setColor(newcolor);
g.fillRoundRect(x,y,w,h,4,4);//绘制圆角矩形
if(num!=0){
g.setColor(new Color(248,248,255));
Font n = new Font("微软雅黑",Font.BOLD,28);
String text = num+"";
g.setFont(n);
int textLen = getWordWidth(n,text,g);//获取文字长度
int tx = x +(w-textLen)/2;//原始坐标+(宽度-字符长度)/2,居中
int ty = y + 52;
g.drawString(text,tx,ty);
}
//还原颜色
g.setColor(oldcolor);
}
//获取字体长度
public static int getWordWidth(Font font,String content,Graphics g) {
FontMetrics metrics = g.getFontMetrics(font);
int width = 0;
for (int i = 0; i < content.length(); i++) {
width += metrics.charWidth(content.charAt(i));
}
return width;
}
private Color getColor(){
Color color=null;
switch (num) {
case 2:
color = new Color(142,229,238);
break;
case 4:
color = new Color(135,206,250);
break;
case 8:
color = new Color(100,149,237);
break;
case 16:
color = new Color(70,130,180);
break;
case 32:
color = new Color(65,105,225);
break;
case 64:
color = new Color(132,112,255);
break;
case 128:
color = new Color(123,104,238);
break;
case 256:
color = new Color(106,90,205);
break;
case 512:
color = new Color(58,95,205);
break;
case 1024:
color = new Color(0,154,205);
break;
case 2048:
color = new Color(16,78,139);
break;
default://卡片默认颜色
color = new Color(24,116,205);
break;
}
return color;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num=num;
}
//向上移动的方法
public boolean moveTop(Card[][] cards,boolean bool) {
//设定递归的退出条件
if(i==0){
return false;
}
//上一个卡片
Card prev = cards[i-1][j];
//交换上去的
if(prev.getNum()==0){
if(bool){
prev.num=this.num;
this.num=0;
prev.moveTop(cards,bool);
}
return true;
//要合并
}else if(prev.getNum()==num && !prev.merge){
if(bool){
prev.merge=true;
prev.num=this.num*2;
this.num=0;
}
return true;
}else {
return false;
}
}
//向下移动的方法
public boolean moveBottom(Card[][] cards,boolean bool) {
//设定递归的退出条件
if(i==3){
return false;
}
//上一个卡片
Card old = cards[i+1][j];
//空白卡片直接移动
if(old.getNum()==0){
if(bool){
old.num=this.num;
this.num=0;
old.moveBottom(cards,bool);
}
return true;
//合并
}else if(old.getNum()==num && !old.merge){
if(bool){
old.merge=true;
old.num=this.num*2;
this.num=0;
}
return true;
}else {
return false;
}
}
//向左移动
public boolean moveLeft(Card[][] cards,boolean bool) {
//设定递归的退出条件
if(j==0){
return false;
}
Card old = cards[i][j-1];
if(old.getNum()==0){
if(bool){
old.num=this.num;
this.num=0;
old.moveLeft(cards,bool);
}
return true;
}else if(old.getNum()==num && !old.merge){
if(bool){
old.merge=true;
old.num=this.num*2;
this.num=0;
}
return true;
}else {
return false;
}
}
//向右移动
public boolean moveRight(Card[][] cards,boolean bool) {
if(j==3){
return false;
}
Card old = cards[i][j+1];
if(old.getNum()==0){
if(bool){
old.num=this.num;
this.num=0;
old.moveRight(cards,bool);
}
return true;
}else if(old.getNum()==num && !old.merge){
if(bool){
old.merge=true;
old.num=this.num*2;
this.num=0;
}
return true;
}else {
return false;
}
}
public boolean isMerge() {
return merge;
}
public void setMerge(boolean merge) {
this.merge = merge;
}
}
以上代码应该有点bug,但是忘记是哪个版本了,但是基础功能应该没问题。
后续修改了积分bug和失败后新游戏bug,增加了排行榜,增加了登录页面,添加了背景和logo。代码就不放上来了。
(有bgm慎点,刚好那个时候在放歌)
2048小游戏演示