java gui詳解貪吃蛇小游戲?qū)崿F(xiàn)流程
根據(jù)狂神的視頻做的,然后自己優(yōu)化了一些bug,比如新生成食物的時(shí)候不會(huì)生成在蛇的身體上,再新增長身體的時(shí)候不會(huì)在左上角出現(xiàn)一個(gè)綠色的方塊以及增加了難度控制功能,以及可以使用WASD進(jìn)行控制,而不僅僅限于上下左右方向鍵
最后的游戲界面是這樣的:

可以在上方的菜單欄里選擇不同的難度進(jìn)行游戲.難度對(duì)應(yīng)的是不同的蛇移動(dòng)的速度,點(diǎn)擊退出游戲和點(diǎn)擊右上角的X是一樣的功能,都是退出游戲.然后點(diǎn)擊重新開始就會(huì)回到最初的時(shí)候重新開始游戲.
按下空格開始游戲,隨時(shí)可以再次按下空格進(jìn)行暫停游戲.同時(shí)每吃到一個(gè)食物就會(huì)在右上角加10分.

游戲代碼一共分成了三個(gè)類
一個(gè)是GameStart類,用來創(chuàng)建JFrame繪制游戲主體.
一個(gè)是GamePanel類,用來添加在JFrame中,里面加入了菜單欄以及一些鍵盤監(jiān)聽器,用來根據(jù)讀入的信息操縱蛇移動(dòng).
另一個(gè)是Data類,用來讀取statics文件夾中的圖片素材,代碼更簡潔.

GameStart類代碼如下:(具體代碼后面都有注釋)
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* @author Laurie
*/
public class GameStart {
public static void main(String[] args) {
JFrame jframe = new JFrame("貪吃蛇");
jframe.setBounds(10, 10, 900, 720);//繪制游戲主界面的范圍
jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jframe.setResizable(false);//不可擴(kuò)大縮小窗口
GamePanel gamePanel = new GamePanel();
jframe.add(gamePanel);
jframe.setVisible(true);//顯形
gamePanel.setLayout(new BorderLayout());
}
}
GamePanel類代碼是:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;
/**
* @author Laurie
*/
public class GamePanel extends JPanel implements ActionListener, KeyListener {
int length;//蛇的長度
int[] snakeX = new int[500];//隨便定義長度,反之不出意外是不會(huì)超過的
int[] snakeY = new int[500];
String direction;//蛇的方向,來根據(jù)這個(gè)改變蛇頭的圖片
Timer timer = new Timer(60, this);//計(jì)時(shí)器,來觸發(fā)蛇移動(dòng)的事件
int score;//分?jǐn)?shù)
Boolean isFail = false;//是否失敗判斷
Boolean isStart = false;//是否啟動(dòng)判斷
int foodX;//食物坐標(biāo)
int foodY;
Random random = new Random();//用來產(chǎn)生隨機(jī)數(shù)的Random類的實(shí)例化
public void init(){
length = 3;//一開始蛇的三節(jié)
snakeX[0] = 100;//蛇頭為下標(biāo)0
snakeY[0] = 100;
snakeX[1] = 75;
snakeY[1] = 100;
snakeX[2] = 50;
snakeY[2] = 100;
direction = "R";//初始化方向朝右邊
score = 0;
foodX = getFoodX();//通過方法獲取食物的坐標(biāo)
foodY = getFoodY();
}
public GamePanel(){
//構(gòu)造函數(shù)
init();//進(jìn)行初始化
this.setFocusable(true);//設(shè)置可以被賦予焦點(diǎn)
this.setForeground(Color.pink);//設(shè)置為粉色
this.addKeyListener(this);//添加鍵盤的監(jiān)聽
timer.start();//開始計(jì)時(shí)器
JMenuBar menuBar = new JMenuBar();//選項(xiàng)欄
JButton jb1 = new JButton("退出游戲");
JButton jb3 = new JButton("新手難度");
JButton jb4 = new JButton("高手難度");
JButton jb5 = new JButton("地獄難度");
menuBar.add(jb1);
menuBar.add(jb3);
menuBar.add(jb4);
menuBar.add(jb5);
jb1.addActionListener(e -> {//添加jb1的監(jiān)聽
System.exit(0);//退出
});
jb3.addActionListener(e -> {
this.requestFocus();//不獲取焦點(diǎn)后面操作鍵盤都會(huì)在菜單欄里,就無法操作蛇了
timer.stop();//把原來的timer停止
timer = new Timer(80, this);//使蛇的速度變慢
timer.start();//重新開始timer
});
jb4.addActionListener(e -> {
this.requestFocus();
timer.stop();
timer = new Timer(40, this);
timer.start();
});
jb5.addActionListener(e -> {
this.requestFocus();
timer.stop();
timer = new Timer(20, this);
timer.start();
});
this.add(menuBar);
JButton jb2 = new JButton("重新開始");
jb2.addActionListener(e -> {
init();//重新開始所以重新初始化一下
repaint();//重畫
this.requestFocus();
});
this.add(jb2);
}
public int getFoodX(){
//如果沒有判斷的話會(huì)出現(xiàn)bug,也就是食物可能出現(xiàn)在蛇的身體里,所以要進(jìn)行判斷優(yōu)化
boolean flag = true;
int x = 25 + 25 * random.nextInt(34);//因?yàn)橐桓駷?5,所以要乘一個(gè)25
while(flag){
for(int i = 0; i < length; i++){
if(snakeX[i] == x) {
x = 25 + 25 * random.nextInt(34);//如果和身體重合就重新隨機(jī)一個(gè)數(shù)
}else{
flag = false;
}
}
}
return x;
}
public int getFoodY(){
boolean flag = true;
int y = 75 + 25 * random.nextInt(24);//24是計(jì)算出來的,總panel的大小除以25
while(flag){
for(int i = 0; i < length; i++){
if(snakeY[i] == y) {
y = 75 + 25 * random.nextInt(34);
}else{
flag = false;
}
}
}
return y;
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponents(g);//這個(gè)不加可能會(huì)出問題
this.setBackground(Color.white);
g.fillRect(0,0,90,30);
g.setColor(Color.black);
g.drawString("貪吃蛇大挑戰(zhàn)",10,20);
g.setColor(Color.LIGHT_GRAY);//分?jǐn)?shù)提示框
g.fillRect( 25, 36, 850, 40);
g.setColor(Color.pink);//游戲主界面
g.fillRect(25,75, 850, 600);
if ("R".equals(direction)) {//判斷是什么蛇頭朝向,選擇不同的蛇頭圖片
Data.right.paintIcon(this, g, snakeX[0], snakeY[0]);
} else if ("L".equals(direction)) {
Data.left.paintIcon(this, g, snakeX[0], snakeY[0]);
} else if ("U".equals(direction)) {
Data.up.paintIcon(this, g, snakeX[0], snakeY[0]);
} else if ("D".equals(direction)) {
Data.down.paintIcon(this, g, snakeX[0], snakeY[0]);
}
for (int i = 1; i < length; i++) {//通過for循環(huán)繪制蛇的身體
Data.body.paintIcon(this, g, snakeX[i], snakeY[i]);
}
g.setColor(Color.WHITE);
g.setFont(new Font("微軟雅黑", Font.BOLD, 18));
g.drawString("長度:"+length,650,54);
g.drawString("分?jǐn)?shù):"+score,750,54);//分?jǐn)?shù)長度等的繪制
Data.food.paintIcon(this, g, foodX, foodY);//繪制食物
if (!isStart) {//如果還沒開始就提示按下空格開始
g.setColor(Color.WHITE);
g.setFont(new Font("微軟雅黑", Font.BOLD, 40));
g.drawString("按下空格來開始游戲", 300, 300);
}
if (isFail) {//如果失敗了就提示按下空格重新開始
g.setColor(Color.red);
g.setFont(new Font("微軟雅黑", Font.BOLD, 40));
g.drawString("蛇死了哦,按下空格重新開始吧", 200, 300);
}
}
@Override
public void actionPerformed(ActionEvent e) {
if (isStart && !isFail) {//如果已經(jīng)開始并且沒有失敗
for (int i = length - 1; i > 0; i--) {
snakeX[i] = snakeX[i - 1];
//就把每節(jié)身體的位置都變成上一個(gè)身體的位置來模擬移動(dòng)
snakeY[i] = snakeY[i - 1];
}
if ("R".equals(direction)) {
//動(dòng)完了身體,這里動(dòng)蛇頭,根據(jù)不同的方向來動(dòng)到位置
snakeX[0] = snakeX[0] + 25;
if (snakeX[0] > 850) {
snakeX[0] = 25;
}
} else if ("L".equals(direction)) {
snakeX[0] = snakeX[0] - 25;
if (snakeX[0] < 25) {
snakeX[0] = 850;
}
} else if ("U".equals(direction)) {
snakeY[0] = snakeY[0] - 25;
if (snakeY[0] < 75) {
snakeY[0] = 650;
}
} else if ("D".equals(direction)) {
snakeY[0] = snakeY[0] + 25;
if (snakeY[0] > 650) {
snakeY[0] = 75;
}
}
if (snakeX[0] == foodX && snakeY[0] == foodY) {//判斷是否吃到食物
//如果兩者的xy坐標(biāo)重合了就說明吃到了
length++;//吃到了長度加一
snakeX[length - 1] = foodX - 1;//這里是避免出現(xiàn)一個(gè)bug,沒有這兩行就會(huì)在吃吃到以后左上角出現(xiàn)一個(gè)綠方塊
//這是因?yàn)樾略黾拥纳眢w沒有賦值坐標(biāo),所以就默認(rèn)在00的位置
snakeY[length - 1] = foodY - 1;
score = score + 10;
foodX = getFoodX();//重新生成新的食物
foodY = getFoodY();
}
for(int i = 1; i < length; i++){//進(jìn)行判斷是否死亡,當(dāng)蛇頭和身體重合就是死亡
if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) {
isFail = true;
break;
}
}
repaint();//蛇移動(dòng)了所以需要重新畫一遍
}
timer.start();//計(jì)時(shí)器重新開始,蛇再進(jìn)行下一步移動(dòng)
}
@Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();//這個(gè)為接受的鍵盤的按鍵對(duì)應(yīng)的代碼
if(keyCode == KeyEvent.VK_SPACE){//如果是空格的話激素hi進(jìn)行判斷
if(!isFail){//如果沒有失敗就進(jìn)行暫停與重新游戲的功能實(shí)現(xiàn)
isStart = !isStart;
}else{//如果已經(jīng)死了就重新開始
isFail = false;
init();
}
repaint();//重新繪制
}
if (keyCode == KeyEvent.VK_LEFT && !"R".equals(direction)) {//檢查獲取的是上下左右的哪個(gè)進(jìn)行蛇頭轉(zhuǎn)向
direction = "L";
} else if (keyCode == KeyEvent.VK_RIGHT && !"L".equals(direction)) {
direction = "R";
} else if (keyCode == KeyEvent.VK_UP && !"D".equals(direction)) {
direction = "U";
} else if (keyCode == KeyEvent.VK_DOWN && !"U".equals(direction)) {
direction = "D";
}
if (keyCode == KeyEvent.VK_A && !"R".equals(direction)) {//檢查獲取的是wasd的哪個(gè)進(jìn)行蛇頭轉(zhuǎn)向
//注意當(dāng)是與蛇頭反方向的不可以進(jìn)行轉(zhuǎn)動(dòng),畢竟也不能180度轉(zhuǎn)脖子把
direction = "L";
} else if (keyCode == KeyEvent.VK_D && !"L".equals(direction)) {
direction = "R";
} else if (keyCode == KeyEvent.VK_W && !"D".equals(direction)) {
direction = "U";
} else if (keyCode == KeyEvent.VK_S && !"U".equals(direction)) {
direction = "D";
}
}
//這兩個(gè)沒有用,所以不用寫了
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
}
}
Data類代碼是:
import javax.swing.*;
import java.awt.*;
import java.net.URL;
/**
* @author Laurie
*/
public class Data {
//讀取statics文件夾里的圖片,加載成圖片
public static URL bodyURL = Data.class.getResource("/statics/body.png");
public static URL foodURL = Data.class.getResource("/statics/food.png");
public static URL downURL = Data.class.getResource("/statics/down.png");
public static URL upURL = Data.class.getResource("/statics/up.png");
public static URL rightURL = Data.class.getResource("/statics/right.png");
public static URL leftURL = Data.class.getResource("/statics/left.png");
public static ImageIcon body = new ImageIcon(bodyURL);
public static ImageIcon food = new ImageIcon(foodURL);
public static ImageIcon down = new ImageIcon(downURL);
public static ImageIcon up = new ImageIcon(upURL);
public static ImageIcon right = new ImageIcon(rightURL);
public static ImageIcon left = new ImageIcon(leftURL);
}
整個(gè)項(xiàng)目文件可以在github上自取,點(diǎn)擊藍(lán)字
github
上不去的也可以在gitee上自取,點(diǎn)擊藍(lán)字
gitee
💕
真的不下一個(gè)自己玩一玩嗎[手動(dòng)滑稽]
到此這篇關(guān)于java gui詳解貪吃蛇小游戲?qū)崿F(xiàn)流程的文章就介紹到這了,更多相關(guān)java 貪吃蛇內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
mybatis單筆批量保存實(shí)體數(shù)據(jù)的方法
這篇文章主要介紹了mybatis單筆批量保存實(shí)體數(shù)據(jù)的相關(guān)知識(shí),非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2018-01-01
Spring?MVC異步上傳、跨服務(wù)器上傳和文件下載功能實(shí)現(xiàn)
這篇文章主要介紹了Spring?MVC異步上傳、跨服務(wù)器上傳和文件下載功能實(shí)現(xiàn),本文通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-07-07

