該專案是根據GUI編程學習所寫,詳細的學習課程可以參考狂神說老師的GUI課程:一小時開發貪吃蛇,
以下僅僅為簡要說一下邏輯,原始碼放在最后,
實作該專案總共用到三個類:Data資料類,用于存放靜態資料;Snake實作類,用于運行;GamePanel核心類,用于具體實作內部邏輯,
實作結果如圖:

Data類
要實作以上的內容,我們需要一些靜態圖片資源,包括蛇頭蛇身,介紹圖等,該類就是為了存放這些而設計的,如開頭廣告欄的存盤:
public static URL headURL = Data.class.getResource("/StaticFile/header.png");
public static ImageIcon header = new ImageIcon(headURL);
由于這些均為靜態資源,使用static進行存盤,
Snake類
該類為運行類,直接呼叫GamePanel核心類進行運行,它繼承了JFrame類,即一個最外層的視窗,只是在視窗中使用frame.add(new GamePanel());添加了面板,而該面板GamePanel即為真正的實作功能的類
GamePanel類
該類為真正的實作類,
實作貪吃蛇功能,我們要明確一點:表面上看見貪吃蛇一格一格動,實際上是整個圖片在一次一次變化,每一次貪吃蛇的移動,象征的是面板的一次更新,
所以邏輯很清晰了,要實作功能,先實作靜態,再實作動態:
先直接在這里寫上在該類中我用到的變數,便于后面大家對照分別是什么意思:
//定義蛇的資料結構
int length;//蛇的長度
String direct;//蛇的方向
int[] snakeX = new int[600];//蛇的x坐標
int[] snakeY = new int[500];//蛇的Y坐標
int score;//游戲成績
boolean isStart;//游戲當前的狀態,初始為暫停
boolean isFail;//游戲失敗狀態
//食物坐標
int foodX;
int foodY;
Random random = new Random();//亂數
Timer timer = new Timer(100,this);//定時器,100ms執行一次
這里對部分引數進行解釋:
- 亂數,用作確定食物的位置,食物要隨機分布,下面init()方法有提到,
- 定時器,定時重繪界面,前面已經講到了貪吃蛇的移動象征面板的一次更新,使用定時器就可以定時更新一次面板,也就可以規定貪吃蛇的移動速度了
首先我們需要初始化屬性的內容,不建議直接在定義時初始化,我這里采用了設計一個init()方法將這些引數初始化:
public void init(){
length = 3;
snakeX[0] = 100; snakeY[0] = 100;//腦袋坐標
snakeX[1] = 75; snakeY[1] = 100;//第一個身體坐標
snakeX[2] = 50; snakeY[2] = 100;//第二個身體坐標
score = 0;//積分初始為0
direct = "R";//初始方向向右
isStart = false;//初始為暫停
isFail = false;//默認為不失敗
//食物隨機分布在界面上
foodX = 25 + 25 * random.nextInt(34);
foodY = 75 + 25 * random.nextInt(24);
}
靜態圖片我們這里繼承JPanel類,重寫了protected void paintComponent(Graphics g)方法,該方法是用作繪制一次圖片的,只要創建該類物件,即可進行一次繪制,自動呼叫一次這個方法,初始樣式如下:
protected void paintComponent(Graphics g){
super.paintComponent(g);//清屏
}
當我們在運行游戲開始時,即呼叫一次該方法,使之在游戲未動的時候對面板進行一次靜態繪制,所以我們可以把我們想要畫的初始界面的樣子在這個時候先寫好,譬如:
protected void paintComponent(Graphics g){
super.paintComponent(g);//清屏
setBackground(Color.white);
//先繪制靜態面板
Data.header.paintIcon(this,g,25,11);//開頭廣告畫上去
g.fillRect(25,75,850,600);//默認游戲界面
//畫積分
g.setColor(Color.white);
g.setFont(new Font("微軟雅黑",Font.BOLD,18));
g.drawString("長度:"+length,750,35);
g.drawString("分數:"+score,750,55);
g.setColor(Color.white);
//設定字體
g.setFont(new Font("微軟雅黑",Font.BOLD,40));
g.drawString("按下空格開始游戲",300,300);
}
這樣就可以實作一個沒有小蛇沒有食物,但是基本框架都寫好的頁面,小蛇的靜態圖繪制只需要找好坐標,以上面類似繪制上廣告的邏輯寫即可:
Data.right.paintIcon(this,g,snakeX[0],snakeY[0]);//蛇頭
for (int i = 1; i < length; i++) {
Data.body.paintIcon(this,g,snakeX[i],snakeY[i]);//蛇身
}
這樣我們就可以實作類似于開頭這個圖一樣的頁面了,
靜態圖繪制好了,我們需要實作動態,那么邏輯應該是怎樣的呢?不妨把自己帶入成一個游戲玩家:
我們按下空格可以開始,暫停游戲,按下上下左右可以變換貪吃蛇的運動方向,所以我們很明顯,需要鍵盤監聽器,
當貪吃蛇吃了食物,長度會變長;貪吃蛇也會按照它的方向保持移動;貪吃蛇吃到自己的身體,會游戲失敗,很明顯需要事件監聽器,
所以要引入這些概念,我們很明顯,呼叫兩個監聽器介面即可:
public class GamePanel extends JPanel implements KeyListener, ActionListener{
@Override
public void keyPressed(KeyEvent e){}//實作鍵盤監聽
@Override
public void actionPerformed(ActionEvent e){}//實作事件監聽
//這兩個不需要實作,只需要寫上,不然會報錯
@Override
public void keyTyped(KeyEvent e) {}
@Override
public void keyReleased(KeyEvent e) {}
}
鍵盤監聽的邏輯很簡單:空格就是開始或者暫停,變換isStart變數;上下左右就是小蛇移動,變換direct變數,
事件監聽稍微麻煩一點:
-
如果游戲是開始的情況,小蛇就要移動,要使小蛇移動很簡單,就是讓小蛇的后一節身體的位置變到前一節身體的位置:
for (int i = length - 1; i > 0; i--) { //后一節身體變成前一節身體的位置 snakeX[i] = snakeX[i-1]; snakeY[i] = snakeY[i-1]; } -
因為要上下左右動,所以監聽方向,當向各種方向時,要變化蛇移動的距離和運動方向,考慮到該游戲的自行設計,設定為超出邊界就從另一邊出來,所以還要設計監聽邊界問題:
if (direct.equals("R")){//右移 snakeX[0] = snakeX[0] + 25; //邊界判斷 if (snakeX[0]>850){snakeX[0] = 25;} }else if (direct.equals("L")){//左移 snakeX[0] = snakeX[0] - 25; //邊界判斷 if (snakeX[0]<0){snakeX[0] = 850;} }else if (direct.equals("U")){//上移 snakeY[0] = snakeY[0] - 25; //邊界判斷 if (snakeY[0]<75){snakeY[0] = 650;} }else if (direct.equals("D")){//下移 snakeY[0] = snakeY[0] + 25; //邊界判斷 if (snakeY[0]>650){snakeY[0] = 75;} } -
貪吃蛇要吃東西,所以需要一個蛇頭碰到食物的情況:
if(snakeX[0] == foodX && snakeY[0] == foodY){ length++;//長度+1 score+=10; //然后再次隨機生成食物 foodX = 25 + 25 * random.nextInt(34); foodY = 75 + 25 * random.nextInt(24); } -
失敗事件,撞到自己就失敗了:
for (int i = 1 ; i<length;i++){ if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]){ isFail = true; repaint(); } }
監聽的話已經實作,但是還是沒有闡述如何動態,很簡單,在監聽器最后呼叫repaint()方法即可,這個方法可以重新呼叫paintComponent方法,也就是重新進行一次靜態繪制,而監聽器通過改變了屬性量,再進行一次靜態繪制時,就已經不是原來的那張圖了,而游戲中每時每刻都發生變化,這樣我們每次繪制的面板都不一樣,自然就能實作動態了,
當然,如果沒有加入這些監聽器是沒用的,這里我們再重寫以下無參構造:
public GamePanel(){
init();
//獲得焦點和鍵盤事件
setFocusable(true);
addKeyListener(this);
timer.start();
}
寫到這里會發現,那個靜態的繪制方法是不是過于簡陋了,所以我們需要在靜態方法里面加入一系列的判斷,因為每一次靜態繪制,定義的引數量都可能發生了變化,要想對他們的變化做出反應,就得針對他們不同情況的量做不同的繪制,這里在原始碼里面都有,不具體說了,
原始碼地址:
鏈接:百度網盤鏈接
提取碼:xb58
備注:里面包含了靜態資源,在staticFile檔案夾內,如果想使用請更改Data資料類里面的路徑
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/443449.html
標籤:Java
上一篇:JAVA學習程序記錄(一)
