Java实现贪吃蛇游戏
最近JAVA和JSwing上手练习了一下贪吃蛇,供大家参考,具体内容如下
欢迎交流和加入新的内容
用到了JSwing,下面是一些具体的思路
实现
* 蛇:
采用单链表记录首尾,整个蛇被分为lattice格子,放在map里
* 移动:
我在实现的过程中发现最难得反而是蛇的定义和实现。一直想着怎么样用单独的方法表示出蛇来,但是如果将蛇单独实现,总有些细节实现起来特别麻烦
其实蛇移动并非牵一发而动全身,其实身子是没有发生变化的,关键是两点:
a.头的移动
b.尾巴的移动
实现:
直接把蛇实现在地图的小格子里,不再单独设置子类或者ArrayList等,Map里加上蛇头的坐标,从而使得Map可以根据蛇头改变蛇的坐标(类似于变量交换)。为头部单独设置x,y,作为移动的方向(也可以作为静态变量x和y,不过没什么区别),为身子设置next指针,只要next.next不是尾巴,那么保持不变。如果next是尾巴,就把自己的设置为尾巴,并且改变next,使之成为普通地图块。(refresh方法)
* 控制方向:
使用键盘事件,目前仅设置了wasd四个
* 窗口设计:
view extends JPanel,控制显示,然后在Lattice里调用Graphics.draw(...)实现对每个格子的显示
下面是核心的map部分代码(包括自动移动,检测食物,增加长度等等)
import codes.myGame.snake.cell.Lattice; import java.util.Random; public class Smap { private boolean getFood = false;//如果得到食物,该指针设为true,并且在随后的autoChange里增加蛇的长度 private boolean gameOver = false; private boolean directionChange = false;//这里标志的作用是保证在一次运动期间只会进行一次转向,使游戏更流畅 private int MAP_SIZE; private Lattice[][] map; private int directionX = 0;//下一次头在当前位置的哪个方向上 private int directionY = 1;//下一次头在当前位置的哪个方向上 private int[] head = new int[2];//记录当前头的位置 private int[] food = new int[2];//记录当前食物的位置 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]);//初始化设置一个头结点,以及他的尾节点 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; } //根据键盘事件,改变头的下一次移动方向,注意 该移动方向是仅针对头部的 //setDirection和setHead两个方法需要互斥进行,这里单线程,用synchronized即可 //(否则,如果当前头部在边界位置,连续变幻方向可能导致在setHead里发生溢出) 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表示不改变当前指向 } //设置尾巴由于没法像头部那样直接设置,这里只能采用链表遍历的方式获取尾巴 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);//先把当前的食物取消掉 boolean flag = false;//设置下一个食物 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()方法实现单个单元格的绘制,另一块设置view类继承自JPanel。负责绘制图画显示
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; //传入参数为null时,不改变当前的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; // 怎么设置下一个头呢?(考虑把DirectionX,Y放到Smap里,而不是这里) }else if(isBody){ if(next.isRear){ next.isRear = false; isRear = true; isBody = false; } } } // 在这里设置细胞可见 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; //填充的像素数量 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);//对应的格子的显示 } } } } @Override public Dimension getPreferredSize() {//创建该div大小 return new Dimension(thisMap.getMAP_SIZE()*GRID_SIZE+1, thisMap.getMAP_SIZE()*GRID_SIZE+1); } }
更多有趣的经典小游戏实现专题,分享给大家:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
相关文章
SpringBoot+Redis执行lua脚本的5种方式总结
Lua是一种快速、轻量级的脚本语言,广泛应用于各种领域,包括数据库,Redis作为一个内嵌Lua解释器的NoSQL数据库,允许通过Lua脚本在服务器端执行一些复杂的操作,本文给大家介绍了使用SpringBoot Redis执行lua脚本的五种方式,需要的朋友可以参考下2023-11-11
最新评论