JAVA大作业-实现2048小游戏

目录

运行环境:

文档要求:

参考:

参考视频:

参考文档:

基础版本:

源代码部分:

进阶功能:

演示效果: 


运行环境:

        java jdk1.8

文档要求:

选题92048游戏

【问题描述】

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 <Long; i++) {
			for (int j = High-1; j >=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<Long;i++){
			for (int j=0;j<High;j++){
				card = cards[i][j];
				card.draw(g);
			}
		}
	}

	private void createMenu() {
		//创建JMenuBar对象
		Font f =new Font("微软雅黑", Font.PLAIN,15);
		//新建字体对象
		JMenuBar jmb = new JMenuBar();
		JMenu game = new JMenu("游戏");//游戏菜单
		game.setFont(f);
		JMenu help = new JMenu("帮助");//帮助菜单
		help.setFont(f);
		JMenu record = new JMenu("排行榜");//排行榜菜单
		record.setFont(f);
		JMenu subject = new JMenu("更换背景");//更换颜色
		subject.setFont(f);
	
		
		jmb.add(game);
		jmb.add(help);
		jmb.add(record);
		jmb.add(subject);//加入菜单栏中
		
		
		JMenuItem game1 = new JMenuItem("新游戏");//加入菜单栏游戏子选项
		game1.setFont(f);
		JMenuItem game2 = new JMenuItem("退出");
		game2.setFont(f);
		game.add(game1);
		game.add(game2);
		
		JMenuItem help1 = new JMenuItem("胜利条件");//加入菜单栏帮助子选项
		help1.setFont(f);
		JMenuItem help2 = new JMenuItem("语言");
		help2.setFont(f);
		help.add(help1);
		help.add(help2);
		
		/*我只改了背景,因为感觉改每个卡片的颜色太麻烦了,不想写*/
		JMenuItem pink = new JMenuItem("少女粉");//加入更换颜色子选项
		pink.setFont(f);
		
		JMenuItem green = new JMenuItem("清新绿");
		green.setFont(f);
		
		JMenuItem blue = new JMenuItem("天空蓝");
		blue.setFont(f);
		subject.add(blue);
		subject.add(pink);
		subject.add(green);
		
		
		JMenuItem check = new JMenuItem("查看");//加入菜单栏排行榜子选项
		check.setFont(f);
		record.add(check);

		frame.setJMenuBar(jmb);
		
		//事件监听
		game1.addActionListener(this);
		game2.addActionListener(this);
		
		help1.addActionListener(this);
		help2.addActionListener(this);
		
		check.addActionListener(this);
		
		pink.addActionListener(this);
		green.addActionListener(this);
		blue.addActionListener(this);
		
		//事件命令
		game1.setActionCommand("restart");
		game2.setActionCommand("exit");
		
		help1.setActionCommand("win");
		help2.setActionCommand("lang");
		
		check.setActionCommand("re");
		
		pink.setActionCommand("pink");
		green.setActionCommand("green");
		blue.setActionCommand("blue");
		}
	
	//创建标签
	public void createLapel() {
		
		Font a=new Font("黑体",Font.BOLD,60);
		Font b=new Font("微软雅黑",Font.BOLD,20);
		Font c=new Font("微软雅黑",Font.PLAIN,15);//游戏说明字体
		
		JLabel title = new JLabel("2048", JLabel.CENTER);
        frame.add(title);
        title.setFont(a); //标题字体
        title.setForeground(Color.WHITE);//颜色
        title.setBounds(35, 5, 150, 60);//位置
        
    	//添加得分
        JLabel score = new JLabel("得  分", JLabel.CENTER);
        frame.add(score);
        score.setFont(b);
        score.setForeground(Color.WHITE);
        score.setOpaque(true); // 设置控件控件不透明
        score.setBackground(Color.GRAY);//背景为灰色
        score.setBounds(240, 0, 120, 30);
        
        //添加“0”
        frame.add(lscore);
        lscore.setFont(b); 
        lscore.setForeground(Color.WHITE);
        lscore.setOpaque(true); 
        lscore.setBackground(Color.GRAY);//背景为灰色
        lscore.setBounds(240, 30, 120, 30);
 
        // 游戏说明:
        JLabel ltips = new JLabel("键盘操作: ↑ ↓ ← →, 菜单“新游戏”重新开始。", JLabel.CENTER);
        frame.add(ltips);
        ltips.setFont(c);
        ltips.setForeground(Color.WHITE);
        ltips.setBounds(0, 60, 350, 40);
		
	}
	
	//事件触发
	public void actionPerformed(ActionEvent e) {
		String command = e.getActionCommand();
		Color pink=new Color(255,181,197);//粉色
		Color blue=new Color(99,184,255);//蓝色
		Color green=new Color(162,205,90);//绿色
		
		if( "restart".equals(command)){
			restart();
			}
		else if( "exit".equals(command)){
			Object[] options={"确定","取消"};
			int rechoose = JOptionPane.showOptionDialog(this,"你确定要退出吗?", "" ,JOptionPane.YES_OPTION ,
					JOptionPane.QUESTION_MESSAGE, null,options, options[0]);
			if(rechoose==0){//确认退出
			System.exit(0);
			}
		}
			else if( "win".equals(command)){
			JOptionPane.showMessageDialog(null, "得到数字2048获得胜利,当没有空卡片则失败!","提示!",JOptionPane.INFORMATION_MESSAGE);
}
		/*这一块我都没写具体内容*/
			else if("lang".equals(command)){
				JOptionPane.showMessageDialog(null, "暂不支持切换语言","警告",JOptionPane.INFORMATION_MESSAGE);
			}
			else if("re".equals(command)){
				JOptionPane.showMessageDialog(null, "您当前的排行为","查看",JOptionPane.INFORMATION_MESSAGE);
			}
			else if("pink".equals(command)){
				frame.getContentPane().setBackground(pink);
			}
			else if("green".equals(command)){
				frame.getContentPane().setBackground(green);
			}
			else if("blue".equals(command)){
				frame.getContentPane().setBackground(blue);
			}

		}
	//重新开始新游戏
	void restart() {
		gameFlag = "start";
		lscore.setText("0");
		Card card;
		for (int i = 0; i < Long; i++) {
			for (int j = 0; j < High; j++) {
				card = cards[i][j];
				card.setNum(0);
				card.setMerge(false);
				}
			}		
			createRandomNum();
			pd=3;//repaint第三条
			repaint();
			sum=0;
		}

}

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小游戏演示

  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

酒精过敏w

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值