JAVA_2048小游戏的实现练习

package com.lyc.java2048;

import java.awt.Graphics;
import java.awt.Image;

/**
*准备图片资源
*/
public class Background {

private final int BG_X = 0;

private final int BG_Y = 0;

private final int FG_X = 50;  

private final int FG_Y = 140;

public void paintBG(Graphics g){
	
	g.drawImage(Resources.IMG_BG, BG_X, BG_Y, null);
	g.drawImage(Resources.IMG_FG, FG_X, FG_Y, null);
}

public void paintMap(Graphics g, int index, int i, int j){
	switch (index) {
	case 1:
		map(g,Resources.IMG_02,i,j);
		break;
	case 2:
		map(g,Resources.IMG_04,i,j);
		break;
	case 3:
		map(g,Resources.IMG_08,i,j);
		break;
	case 4:
		map(g,Resources.IMG_16,i,j);
		break;
	case 5:
		map(g,Resources.IMG_32,i,j);
		break;
	case 6:
		map(g,Resources.IMG_64,i,j);
		break;
	case 7:
		map(g,Resources.IMG_128,i,j);
		break;
	case 8:
		map(g,Resources.IMG_256,i,j);
		break;
	case 9:
		map(g,Resources.IMG_512,i,j);
		break;
	case 10:
		map(g,Resources.IMG_1024,i,j);
		break;
	case 11:
		map(g,Resources.IMG_2048,i,j);
		break;
	default:
		break;
	}
}

public void map(Graphics g, Image img, int i, int j){
	g.drawImage(img, FG_X+100*j, FG_Y+100*i, null);
}

}

package com.lyc.java2048;

import java.awt.Image;

import javax.swing.ImageIcon;

/**

  • 加载游戏资源类
  • @author Administrator

*/
public class Resources {

 static final Image IMG_02;
 static final Image IMG_04;
 static final Image IMG_08;
 static final Image IMG_16;
 static final Image IMG_32;
 static final Image IMG_64;
 static final Image IMG_128;
 static final Image IMG_256;
 static final Image IMG_512;
 static final Image IMG_1024;
 static final Image IMG_2048;
 static final Image IMG_NUM;
 static final Image IMG_SCORE;
 static final Image IMG_HEIGHSCORE;
 static final Image IMG_BG;
 static final Image IMG_FG;

static {
	IMG_02 = getImage("res/2.png");
	IMG_04 = getImage("res/4.png");
	IMG_08 = getImage("res/8.png");
	IMG_16 = getImage("res/16.png");
	IMG_32 = getImage("res/32.png");
	IMG_64 = getImage("res/64.png");
	IMG_128 = getImage("res/128.png");
	IMG_256 = getImage("res/256.png");
	IMG_512 = getImage("res/512.png");
	IMG_1024 = getImage("res/1024.png");
	IMG_2048 = getImage("res/2048.png");
	IMG_NUM = getImage("res/num.png");
	IMG_SCORE = getImage("res/score.png");
	IMG_HEIGHSCORE = getImage("res/highscore.png");
	IMG_BG = getImage("res/bg.png");
	IMG_FG = getImage("res/fg.png");
}

static Image getImage(String path){
	
	return new ImageIcon(path).getImage();
}

}

package com.lyc.java2048;

import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.JPanel;

/**

  • 显示面板类
  • @author Administrator

*/
@SuppressWarnings(“serial”)
public class Game_2048 extends JPanel implements KeyListener{

private Background background;
private GameService gameService;

Game_2048(){
	background = new Background();
	gameService = new GameService();
}

public void paint(Graphics g){
	background.paintBG(g);
	gameService.gamePaint(g);
}

@Override
public void keyTyped(KeyEvent e) {
	
}

@Override
public void keyPressed(KeyEvent e) {
	boolean ismove = false;
	boolean isRemove = false;
	if(e.getKeyCode()==37){
		ismove=gameService.moveLeft();
		isRemove = gameService.removeLeft();
		if(ismove||isRemove){
			gameService.newBlock();
		}
	}
	else if(e.getKeyCode()==38){
		ismove=gameService.moveUp();
		isRemove = gameService.removeUp();
        if(ismove||isRemove){
        	gameService.newBlock();
		}
	}
	else if(e.getKeyCode()==39){
		ismove=gameService.moveRight();
		isRemove = gameService.removeRight();
		if(ismove||isRemove){
			gameService.newBlock();
		}
	}
	else if(e.getKeyCode()==40){
		ismove=gameService.moveDown();
		isRemove = gameService.removeDown();
		if(ismove||isRemove){
			gameService.newBlock();
		}
	}
	else if(e.getKeyCode()==27){
		int i = OptionPanel.showExitDialog();
		gameService.refreshHighscore();
		if(i==0){
			
			System.exit(0);
			
		}else{
			
		}
	}
	else if(e.getKeyCode()==112){
		int i = OptionPanel.showRestartDialog();
		gameService.refreshHighscore();
		if(i==0){
			
			gameService.restart();
		}else{
			
		}
		
	}
	repaint();
	gameService.isGameOver();//每走一步,判断是否结束游戏
	repaint();//解决游戏结束后的重新开始游戏画面问题
}

@Override
public void keyReleased(KeyEvent e) {
	
}

}

package com.lyc.java2048;

import java.awt.Graphics;

/**

  • 游戏分数处理类
  • @author Administrator

*/
public class Data {

private static final int SCORE_X=80;
private static final int SCORE_Y=20;
private static final int SCORE_HEIGHT_X=280;
private static final int SCORE_HEIGHT_Y=20;
private static final int NUM_SIZE = 21;//一个数字图片的大小  宽高都是21
private static final int SCORE_SIZE = 140;// 分数图片的宽度     最高分数图片的宽度  都是140


private int score;
private int heightScore;

public int getScore() {
	return score;
}


public void setScore(int score) {
	this.score = score;
}


public int getHeightScore() {
	return heightScore;
}


public void setHeightScore(int heightScore) {
	this.heightScore = heightScore;
}


public void painScore(Graphics g){
	
	g.drawImage(Resources.IMG_SCORE, SCORE_X, SCORE_Y, null);//画分数图片
	
	g.drawImage(Resources.IMG_HEIGHSCORE, SCORE_HEIGHT_X, SCORE_HEIGHT_Y, null);//画最高分图片
	
	drawScore(g);//画分数值
	
	draw_height_score(g);
}

/**
 * 画当前分数:
 * 动态画出分数图片数据,实现动态居中的效果
 * 1).需要传入一个分数数据
 * 2).计算出第一个数字的左上角x,y坐标位置
 *  1.获取图片正中间坐标位置:SCORE_X+图片宽度/2
	2.计算出需要画的数字的图片宽度:数字图片个数*21
	3.计算出左边要画出的位置,在中间坐标位置往左偏移数字图片宽度的一半:
	SCORE_X+图片宽度/2-数字图片个数*21/2
	3)计算出循环过程中其他数字的左上角的x和y
	第一个数字的左上角x+数字所在字符串中的索引值位置*21
 */
public void drawScore(Graphics g){
	int mid_x = SCORE_X+SCORE_SIZE/2;//中间位置的x坐标
	//score: 128   -> 转换成  "128"  可以求出个数
	//转换分数为字符串数据
	String score_str = score+"";
	//求出要画出的数字图片宽度
	int pic_width = score_str.length()*NUM_SIZE;
	//求出第一个数字图片的左上角位置
	int x = mid_x - pic_width/2;//x
	int y = SCORE_Y+40;//y
	
	//循环遍历所有的数字,逐个画出
	for (int i = 0; i < score_str.length(); i++) {
		//计算出分数数字字符串的值
		char c = score_str.charAt(i);//从字符串中,根据索引获取出字符    如    "128".charAt(0)->  '1'
		int num_post = c-'0';
		g.drawImage(
				Resources.IMG_NUM,      //num.png数字图片对象
				x+i*NUM_SIZE, y, //当前要画出的数字的左上角位置
				x+i*NUM_SIZE+NUM_SIZE, y+NUM_SIZE, //当前要画出的数字的右下角位置
				num_post*NUM_SIZE, 0, //num.png数字图片的左上角位置
				num_post*NUM_SIZE+NUM_SIZE, NUM_SIZE, //num.png数字图片的右下角位置
				null);
	}
	
}

public void draw_height_score(Graphics g){
	int mid_x = SCORE_HEIGHT_X+SCORE_SIZE/2;//中间位置的x坐标
	//score: 128   -> 转换成  "128"  可以求出个数
	//转换分数为字符串数据
	String score_str = heightScore+"";
	//求出要画出的数字图片宽度
	int pic_width = score_str.length()*NUM_SIZE;
	//求出第一个数字图片的左上角位置
	int x = mid_x - pic_width/2;//x
	int y = SCORE_Y+40;//y
	
	//循环遍历所有的数字,逐个画出
	for (int i = 0; i < score_str.length(); i++) {
		//计算出分数数字字符串的值
		char c = score_str.charAt(i);//从字符串中,根据索引获取出字符    如    "128".charAt(0)->  '1'
		int num_post = c-'0';
		g.drawImage(
				Resources.IMG_NUM,      //num.png数字图片对象
				x+i*NUM_SIZE, y, //当前要画出的数字的左上角位置
				x+i*NUM_SIZE+NUM_SIZE, y+NUM_SIZE, //当前要画出的数字的右下角位置
				num_post*NUM_SIZE, 0, //num.png数字图片的左上角位置
				num_post*NUM_SIZE+NUM_SIZE, NUM_SIZE, //num.png数字图片的右下角位置
				null);
	}
}

}

package com.lyc.java2048;

import java.awt.Graphics;
import java.util.Random;

/**

  • 游戏服务类,用于处理游戏中的数据
  • @author Administrator

*/
public class GameService {
private Data data;
private int[][] gameMap;

GameService(){
	data = new Data();
	start();
}

public void gamePaint(Graphics g){
	Background background = new Background();
	data.painScore(g);
	for (int i = 0; i < gameMap.length; i++) {
		for (int j = 0; j < gameMap[i].length; j++) {
			background.paintMap(g,gameMap[i][j],i,j);
			//System.out.print(gameMap[i][j]+",");
			
		}
		//System.out.println();
	}
	System.out.println(data.getScore());//模拟显示分数
}

public void start(){
	gameMap = new int[4][4];
	newBlock();
	newBlock();
}

public void newBlock(){
	int x,y;
	Random random = new Random();
	
	do{
		x=random.nextInt(4);
		y=random.nextInt(4);
		
	}while(gameMap[x][y]!=0);
	
	int index = random.nextInt(8);
	if(index==0){
		gameMap[x][y]=2;
	}else{
		gameMap[x][y]=1;
	}
}

/**
 * System.out.println("左移动");
 * @return
 */
public boolean moveLeft(){
	boolean ismove = false;
	
	for (int i = 0; i < 4; i++) {//遍历4行
		for (int j = 1; j < 4; j++) {//遍历列   列索引范围  [1~3]   0索引位置的列左边没有格子,不需要移动
			int mov_i = i;
			int mov_j = j;//当前移动格子的 坐标
			while(gameMap[mov_i][mov_j]!=0&&gameMap[mov_i][mov_j-1]==0){
				gameMap[mov_i][mov_j-1] = gameMap[mov_i][mov_j];//将当前方块数据赋值给左边位置
				gameMap[mov_i][mov_j] = 0;
				
				//移动下一个位置标记
				if(mov_j>1){
					mov_j--;
				}
				ismove = true;//有方块移动了
			}
		}
	}
	return ismove;
}

/**
 * System.out.println("右移动");
 * @return
 */
public boolean moveRight(){
	boolean ismove = false;
	
	for (int i = 0; i < 4; i++) {//遍历4行
		for (int j = 0; j < 3; j++) {//遍历列   列索引范围  [0~2]   3索引位置的列左边没有格子,不需要移动
			int mov_i = i;
			int mov_j = j;//当前移动格子的 坐标
			while(gameMap[mov_i][mov_j]!=0&&gameMap[mov_i][mov_j+1]==0){
				gameMap[mov_i][mov_j+1] = gameMap[mov_i][mov_j];//将当前方块数据赋值给左边位置
				gameMap[mov_i][mov_j] = 0;
				
				//移动下一个位置标记
				if(mov_j<2){
					mov_j++;
				}
				ismove = true;//有方块移动了
			}
		}
	}
	return ismove;
}

/**
 * System.out.println("上移动");
 * @return
 */
public boolean moveUp(){
	boolean ismove = false;
	
	for (int i = 1; i < 4; i++) {//第一行不用上移动
		for (int j = 0; j < 4; j++) {
			int mov_i = i;
			int mov_j = j;//当前移动格子的 坐标
			while(gameMap[mov_i][mov_j]!=0&&gameMap[mov_i-1][mov_j]==0){
				gameMap[mov_i-1][mov_j] = gameMap[mov_i][mov_j];
				gameMap[mov_i][mov_j] = 0;
				
				if(mov_i>1){//判断是否能够移动下一个位置的标记
					mov_i--;
				}
				ismove = true;
			}
		}
	}
	return ismove;
}

/**
 * System.out.println("下移动");
 * @return
 */
public boolean moveDown(){
	boolean ismove = false;
	for (int i = 0; i < 3; i++) {//最后一行不用下移动
		for (int j = 0; j < 4; j++) {
			int mov_i = i;
			int mov_j = j;//当前移动格子的 坐标
			while(gameMap[mov_i][mov_j]!=0&&gameMap[mov_i+1][mov_j]==0){
				gameMap[mov_i+1][mov_j] = gameMap[mov_i][mov_j];
				gameMap[mov_i][mov_j] = 0;
				
				if(mov_i<2){//判断是否能够移动下一个位置的标记
					mov_i++;
				}
				ismove = true;
			}
		}
	}
	return ismove;
}

public boolean removeLeft(){
	boolean isRemove = false;
	for (int i = 0; i < gameMap.length; i++) {
		for (int j = 0; j <3; j++) {
			if(gameMap[i][j]!=0 && gameMap[i][j]==gameMap[i][j+1]){
				gameMap[i][j]++;;
				gameMap[i][j+1]=0;
				isRemove=true;
				bonus(gameMap[i][j]);
			}
		}
	}
	moveLeft();
	return isRemove;
}

public boolean removeRight(){
	boolean isRemove = false;
	for (int i = 0; i < gameMap.length; i++) {
		for (int j = 3; j > 0; j--) {
			if(gameMap[i][j]!=0 && gameMap[i][j]==gameMap[i][j-1]){
				gameMap[i][j]++;;
				gameMap[i][j-1]=0;
				isRemove=true;
				bonus(gameMap[i][j]);
			}
		}
	}
	moveRight();
	return isRemove;
}


public boolean removeUp(){
	boolean isRemove = false;
	for (int i = 0; i <3 ; i++) {
		for (int j = 0; j < gameMap.length; j++) {
			if(gameMap[i][j]!=0 && gameMap[i][j]==gameMap[i+1][j]){
				gameMap[i][j]++;;
				gameMap[i+1][j]=0;
				isRemove=true;
				bonus(gameMap[i][j]);
			}
		}
	}
	moveUp();
	return isRemove;
}

public boolean removeDown(){
	boolean isRemove = false;
	for (int i = 3; i >0 ; i--) {
		for (int j = 0; j < gameMap.length; j++) {
			if(gameMap[i][j]!=0 && gameMap[i][j]==gameMap[i-1][j]){
				gameMap[i][j]++;;
				gameMap[i-1][j]=0;
				isRemove=true;
				bonus(gameMap[i][j]);
			}
		}
	}
	moveDown();
	return isRemove;
}

public void bonus(int num){
	int value = (int) Math.pow(2, num);
	data.setScore(data.getScore()+value);
}


/**
 * 刷新最高分
 */
public void refreshHighscore(){
	if(data.getScore() >data.getHeightScore()){
		data.setHeightScore(data.getScore());//如果当前分数大于历史最高分,则更新为最高分
	}
}

/**
 * 重新开始游戏
 * 1.gameMap清零
 * 2.生成两个新方块
 * 3.score清零
 */
public void restart(){
	start();
	data.setScore(0);
}

/**
 * 是否游戏结束
 * 1.达成2048    is2048()
 * a.刷新最高分
 * b.弹框提示是否重新开始游戏
 * c.重新开始游戏   restart()
 * d.不重新开始游戏  退出游戏
 * 
 * 2.无法移动或消除  canMove()
 * a.刷新最高分
 * b.弹框是否重新开始游戏
 * c.重新开始游戏   restart()
 * d.不重新开始游戏  退出游戏
 * 
 * @return
 */
public void isGameOver(){
	if(is2048()){//如果游戏胜利
		int choice = OptionPanel.showGameOverDialog(data);//0为重新开始按钮    1为结束游戏
		refreshHighscore();
		/*System.out.println(choice);*/
		if(choice==0){
			restart();
		}else{
			System.exit(0);
		}
	}else if(isFull()&&!canMove()){//或者游戏失败
		refreshHighscore();
		int choice = OptionPanel.showGameOverDialog(data);//0为重新开始按钮    1为结束游戏
		System.out.println(choice);
		if(choice==0){
			restart();
		}else{
			System.exit(0);
		}
	}
}

/**
 * 是否达成2048
 * 遍历gameMap,查找是否有11
 */
public boolean is2048(){
	boolean is2048 = false;
	for (int i = 0; i < 4; i++) {//遍历行
		for (int j = 0; j < 4; j++) {//遍历列
			if(gameMap[i][j]==11){
				is2048 = true;//达成2048
				return is2048;//直接返回,结束循环
			}
		}
	}
	return is2048;
}



/**
 * 是否不能移动或消除
 * 1.占满屏幕
 * 2.不能做上下左右任意的消除
 * @return    false  不能移动      true  可以移动
 */
public boolean canMove(){
	boolean canMove = false;
	if(isFull()){//占满屏幕
		//判断不能上下左右消除
		for (int i = 0; i < 4; i++) {
			for (int j = 0; j < 4; j++) {
				//能上消除  并且要排除掉最下面的一行  
				if(j<3&&gameMap[i][j]==gameMap[i][j+1]){
					canMove = true;
					return canMove;
				}
				//下消除
				if(j>0&&gameMap[i][j]==gameMap[i][j-1]){
					canMove = true;
					return canMove;
				}
				//左消除
				if(i<3&&gameMap[i][j]==gameMap[i+1][j]){
					canMove = true;
					return canMove;
				}
				//右消除
				if(i>0&&gameMap[i][j]==gameMap[i-1][j]){
					canMove = true;
					return canMove;
				}
			}
		}
	}
	return canMove;
}

/**
 * 是否占满屏幕
 * 遍历gameMap,全部不为0
 * @return
 */
public boolean isFull(){
	boolean isFull = true;
	for (int i = 0; i < 4; i++) {
		for (int j = 0; j < 4; j++) {
			if(gameMap[i][j]==0){//未占满屏幕
				isFull = false;
				return isFull;
			}
		}
	}
	return isFull;
}

}

package com.lyc.java2048;

import javax.swing.JOptionPane;

public class OptionPanel {

public static int showExitDialog(){
	return JOptionPane.showConfirmDialog(
			null, 
			"               退出游戏?", 
			"游戏提示", 
			JOptionPane.OK_CANCEL_OPTION);
}

public static int showRestartDialog(){
	return JOptionPane.showConfirmDialog(
			null, 
			"    重新开始游戏?", 
			"游戏提示", 
			JOptionPane.OK_CANCEL_OPTION);
}


public static int showGameOverDialog(Data data){
	/*
	 * JOptionPane.showOptionDialog
	 * (parentComponent, message, title, optionType, messageType,icon, options, initialValue)
	 * 
	 * JOptionPane:弹出要求用户提供值或向其发出通知的标准对话框
	 * showOptionDialog:询问一个确认问题,如 yes/no/cancel。
	 * parentComponent:定义父对话框,设置为null则默认的 Frame 用作父级
	 * message:要置于对话框中的描述消息,类型是 Object[]
	 * title:对话框的标题
	 * optionType:定义在对话框的底部显示的选项按钮的类型  YES_NO_OPTION
	 * messageType:定义 message 的样式   QUESTION_MESSAGE
	 * icon:要置于对话框中的装饰性图标。设置为null图标的默认值由 messageType 参数确定
	 * options:将在对话框底部显示的选项按钮集合的更详细描述。参数类型是 Object[] 
	 * initialValue:默认选择    
	 */
	Object[] options = {"重新开始","退出游戏"};
	return JOptionPane.showOptionDialog(
			null,
			"\no(╯□╰)o 游戏结束!\n      分数为:"+data.getScore(), 
			"游戏提示", 
			JOptionPane.YES_NO_OPTION, 
			JOptionPane.QUESTION_MESSAGE, 
			null, 
			options, 
			options[0]);
}

}

package com.lyc.java2048;

import javax.swing.JFrame;

/**

  • 游戏启动类
  • @author Administrator

*/
public class StartGame {

public static void main(String[] args) {

	JFrame frame = new JFrame();//创建一个窗口
	
	Game_2048 panel = new Game_2048();//自定义面板
	
	frame.add(panel);//窗口装载自定义面板
	
	frame.addKeyListener(panel);//添加监听器
	
	frame.setSize(505, 600);
	
	frame.setLocationRelativeTo(null);//居中
	
	frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//关闭窗口且关闭程序
	
	frame.setResizable(false);//窗体不可改变大小
	
	frame.setVisible(true);
}

}

猜你喜欢

转载自blog.csdn.net/qq_42902470/article/details/85074431