贪吃蛇

思路

  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. DirectX11 With Windows SDK--31 阴影映射

    前言 阴影既暗示着光源相对于观察者的位置关系,也从侧面传达了场景中各物体之间的相对位置.本章将起底最基础的阴影映射算法,而像复杂如级联阴影映射这样的技术,也是在阴影映射的基础上发展而来的. 学习目标: ...

  2. 开发者大赛 | aelf轻型DApp开发训练大赛结果公布!

    6月9日,由aelf基金会发起的轻型DApp开发训练大赛圆满收官.本次训练赛基于aelf公开测试网展开,主要针对轻型DApp,旨在激励更多的开发者参与到aelf生态中来. 活动于4月21日上线后,ae ...

  3. 实现saas多租户方案比较

    看到一篇比较多租户数据隔离方案的文章,总结挺不错.其实大部分内容在我前几年写的文章都有. 文章翻译自: https://blog.arkency.com/comparison-of-approache ...

  4. jQuery实现购物车商品数量及总价的计算

    记录一下项目中遇到的计算购物车商品数量和总价的jQuery代码,重点在于选择器以及.text()命令的使用. 先上效果图,点击加减,商品数量以及总价会发生相应变化. html代码: <div c ...

  5. 恕我直言你可能真的不会java第3篇:Stream的Filter与谓词逻辑

    一.基础代码准备 建立一个实体类,该实体类有五个属性.下面的代码使用了lombok的注解Data.AllArgsConstructor,这样我们就不用写get.set方法和全参构造函数了.lombok ...

  6. MFC exe文件生成的图标更改方法

    MFC exe文件生成的图标更改方法 https://blog.csdn.net/txwtech/article/details/92980545

  7. 7.kubernetes集群版本升级

    1.查看原集群的Node节点的版本号 [root@hdss7-22 opt]# kubectl get node -o wide 2.将要升级的kubernetes版本上传到node节点上并解压(v1 ...

  8. Linux下安装 Java

    一.在线下载 Java JDK 环境 (1)搜索 yum 库有什么 JDK 版本 [root@localhost ~]# yum search java | grep jdk ldapjdk-java ...

  9. TCP实战一(三握四挥、流量控制)

    上一篇博文已经介绍了tcpdump的一些基本操作与命令,今天这篇博文将带你解密如何利用wireshark对tcpdump抓到的数据包进行可视化分析! 参考文献:https://zhuanlan.zhi ...

  10. Jenkins入门教程之linux下安装配置jenkins(一)

    https://blog.csdn.net/zjh_746140129/article/details/80835866