Java实现贪吃蛇游戏
作者:菜鸡上路 发布时间:2023-11-20 18:40:29
最近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);
}
}
更多有趣的经典小游戏实现专题,分享给大家:
C++经典小游戏汇总
python经典小游戏汇总
python俄罗斯方块游戏集合
JavaScript经典游戏 玩不停
javascript经典小游戏汇总
来源:https://blog.csdn.net/qq_38908061/article/details/81952660


猜你喜欢
- 第一部分:Java数据结构要理解Java数据结构,必须能清楚何为数据结构?数据结构:Data_Structure,它是储存数据的一种结构体,
- AuthenticationProvider解析首先进入到AuthenticationProvider源码中可以看到它只是个简单的接口里面也
- 本文实例讲述了C#开发的人脸左右相似度计算软件。分享给大家供大家参考。具体分析如下:模仿湖南卫视快乐大本营中所使用的一款人脸左右对称相似度计
- MyBatis动态sql动态sql处理简单的多参数查询常用标签标签说明if条件判断,与java中的if语句类似where为sql语句动态添加
- 前言公司的邮件系统用的是 * 的 Lotus notes, 你敢信?最近要实现一个功能,邮件提醒功能,就是通过自动发送提醒邮件 前
- 这里以电视遥控器的一个例子来引出桥接模式解决的问题,首先,我们每个牌子的电视机都有一个遥控器,此时我们能想到的一个设计是——把遥控器做为一个
- 这篇文章主要介绍了Maven打包jar生成javadoc文件和source文件代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作
- 实现说明这里的核心在于如何在大并发的情况下保证数据库能扛得住压力,因为大并发的瓶颈在于数据库。如果用户的请求直接从前端传到数据库,显然,数据
- 最近学了很多的知识,脑容量小,记不清,还是得做做练习!今天就做了一个扑克牌的练习首先呢..这个逻辑一定要非常清楚,我们要想做出一副扑克牌,必
- 图像切换器(ImageSwitcher),用于实现类似于Windows操作系统的“Windows照片查看器”中的上一张、下一张切换图片的功能
- 今天安装了jdk1.8、tomcat8、和maven3.5.2,弄好后在myeclipse新建了一个maven项目,项目默认是jdk1.5,
- 所谓泛型:就是允许在定义类、接口指定类型形参,这个类型形参在将在声明变量、创建对象时确定(即传入实际的类型参数,也可称为类型实参)泛型类或接
- 我们知道,(1)如果是整百的年份,能被400整除的,是闰年;(2)如果不是整百的年份,能被4整除的,也是闰年。每400年,有97个闰年。鉴于
- 前言当我们编写 C# 代码时,经常需要处理大量的数据集合。在传统的方式中,我们往往需要先将整个数据集合加载到内存中,然后再进行操作。但是如果
- mapper.xml文件<?xml version="1.0" encoding="UTF-8"
- IOC创建对象的方式一、 使用无参构造创建对象(默认方式)创建实体类注意:属性必须要有set方法,来完成注入public class Use
- strftime函数主要用于时间格式化,它的函数原型如下:size_t __cdecl strftime(char * __restrict
- 最近在学习springboot,session这个点一直困扰了我好久,今天把这些天踩的坑分享出来吧,希望能帮助更多的人。一、pom.xml配
- 一,定义变量C# 表达式树中,定义一个变量,使用 ParameterExpression。创建变量结点的方法有两种,Express
- 本文实例为大家分享了java实现订餐系统的具体代码,供大家参考,具体内容如下import java.util.Scanner;public