Java学习之路--GUI编程--贪吃蛇小游戏

发布时间 2023-09-20 18:40:29作者: 寂灭无言
贪吃蛇小游戏界面实现四步走
1.定义数据
2.面板里将数据画上去
3.监听事件
键盘
事件

package com.gui.snake;

import javax.swing.*;
import java.net.URL;

//2023.3.28 GUI编程--贪吃蛇小游戏的实现--数据中心(只有一堆图片的数据) --贪吃蛇各个部分的导入--图片文件夹有两种导入方式,
// 一种是直接拖那些图片到需要放的位置,一种是先建一个statics包(这个包专门用来存放静态文件),再将图片拖了放这个包里
public class Data {

//相对路径 txt.jpg
//绝对路径 /../.. /--相当于当前的目录
public static URL headerURL = Data.class.getResource("statics/header.png");//获取贪吃蛇头部图片的资源
public static ImageIcon header = new ImageIcon(headerURL);//将获得的图片放入图片图标中

public static URL bodyURL = Data.class.getResource("statics/body.png");//获取贪吃蛇肢体图片的资源
public static URL rightURL = Data.class.getResource("statics/right.png");//获取贪吃蛇右边头图片的资源
public static URL leftURL = Data.class.getResource("statics/left.png");//获取贪吃蛇左边头图片的资源
public static URL upURL = Data.class.getResource("statics/up.png");//获取贪吃蛇上方头图片的资源
public static URL downURL = Data.class.getResource("statics/down.png");//获取贪吃蛇下方头图片的资源
public static URL foodURL = Data.class.getResource("statics/food.png");//获取贪吃蛇食物图片的资源

public static ImageIcon body = new ImageIcon(bodyURL);//将获得的图片放入图片图标中
public static ImageIcon right = new ImageIcon(rightURL);//将获得的图片放入图片图标中
public static ImageIcon left = new ImageIcon(leftURL);//将获得的图片放入图片图标中
public static ImageIcon up = new ImageIcon(upURL);//将获得的图片放入图片图标中
public static ImageIcon down = new ImageIcon(downURL);//将获得的图片放入图片图标中
public static ImageIcon food = new ImageIcon(foodURL);//将获得的图片放入图片图标中
}

//
package com.gui.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;

//2023.3.27/3.28/3.29/3.30/3.31/4.1/4.2 GUI编程--贪吃蛇小游戏练习--游戏面板的实现
public class GamePanel extends JPanel implements KeyListener, ActionListener {//GamePanel类继承JPanel类,实现KeyListener接口和ActionListener接口
//定义蛇的数据结构
int length;//蛇的长度
int[] snakeX = new int[600];//蛇的X坐标 25*25
int[] snakeY = new int[500];//蛇的Y坐标 25*25
String fx;//定义一个变量fx实现蛇头的方向改变

//食物的坐标
int foodX;
int foodY;
Random random = new Random();//随机数Random,用来改变随机生成食物
int score;//定义一个得分score,用来实现贪吃蛇的积分面板用
int failSum;//定义一个变量记录失败次数 失败次数判断没写好,所以注释掉

//游戏当前的状态:开始,停止
boolean isStart = false;//默认游戏是处于没有开始状态

boolean isFail = false;//定义变量来判断游戏状态失败,Fail(失败),默认游戏是不失败状态

//定时器 以毫秒(ms)为单位 1000ms = 1s
Timer timer = new Timer(100, this);//是以毫秒为单位 delay:100 100毫秒刷新(执行)一次

//构造器 2023.4.1
public GamePanel() {
init();
//获得焦点和键盘事件
this.setFocusable(true);//获得焦点事件
this.addKeyListener(this);//获得键盘的监听事件 GamePanel实现了KeyListener接口,所以这里()写this就行
timer.start();//游戏一开始定时器就启动
}

//初始化方法
public void init() {
length = 3;//蛇的初始长度3节
snakeX[0] = 100;
snakeY[0] = 100;//蛇脑袋的坐标
snakeX[1] = 75;
snakeY[1] = 100;//蛇第一个身体的坐标
snakeX[2] = 50;
snakeY[2] = 100;//蛇第二个身体的坐标
fx = "R";//小蛇初始方向向右

//把食物随机分布在界面上!
foodX = 25 + 25 * random.nextInt(34);//随机生成食物X的坐标 34 是
foodY = 75 + 25 * random.nextInt(24);//随机生成食物Y的坐标

//积分初始为0
score = 0;

}

//绘制面板,我们游戏中的所有东西都使用这个面板来画
@Override
protected void paintComponent(Graphics g) {//画组件(paintComponent)
super.paintComponent(g);//清屏,不写这一句的话面板会不停的闪烁
//绘制静态的面板
this.setBackground(Color.WHITE);//设置背景颜色为白色
Data.header.paintIcon(this, g, 25, 11);//头部广告栏画上去
g.fillRect(25, 75, 850, 600);//默认的游戏界面 fillRect(画矩形)

//画积分
g.setColor(Color.black);//积分板颜色为黑色
g.setFont(new Font("微软雅黑", Font.BOLD, 20));//设置字体 "微软雅黑"--字体样式,Font.BOLD(字体加粗),size:40(字体大小)
g.drawString("小蛇长度:" + length, 550, 25);//画(记录)小蛇的长度的变化
g.drawString("游戏分数:" + score, 550, 45);//画(记录)小蛇吃食物的分数变化

//画食物 2023.4.2 小蛇吃食物实际是蛇小蛇头碰到食物它身体覆盖掉食物,然后小蛇的长度加1 ,这里如果先画小蛇的话食物和小蛇的身体就会出现重叠的情况
Data.food.paintIcon(this, g, foodX, foodY);//把食物画在界面上

//把小蛇画上去
//写条件语句if判断蛇头方向 2023.3.30
if (fx.equals("R")) {
Data.right.paintIcon(this, g, snakeX[0], snakeY[0]);//蛇头初始方向向右
} else if (fx.equals("L")) {
Data.left.paintIcon(this, g, snakeX[0], snakeY[0]);//蛇头初始方向向左
} else if (fx.equals("U")) {
Data.up.paintIcon(this, g, snakeX[0], snakeY[0]);//蛇头初始方向向上
} else if (fx.equals("D")) {
Data.down.paintIcon(this, g, snakeX[0], snakeY[0]);//蛇头初始方向向下
}

//写循环是为了让蛇实现动起来的效果
for (int i = 1; i <= length; i++) {
Data.body.paintIcon(this, g, snakeX[i], snakeY[i]);//蛇的身体的坐标,用循环不停变换X,Y的坐标,实现蛇动起来
}

//游戏状态 写if判断游戏处于未开始状态 2023.3.31
if (isStart == false) {
g.setColor(Color.white);//设置字体颜色
g.setFont(new Font("微软雅黑", Font.BOLD, 40));//设置字体 "微软雅黑"--字体样式,Font.BOLD(字体加粗),size:40(字体大小)
g.drawString("按下空格开始游戏", 300, 300);//画需要设置的字体内容和xy位置
}
// Data.body.paintIcon(this,g,snakeX[0],snakeY[0]);//蛇的第一个身体的坐标
// Data.body.paintIcon(this,g,snakeX[2],snakeY[2]);//蛇的第二个身体的坐标

if (isFail == true) {//如果游戏失败 2023.4.2
g.setColor(Color.red);//设置字体颜色,失败的字体颜色为红色
g.setFont(new Font("微软雅黑", Font.BOLD, 40));//设置字体 "微软雅黑"--字体样式,Font.BOLD(字体加粗),size:40(字体大小)
g.drawString("游戏失败,按下空格重新开始", 300, 300);//画需要设置的字体内容和xy位置
}
}

@Override //重写KeyListener接口的方法
public void keyTyped(KeyEvent e) {

}

//2023.3.31 键盘监听事件
@Override
public void keyPressed(KeyEvent e) {
int keycode = e.getKeyCode();//获得键盘按键是哪一个

if (keycode == KeyEvent.VK_SPACE) {//如果按下的键位是空格键
if (isFail) {//如果游戏失败,则按下按键重新开始
//重新开始
isFail = false;
init();//数据回到初始状态
} else {
isStart = !isStart;//取反,这里不写isStart = true 是因为这样写控制不了小蛇停下,取反写法,按下空格键开始,再按空格键停止

}
repaint();//重新画小蛇
}

//小蛇移动 --写这段控制小蛇上下左右的方向移动
if (keycode == KeyEvent.VK_UP) {//如果按下的键位置是上键
fx = "U";
} else if (keycode == KeyEvent.VK_DOWN) {//如果按下的键位是下键
fx = "D";
} else if (keycode == KeyEvent.VK_LEFT) {//如果按下的键位是左键
fx = "L";
} else if (keycode == KeyEvent.VK_RIGHT) {//如果按下的键位是右键
fx = "R";
}
}

@Override
public void keyReleased(KeyEvent e) {
}

//事件监听--需要通过固定事件来刷新 1s = 10次
//重写ActionListener接口的方法
@Override
public void actionPerformed(ActionEvent e) {
if (isStart && isFail == false) {//如果游戏是开始状态并且不处于失败状态,就让小蛇动起来

//吃食物
if (snakeX[0] == foodX && snakeY[0] == foodY) {//如果小蛇的x坐标等于食物的x坐标并且小蛇的y坐标等于食物的y坐标
//长度 + 1
length++;//这里的长度加1 是指小蛇每吃一个食物,长度就加1,身体节数
score = score + 10;//每吃一个食物分数加10
//再次随机食物
foodX = 25 + 25 * random.nextInt(34);//34是
foodY = 75 + 25 * random.nextInt(24);
}

//小蛇移动
for (int i = length - 1; i > 0; i--) {
snakeX[i] = snakeX[i - 1];//小蛇向前移动一节 小蛇x坐标的改变,
snakeY[i] = snakeY[i - 1];//小蛇y坐标的改变
}
//走向 小蛇上下左右的走向
if (fx.equals("R")) {
snakeX[0] = snakeX[0] + 25;
if (snakeX[0] > 850) {//边界判断 --写这一块是因为按下空格小蛇右移出右边窗口边界,小蛇就从这一行最左边重头再来
snakeX[0] = 25;
}
} else if (fx.equals("L")) {
snakeX[0] = snakeX[0] - 25;
if (snakeX[0] < 25) {//边界判断 --写这一块是因为按下空格小蛇左移出左边窗口边界,小蛇就从这一行最右边重头再来
snakeX[0] = 850;
}
} else if (fx.equals("U")) {
snakeY[0] = snakeY[0] - 25;
if (snakeY[0] < 75) {//边界判断
snakeY[0] = 650;
}
} else if (fx.equals("D")) {
snakeY[0] = snakeY[0] + 25;
if (snakeY[0] > 650) {//边界判断
snakeY[0] = 75;
}
}
}

//失败判断,小蛇撞到自己就算失败或者小蛇头撞到窗口边界就失败
for (int i = 1; i < length; i++) {
if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) {//同一时间,小蛇出现同一个部位或不同部位坐标相同时候(即小蛇撞到自己),就算失败
isFail = true;

// else if (snakeX[0] == 850 || snakeY[0] == 650){//如果小蛇的头撞到窗口宽(长)边的边界或者小蛇的头撞到高边的边界,就算失败
// isFail = true;
// }
}


repaint();//重画(页面)一次小蛇
}
// timer.start();//定时器开启!

}
}

//
package com.gui.snake;

import javax.swing.*;

//2023.3.27 GUI编程--贪吃蛇小游戏的实现--界面实现 Snake--蛇 这个类是游戏的主启动类
//游戏开始
public class StartGame {
public static void main(String[] args) {
JFrame frame = new JFrame();//创建窗体对象

frame.setBounds(10,10,900,720);//设置初始位置和窗体大小 这里设置的宽900,高720是计算得出的,不能乱来
frame.setResizable(false);//设置窗体大小不可变,就是窗体大小固定的意思 false--不可变 true-可变
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);//JFrame 默认关闭窗口的事件方法

//正常的游戏界面都应该放在面板上
frame.add(new GamePanel());//将面板添加到窗体中

frame.setVisible(true);//设置可见性
}
}