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