Java實現(xiàn)貪吃蛇游戲
最近JAVA和JSwing上手練習(xí)了一下貪吃蛇,供大家參考,具體內(nèi)容如下

歡迎交流和加入新的內(nèi)容
用到了JSwing,下面是一些具體的思路
實現(xiàn)
* 蛇:
采用單鏈表記錄首尾,整個蛇被分為lattice格子,放在map里
* 移動:
我在實現(xiàn)的過程中發(fā)現(xiàn)最難得反而是蛇的定義和實現(xiàn)。一直想著怎么樣用單獨的方法表示出蛇來,但是如果將蛇單獨實現(xiàn),總有些細(xì)節(jié)實現(xiàn)起來特別麻煩
其實蛇移動并非牽一發(fā)而動全身,其實身子是沒有發(fā)生變化的,關(guān)鍵是兩點:
a.頭的移動
b.尾巴的移動
實現(xiàn):
直接把蛇實現(xiàn)在地圖的小格子里,不再單獨設(shè)置子類或者ArrayList等,Map里加上蛇頭的坐標(biāo),從而使得Map可以根據(jù)蛇頭改變蛇的坐標(biāo)(類似于變量交換)。為頭部單獨設(shè)置x,y,作為移動的方向(也可以作為靜態(tài)變量x和y,不過沒什么區(qū)別),為身子設(shè)置next指針,只要next.next不是尾巴,那么保持不變。如果next是尾巴,就把自己的設(shè)置為尾巴,并且改變next,使之成為普通地圖塊。(refresh方法)
* 控制方向:
使用鍵盤事件,目前僅設(shè)置了wasd四個
* 窗口設(shè)計:
view extends JPanel,控制顯示,然后在Lattice里調(diào)用Graphics.draw(...)實現(xiàn)對每個格子的顯示
下面是核心的map部分代碼(包括自動移動,檢測食物,增加長度等等)
import codes.myGame.snake.cell.Lattice;
import java.util.Random;
public class Smap {
private boolean getFood = false;//如果得到食物,該指針設(shè)為true,并且在隨后的autoChange里增加蛇的長度
private boolean gameOver = false;
private boolean directionChange = false;//這里標(biāo)志的作用是保證在一次運動期間只會進行一次轉(zhuǎn)向,使游戲更流暢
private int MAP_SIZE;
private Lattice[][] map;
private int directionX = 0;//下一次頭在當(dāng)前位置的哪個方向上
private int directionY = 1;//下一次頭在當(dāng)前位置的哪個方向上
private int[] head = new int[2];//記錄當(dāng)前頭的位置
private int[] food = new int[2];//記錄當(dāng)前食物的位置
public Smap(int size) {
MAP_SIZE = size;
map = new Lattice[MAP_SIZE][MAP_SIZE];
for(int i=0;i<size;i++){
for (int j = 0 ;j<size;j++){
map[i][j] = new Lattice();
}
}
map[MAP_SIZE/2][MAP_SIZE/2].setHead(true,map[MAP_SIZE/2][MAP_SIZE/2-1]);//初始化設(shè)置一個頭結(jié)點,以及他的尾節(jié)點
head[0] = MAP_SIZE/2;
head[1] = MAP_SIZE/2;
map[MAP_SIZE/2][MAP_SIZE/2-1].setRear(true,null);
this.randFood();
}
//模擬蛇的自動移動
public void autoChange(){
this.setHead();
if(food[0]==head[0] && food[1]==head[1]){//如果新的頭部碰觸到了食物,那么尾部增長
getFood = true;
}
if(!gameOver)this.setRear();
if(getFood)this.randFood();
directionChange = false;
}
//根據(jù)鍵盤事件,改變頭的下一次移動方向,注意 該移動方向是僅針對頭部的
//setDirection和setHead兩個方法需要互斥進行,這里單線程,用synchronized即可
//(否則,如果當(dāng)前頭部在邊界位置,連續(xù)變幻方向可能導(dǎo)致在setHead里發(fā)生溢出)
public synchronized void setDirection(int x,int y){
if(directionY!=y && directionX!=x &&!directionChange){
directionX = x;
directionY = y;
directionChange = true;
}
}
public boolean gameOver(){
return gameOver;//頭碰到身子,證明gameOver
}
private synchronized void setHead(){
int i = head[0];
int j = head[1];
head[0] = ( head[0] + directionX + MAP_SIZE)%MAP_SIZE;
head[1] = ( head[1] + directionY + MAP_SIZE )%MAP_SIZE;
if(map[head[0]][head[1]].isBody())gameOver = true;
map[head[0]][head[1]].setHead(true,map[i][j]);
map[i][j].setBody(true,null);
map[i][j].setHead(false,null); //傳入null表示不改變當(dāng)前指向
}
//設(shè)置尾巴由于沒法像頭部那樣直接設(shè)置,這里只能采用鏈表遍歷的方式獲取尾巴
private void setRear(){
if(!getFood){
Lattice temp = map[head[0]][head[1]];
while (!temp.next.isRear())temp = temp.next;
temp.next().setRear(false,null);
temp.setRear(true,null);
temp.setBody(false,null);
}
}
private void randFood(){
getFood = false;
map[food[0]][food[1]].setFood(false);//先把當(dāng)前的食物取消掉
boolean flag = false;//設(shè)置下一個食物
Random random = new Random();
int x = random.nextInt(MAP_SIZE);
int y = random.nextInt(MAP_SIZE);
while (!flag){
x = random.nextInt(MAP_SIZE);
y = random.nextInt(MAP_SIZE);
if(!map[x][y].isHead() && !map[x][y].isRear() &&!map[x][y].isBody())flag = true;
}
map[x][y].setFood(true);
food[0] = x;
food[1] = y;
}
public Lattice get(int row, int col){
return map[row][col];
}
public int getMAP_SIZE() {
return MAP_SIZE;
}
}
下面是顯示部分的代碼
顯示分為兩部分,一塊是利用Graphics.draw()方法實現(xiàn)單個單元格的繪制,另一塊設(shè)置view類繼承自JPanel。負(fù)責(zé)繪制圖畫顯示
public class Lattice {
private boolean isBody = false;
private boolean isHead = false;
private boolean isFood = false;
private boolean isRear = false;
public Lattice next = null;
public void setHead(boolean bool,Lattice next){
isHead = bool;
if(next!=null)this.next = next;
}
public void setBody(boolean bool,Lattice next){
isBody = bool;
if(next!=null)this.next = next; //傳入?yún)?shù)為null時,不改變當(dāng)前的next
}
public void setRear(boolean bool,Lattice next){
isRear = bool;
this.next = next;
}
public void setFood(boolean bool){
isFood = bool;
}
public Lattice next(){
return next;
}
public boolean isHead(){
return isHead;
}
public boolean isFood(){
return isFood;
}
public boolean isRear(){
return isRear;
}
public boolean isBody(){
return isBody;
}
public void refresh(){
if(isHead){
isBody = true;
isHead = false;
// 怎么設(shè)置下一個頭呢?(考慮把DirectionX,Y放到Smap里,而不是這里)
}else if(isBody){
if(next.isRear){
next.isRear = false;
isRear = true;
isBody = false;
}
}
}
// 在這里設(shè)置細(xì)胞可見
public void draw(Graphics g, int x, int y, int size) {
g.setColor(black);
g.drawRect(x, y, size, size);
if ( isHead ) {
g.setColor( red);
g.fillRect(x, y, size, size);
}else if ( isBody || isRear) {
g.setColor(black);
g.fillRect(x, y, size, size);
}else if(isFood){
g.setColor( blue);
g.fillRect(x, y, size, size);
}
}
}
view部分:
import codes.myGame.snake.cell.Lattice;
import javax.swing.*;
import java.awt.*;
public class View extends JPanel {
private static final long serialVersionUID = -5258995676212660595L;
private static final int GRID_SIZE = 32; //填充的像素數(shù)量
private Smap thisMap;
public View(Smap map) {
thisMap = map;
}
@Override
public void paint(Graphics g) {
super.paint(g);
int size = thisMap.getMAP_SIZE();
for (int row = 0; row< size; row++ ) {
for (int col = 0; col< size; col++ ) {
Lattice lattice = thisMap.get(row, col);
if ( lattice != null ) {
lattice.draw(g, col*GRID_SIZE, row*GRID_SIZE, GRID_SIZE);//對應(yīng)的格子的顯示
}
}
}
}
@Override
public Dimension getPreferredSize() {//創(chuàng)建該div大小
return new Dimension(thisMap.getMAP_SIZE()*GRID_SIZE+1, thisMap.getMAP_SIZE()*GRID_SIZE+1);
}
}
更多有趣的經(jīng)典小游戲?qū)崿F(xiàn)專題,分享給大家:
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Java多種方式實現(xiàn)生產(chǎn)者消費者模式
這篇文章主要介紹了Java多種方式實現(xiàn)生產(chǎn)者消費者模式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-07-07
IntelliJ IDEA的數(shù)據(jù)庫管理工具實在太方便了(推薦)
這篇文章主要介紹了IntelliJ IDEA的數(shù)據(jù)庫管理工具實在太方便了,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-09-09
如何基于Autowired對構(gòu)造函數(shù)進行注釋
這篇文章主要介紹了如何基于Autowired對構(gòu)造函數(shù)進行注釋,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-10-10
SpringBoot生成PDF的五種實現(xiàn)方法總結(jié)
這篇文章主要介紹了SpringBoot生成PDF的五種實現(xiàn)方法,在開發(fā)中經(jīng)常會遇到需要進行對一些數(shù)據(jù)進行動態(tài)導(dǎo)出PDF文件,然后讓用戶自己選擇是否需要打印出來,這篇文章我們來介紹五種實現(xiàn)方法,需要的朋友可以參考下2024-10-10
Springboot+Redis執(zhí)行l(wèi)ua腳本的項目實踐
本文主要介紹了Springboot+Redis執(zhí)行l(wèi)ua腳本的項目實踐,詳細(xì)的介紹Redis與Lua腳本的結(jié)合應(yīng)用,具有一定的參考價值,感興趣的可以了解一下2023-09-09
IDEA “Cannot resolve symbol”爆紅問題解決
最近發(fā)現(xiàn)個問題,IDEA 無法識別同一個 package 里的其他類,將其顯示為紅色,本文就來介紹一下IDEA “Cannot resolve symbol”爆紅問題解決,感興趣的可以了解一下2023-10-10
idea社區(qū)版如何設(shè)置vm?options
這篇文章主要介紹了idea社區(qū)版如何設(shè)置vm?options問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-09-09

