贪吃蛇

思路

  1. 首先构思游戏布局,计算合理的坐标系。

  2. 绘制静态数据(广告、初始小蛇、提示信息、棋盘)

  3. 添加键盘监听事件,改变游戏状态以及小蛇运动方向

  4. 添加定时器,让小蛇在一段时间内移动一定的距离

  5. 随机产生食物,并监听食物状态是否被吃

  6. 处理游戏结束事件

  7. 扩展相关游戏机制(积分、等级)

    • 定义数据
    • 绘制图像
    • 事件监听

注意事项

  • 导入文件资源时,通过类的相对路径获取

    • URL headURL = Data.class.getResource("header.png"); // 这是放在源代码同一个包下的文件

    • URL headURL = Data.class.getResource("/header.png"); // 这是放在项目根目录下的文件

  • 键盘监听时,需要自动获取焦点。

    • this.setFocusable(true); // 获取焦点

      this.addKeyListener(new MyKeyListener());
  • 在修改数据之后,需要repaint重绘图形

  • 小蛇运动时,需要注意边界问题

  • 食物随机产生的坐标也要限制在游戏区域内

  • 添加游戏的可玩性

具体实现

Data.java 存放所有图像数据

package snake;

import javax.swing.*;
import java.net.URL; /**
* 数据中心
*/
public class Data { // 相对路径
// 绝对路径 / 当前项目-->"GUI编程目录" public static URL headURL = Data.class.getResource("header.png");
public static URL upURL = Data.class.getResource("up.png");
public static URL downURL = Data.class.getResource("down.png");
public static URL leftURL = Data.class.getResource("left.png");
public static URL rightURL = Data.class.getResource("right.png");
public static URL bodyURL = Data.class.getResource("body.png");
public static URL foodURL = Data.class.getResource("food.png");
public static URL foodURL2 = Data.class.getResource("food2.png"); public static ImageIcon header = new ImageIcon(headURL);
public static ImageIcon up = new ImageIcon(upURL);
public static ImageIcon down = new ImageIcon(downURL);
public static ImageIcon left = new ImageIcon(leftURL);
public static ImageIcon right = new ImageIcon(rightURL);
public static ImageIcon body = new ImageIcon(bodyURL);
public static ImageIcon food = new ImageIcon(foodURL);
public static ImageIcon food2 = new ImageIcon(foodURL2);
}

StartGame.java 游戏启动类

package snake;

import javax.swing.*;
import java.awt.*; public class StartGame {
public static void main(String[] args) {
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int screenWidth = (int) screenSize.getWidth();
int screenHeight = (int) screenSize.getHeight();
System.out.println("屏幕宽度:" + screenWidth + ",屏幕高度:" + screenHeight); // 本游戏固定窗体大小(900,720)不可变
int x = (screenWidth - 900) / 2;
int y = (screenHeight - 720) / 2;
System.out.println("相对坐标x:" + x + ",y:" + y); JFrame jFrame = new JFrame("贪吃蛇");
jFrame.setBounds(x, y, 900, 720);
jFrame.setResizable(false);
jFrame.add(new GamePanel());
jFrame.setVisible(true);
jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); }
}

GamePanel.java 处理游戏逻辑

package snake;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.Random; public class GamePanel extends JPanel {
// 蛇的数据结构
int length;
int[] snakeX = new int[600];
int[] snakeY = new int[500];
char direction; // 食物的坐标
int foodx;
int foody;
Random random = new Random(); // 计数器
int count;
int score;
int worth; boolean isStart;
boolean isFail; // 定时器,每隔100毫秒执行一次参数的事件。
int interval = 100;
Timer timer = new Timer(interval, new MyActionLinstener()); public void init() {
// 默认游戏未开始
this.isStart = false;
this.isFail = false; // 初始化速度
interval = 200;
timer.setDelay(interval); // 初始化小蛇数据
this.length = 3;
this.score = 0;
this.snakeX[0] = 100;
this.snakeY[0] = 100;
this.snakeX[1] = 75;
this.snakeY[1] = 100;
this.snakeX[2] = 50;
this.snakeY[2] = 100;
this.direction = 'R'; // 随机产生事物的坐标
this.foodx = 25 + 25 * random.nextInt(34);
this.foody = 75 + 25 * random.nextInt(24);
this.count = 1; // 统计食物数量 // 获得焦点和键盘监听事件
this.setFocusable(true);
// 添加键盘监听事件
this.addKeyListener(new MyKeyListener());
// 开启定时器
timer.start();
} public GamePanel() {
init();
} // 自动绘制面板
// paintComponent()是swing的一个方法,相当于图形版的main(),是会自执行的。
@Override
protected void paintComponent(Graphics g) {
//清屏
super.paintComponent(g);
this.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, 30);
g.drawString("分数:" + score, 750, 56); // 绘制食物
if (count % 5 != 0) {
Data.food.paintIcon(this, g, foodx, foody);
worth = 1;
} else {
Data.food2.paintIcon(this, g, foodx, foody);
worth = 3;
} // 游戏状态 默认为暂停 需要提示信息
if (!isStart) {
g.setColor(Color.white);
g.setFont(new Font("微软雅黑", Font.BOLD, 40));
g.drawString("按下空格开始游戏", 300, 300);
} // 画小蛇 初始化向右 长度为3
// 选择头部的方向
ImageIcon head = Data.right;
switch (this.direction) {
case 'U':
head = Data.up;
break;
case 'D':
head = Data.down;
break;
case 'L':
head = Data.left;
break;
case 'R':
head = Data.right;
break;
default:
break;
} head.paintIcon(this, g, snakeX[0], snakeY[0]);
for (int i = 1; i < this.length; i++) {
Data.body.paintIcon(this, g, snakeX[i], snakeY[i]);
} // 结束状态
if (isFail) {
g.setColor(Color.red);
g.setFont(new Font("微软雅黑", Font.BOLD, 100));
g.drawString("Game Over", 150, 200);
g.drawString("按空格重新开始", 100, 400);
} } /**
* 内部类监听键盘事件
*/
class MyKeyListener extends KeyAdapter {
// 只需要监听键盘按下
@Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
// System.out.println(keyCode);
if (keyCode == KeyEvent.VK_SPACE) {
if (isFail) {
// 重新开始
init();
isStart = true;
} else {
isStart = !isStart;
} repaint();
} // 内层增加一个判断,180度转向 不生效
switch (keyCode) {
case KeyEvent.VK_UP:
if (direction != 'D') {
direction = 'U';
}
break;
case KeyEvent.VK_DOWN:
if (direction != 'U') {
direction = 'D';
}
break;
case KeyEvent.VK_LEFT:
if (direction != 'R') {
direction = 'L';
}
break;
case KeyEvent.VK_RIGHT: if (direction != 'L') {
direction = 'R';
}
break;
default:
break;
} // 调整速度 小键盘的 + -
if (keyCode == 107) {
interval = interval > 10 ? interval - 10 : 10;
timer.setDelay(interval);
System.out.println("加速" + interval);
} else if (keyCode == 109) {
interval += 20;
timer.setDelay(interval);
System.out.println("减速" + interval);
}
}
} class MyActionLinstener implements ActionListener {
// 事件监听类
@Override
public void actionPerformed(ActionEvent e) {
// 通过事件刷新界面
// 如果游戏开始且未失败,则刷新界面
if (isStart && !isFail) { // 吃食物 蛇头与食物重合
if (snakeX[0] == foodx && snakeY[0] == foody) {
length++; // 吃了边长
foodx = 25 + 25 * random.nextInt(34);
foody = 75 + 25 * random.nextInt(24);
count++; // 统计食物数量
score += worth; // 这里可以设置积分到一定值,增加移动速度
if (score < 5) {
interval = 200;
timer.setDelay(interval);
System.out.println("一级速度");
} else if (score < 15) {
interval = 150;
timer.setDelay(interval);
System.out.println("二级速度");
} else if (score < 25) {
interval = 100;
timer.setDelay(interval);
System.out.println("三级速度");
} else if (score < 50) {
interval = 50;
timer.setDelay(interval);
System.out.println("四级速度");
} } for (int i = length - 1; i > 0; i--) {
// 从最后一节开始继承前一节的位置
snakeX[i] = snakeX[i - 1];
snakeY[i] = snakeY[i - 1];
}
// 头部找新的路,利用三元运算符判断边界重置
switch (direction) {
case 'U':
snakeY[0] = snakeY[0] <= 75 ? 650 : snakeY[0] - 25;
break;
case 'D':
snakeY[0] = snakeY[0] >= 650 ? 75 : snakeY[0] + 25;
break;
case 'L':
snakeX[0] = snakeX[0] <= 25 ? 850 : snakeX[0] - 25;
break;
case 'R':
snakeX[0] = snakeX[0] % 850 + 25;
break;
default:
break;
} // 失败判定
for (int i = 1; i < length; i++) {
if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) {
isFail = true;
}
}
repaint();
}
}
} }

游戏效果

该版本已知bug

小蛇不能反向运动,玩家操作过快导致的意外死亡。

比如小蛇向↑运动过程中,玩家按 ↓ 是无效的,但如果按 ← 的瞬间按 ↓

由于定时器的刷新速度没有跟上玩家的操作,游戏帧尚未刷新就已经有了下一次操作。

误判为小蛇回头撞了自己,游戏结束。

第二个问题是

空格本应该具有暂停/继续/重玩的功能。

游戏局数为偶数时,空格键暂停功能失效。

打包游戏

首先通过IDEA生成Jar包,再通过exe4j将jar打包为exe

具体操作跳转至Java桌面应用程序打包。

Java贪吃蛇小游戏的更多相关文章

  1. java贪吃蛇小游戏详解

    https://blog.csdn.net/u011622021/article/details/81162083

  2. Java GUI学习,贪吃蛇小游戏

    JAVA GUI练习 贪吃蛇小游戏 前几天虽然生病了,但还是跟着狂神学习了GUI的方面,跟着练习了贪吃蛇的小项目,这里有狂神写的源码点我下载,还有我跟着敲的点我下载,嘿嘿,也就注释了下重要的地方,这方 ...

  3. 用GUI实现java版贪吃蛇小游戏

    项目结构 新建一个JFrame窗口,作为程序入口 public class GameStart{ public static void main(String[] args) { JFrame jFr ...

  4. C++ 简单的控制台贪吃蛇小游戏

    由于比较懒,所以不怎么写,觉得这样不应该.我应该对自己学的做出整理,不管是高端低端,写出来是自己的. // 贪吃蛇.cpp : 定义控制台应用程序的入口点. // #include "std ...

  5. 贪吃蛇小游戏-----C语言实现

    1.分析 众所周知,贪吃蛇游戏是一款经典的益智游戏,有PC和手机等多平台版本,既简单又耐玩.该游戏通过控制蛇头方向吃食物,从而使得蛇变得越来越长,蛇不能撞墙,也不能装到自己,否则游戏结束.玩过贪吃蛇的 ...

  6. JS高级---案例:贪吃蛇小游戏

    案例:贪吃蛇小游戏 可以玩的小游戏,略复杂,过了2遍,先pass吧 先创建构造函数,再给原型添加方法.分别创建食物,小蛇和游戏对象. 食物,小蛇的横纵坐标,设置最大最小值,运动起来的函数,按上下左右键 ...

  7. Java 用java GUI写一个贪吃蛇小游戏

    目录 主要用到 swing 包下的一些类 上代码 游戏启动类 游戏数据类 游戏面板类 代码地址 主要用到 swing 包下的一些类 JFrame 窗口类 JPanel 面板类 KeyListener ...

  8. html5面向对象做一个贪吃蛇小游戏

    canvas加面向对象方式的贪吃蛇 2016-08-25 这个小游戏可以增加对面向对象的理解,可以加强js逻辑能力,总之认真自己敲一两遍收获还是不少啊!!适合刚学canvas的同学练习!! 废话不多说 ...

  9. 用python+pygame写贪吃蛇小游戏

    因为python语法简单好上手,前两天在想能不能用python写个小游戏出来,就上网搜了一下发现了pygame这个写2D游戏的库.了解了两天再参考了一些资料就开始写贪吃蛇这个小游戏. 毕竟最开始的练手 ...

随机推荐

  1. java实现第六届蓝桥杯胡同门牌号

    胡同门牌号 小明家住在一条胡同里.胡同里的门牌号都是连续的正整数,由于历史原因,最小的号码并不是从1开始排的. 有一天小明突然发现了有趣的事情: 如果除去小明家不算,胡同里的其它门牌号加起来,刚好是1 ...

  2. cuda 9.0

    https://docs.nvidia.com/cuda/archive/9.0/index.html cuda9.0工具包

  3. sort运用

    #include<iostream> #include<algorithm> #include<cstdio> using namespace std; struc ...

  4. vs2019离线下载安装包

    官方的离线安装说明-->点击打开 1.下载 vs2019引导程序 ,选择你所需的版本下载,我选择了企业版 vs_enterprise__184447765.1558180718.exe 2.设置 ...

  5. Volley框架学习资料汇总

    Android Volley完全解析(一),初识Volley的基本用法(http://blog.csdn.net/guolin_blog/article/details/17482095/)

  6. css实现朋友圈照片排列布局

    纯css实现朋友圈不同数量图片不同布局 首先可以打开朋友圈观察不同图片数量的几种布局,也可参考下图示例: 可以发现 除1张图片,4张图片特殊外,其他数量图片均使用一行三列的方式排列: 假设有如下HTM ...

  7. Uber基于Apache Hudi构建PB级数据湖实践

    1. 引言 从确保准确预计到达时间到预测最佳交通路线,在Uber平台上提供安全.无缝的运输和交付体验需要可靠.高性能的大规模数据存储和分析.2016年,Uber开发了增量处理框架Apache Hudi ...

  8. TensorFlow从0到1之TensorFlow优化器(13)

    高中数学学过,函数在一阶导数为零的地方达到其最大值和最小值.梯度下降算法基于相同的原理,即调整系数(权重和偏置)使损失函数的梯度下降. 在回归中,使用梯度下降来优化损失函数并获得系数.本节将介绍如何使 ...

  9. 记PHP下载大文件失败的一次坑

    说明 php提供文件的储存和下载,nginx作为web服务器,fpm做解析. 现象 当下载一个5M大小的图片时,总提示下载失败,或下载下来的文件不完整,仅显示部分图像(每次下载不一样) php下载相关 ...

  10. WeChair项目Beta冲刺(9/10)

    团队项目进行情况 1.昨日进展    Beta冲刺第九天 昨日进展: 项目开始扫尾 2.今日安排 前端:前端工作已经完成 后端:扫码占座后端测试,实现对超时预约座位下座的功能 数据库:和后端组织协商扫 ...