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);
}
}