文章目录
前言
在这里我会用JAVA写一个贪吃蛇的小游戏,这个的制作过程及创意都是来自于“秦疆老师,他的java课程在www.kuangstudy.com"有兴趣的同学可以去看看,也可以在哔哩哔哩上找到他的视频,关注“遇见狂神说”即可。
现在,我们开始了!!
一、前期的准备工作
-
设置窗口及图形的大小
- 最外层窗口设置为:900X720
- 内部游戏运行的面板设置为:850X600
- 蛇的头部、身体、食物设置为 25X25 大小的png图片。(这个可以用画图工具自己编辑一个的。)
- 画布上方自己弄一个图片,在这个图片上后期会加载吃食物的个数及获得的分数,这个图片的大小为600*40
-
以下是我自己弄得图片你们可以参考一下
二、设置包及代码的架构及具体代码
1.在项目中建立一个snake的包,文件结构如下:![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/0e03ff9e0ed85b730130009e68d0ee59.png)
这里说明一下包内的东西哈
- 首先static文件夹中存放的是图片,body.png是蛇的身体,down.png,up.png,right.png,left.png这几个是蛇头的四个方向所对应的图片,food.png是吃的食物的图片,header.png是游戏区域上方的图片,后期可以在这个图片上添加文字及得分,时间等信息。
- Data文件是一个数据类,主要是构造了一个读取图片文件的功能,后期可以通过这个类直接获取图片信息,当然不用这个也可以,直接在代码中通过设置变量获取都可以,不过这样放的好处是方便后期维护,也算是一个写代码的好习惯吧
- GamePanel 中存放的是具体的运行代码
package snake;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;
/**
* @author marker_sun
* @version 1.0
* @since 1.8
*/
public class GamePanel extends JPanel implements KeyListener, ActionListener {
// 定义常量
/** 蛇的方向 {@value}*/
private final char RIGHT = 'r';
private final char LEFT = 'l';
private final char UP = 'u';
private final char DOWN = 'd';
// 定义蛇的数据结构
/** 这是蛇的长度{@value}*/
int length;
/** 蛇的X坐标系{@value}*/
int[] snakeX = new int[600];
/** 蛇的Y坐标系{@value}*/
int[] snakeY = new int[600];
char fx;
/** 食物的坐标系{@value}*/
int foodX;
int foodY;
/**
* 计分
*/
short score;
/**
* 获取一个随机数
*/
Random random = new Random();
/**
*判断游戏状态: 1:开始 2:暂停 3:死亡 0:初始化
*/
byte isStart = 0;
/**
* 定时器 // 每100毫秒刷新一次
*/
Timer timer = new Timer(100, this);
/**
* 一个构造器
*/
public GamePanel() {
init();
// 获得焦点和键盘事件
// 获得焦点事件
this.setFocusable(true);
// 获得键盘监听事件
this.addKeyListener(this);
// 游戏开始定时器启动
timer.start();
}
/**
* 初始化值
*/
public void init() {
length = 3;
// 蛇脑袋的初始位置
snakeX[0] = 100;
snakeY[0] = 100;
// 蛇第一节身体的初始位置
snakeX[1] = 75;
snakeY[1] = 100;
// 蛇第二节身体的初始位置
snakeX[2] = 50;
snakeY[2] = 100;
// 食物随机分布在界面上
foodX = 25 + 25 * random.nextInt(34);
foodY = 75 + 25 * random.nextInt(24);
isStart = 0;
fx = RIGHT;
score = 0;
}
/**
* 设置一个画笔,接下来的所有内容都是用这个画笔来完成绘画
*/
@Override
protected void paintComponent(Graphics g) {
// 这个代码的作用是清屏,如果没有这个会导致画面闪烁
super.paintComponent(g);
this.setBackground(Color.WHITE);
// 将静态的图片绘制到面板上
Data.header.paintIcon(this, g, 25, 10);
// 默认的游戏界面
g.fillRect(25, 75, 850, 600);
//画食物
Data.food.paintIcon(this, g, foodX, foodY);
//画分数
g.setColor(Color.black);
g.setFont(new Font("微软雅黑", Font.BOLD, 15));
g.drawString("长度为:"+length,650,35);
g.drawString("分数为:"+ score,650,55);
// 把初始的蛇画到面板上
// 画蛇的头
switch (fx) {
case RIGHT:
Data.right.paintIcon(this, g, snakeX[0], snakeY[0]);
break;
case LEFT:
Data.left.paintIcon(this, g, snakeX[0], snakeY[0]);
break;
case UP:
Data.up.paintIcon(this, g, snakeX[0], snakeY[0]);
break;
case DOWN:
Data.down.paintIcon(this, g, snakeX[0], snakeY[0]);
break;
default:
}
// 蛇的身体
for (int i = 1; i < length; i++) {
Data.body.paintIcon(this, g, snakeX[i], snakeY[i]);
}
// 游戏状态
if (isStart==0 || isStart == 2) {
g.setColor(Color.white);
g.setFont(new Font("微软雅黑", Font.BOLD, 40));
g.drawString("按下空格,开始游戏!", 300, 300);
}
// 死亡状态
if (isStart == 3){
g.setColor(Color.red);
g.setFont(new Font("微软雅黑", Font.BOLD, 35));
g.drawString("您已失败,请按Enter键重新开始!!",150,300);
}
}
/**
* 键盘监听事件
*/
@Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_SPACE) {
switch (isStart){
case 0:isStart = 1;
break;
case 1: isStart = 2;
break;
case 2: isStart = 1;
default:
}
// 重新绘制
repaint();
}
if (keyCode == KeyEvent.VK_ENTER) {
this.init();
repaint();
}
// 小蛇移动
switch (keyCode) {
case KeyEvent.VK_UP:
if (fx != DOWN) {
fx = UP;
}
break;
case KeyEvent.VK_DOWN:
if (fx != UP) {
fx = DOWN;
}
break;
case KeyEvent.VK_LEFT:
if (fx != RIGHT) {
fx = LEFT;
}
break;
case KeyEvent.VK_RIGHT:
if (fx != LEFT) {
fx = RIGHT;
}
break;
default:
}
}
/**
* 事件监听 -- 需要通过固定事件来刷新, 1秒10次
*/
@Override
public void actionPerformed(ActionEvent e) {
int boundaryLeft = 25;
int boundaryRight = 850;
int boundaryUp = 75;
int boundaryDown = 650;
if (isStart==1) {
// 吃食物
if (snakeX[0] == foodX && snakeY[0] == foodY) {
//长度加1
length++;
// 分数加10分
score += 10;
// 再次随机食物
foodX = 25 + 25 * random.nextInt(34);
foodY = 75 + 25 * random.nextInt(24);
}
// 这里注意,要是头写到身体上面,身体就会覆盖头
// 移动身体
for (int i = length - 1; i > 0; i--) {
snakeX[i] = snakeX[i - 1];
snakeY[i] = snakeY[i - 1];
}
//头转向
switch (fx) {
case RIGHT:
snakeX[0] += 25;
if (snakeX[0] > boundaryRight) {
snakeX[0] = boundaryLeft;
}// 边界判断
break;
case LEFT:
snakeX[0] -= 25;
if (snakeX[0] < boundaryLeft) {
snakeX[0] = boundaryRight;
}// 边界判断
break;
case UP:
snakeY[0] -= 25;
if (snakeY[0] < boundaryUp) {
snakeY[0] = boundaryDown;
}// 边界判断
break;
case DOWN:
snakeY[0] += 25;
if (snakeY[0] > boundaryDown) {
snakeY[0] = boundaryUp;
}// 边界判断
break;
default:
}
repaint(); //重画画面
}
// 死亡判断
if(isStart != 3) {
for (int i = length - 1; i > 0; i--) {
// 死亡判定
if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) {
isStart = 3;
}
}
}
timer.start(); //定时器开启
}
@Override
public void keyReleased(KeyEvent e) {
}
@Override
public void keyTyped(KeyEvent e) {
}
}
- GameStart这个是游戏的启动文件,具体代码如下
package snake;
import javax.swing.*;
/**
* @author marker_sun
* @version 1.0
* @since 1.8
*/
public class GameStart {
public static void main(String[] args) {
JFrame frame = new JFrame();
// 将面板放置到主窗体中
frame.add(new GamePanel());
frame.setTitle("贪吃蛇");
// 设置外窗体的大小及初始位置
frame.setBounds(20,20,900,750);
// 设置窗口大小不可变
frame.setResizable(false);
// 点击窗口的关闭按钮可以关闭窗口
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
// 使窗口调整为可见状态
frame.setVisible(true);
}
}
2、项目运行起来的样子如下:
- 开始的样子
- 结束的样子
总结
至此,这个小项目就完成了。
说明:
- GamePanel中的代码我用阿里巴巴的规范进行了调整,可能不太好,大家可以参考一下,我觉得大家平时写代码的时候就用这个规范去书写,对我们自身的帮助还是很大的。
- GamePanel中狂神对转向用的是 if 语法,我改成了swith…case…我觉得在这种地方,用swith会更加好一点,如果我的这个改动不好请大家告诉我,我好改正。
- 最后,谢谢大家观看我写的这个项目,初次写,有不对及不好的地方欢迎大家批评指正。感谢大家