项目实战 - 贪吃蛇
图片资源提取地址
百度网盘
链接:https://pan.baidu.com/s/19gEhIuZrut12l5a8jcknFA?pwd=1111
提取码:1111
界面绘制
这里设计到帧的概念,即一秒钟里有多少张图片,一般电影为24帧,每一帧就是一张图片
涉及到键盘监听,以及计时器每隔一段时间进行页面刷新
先绘制静态页面
![[Pasted image 20220604102255.png]]
游戏整体界面
- 里面承载广告图片以及游戏界面
游戏启动类
代码
public class StartGame {
public static void main(String[] args) {
JFrame frame = new JFrame("贪吃蛇");
//游戏界面-单独在面板上面
frame.add(new GamePanel());
frame.setResizable(false);//窗口大小不可变
frame.setBounds(200,200,905,720);
frame.setVisible(true);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
}
游戏面板类
代码
//游戏的面板
public class GamePanel extends JPanel {
//绘制面板
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);//清屏
this.setBackground(Color.BLACK);
this.setBackground(Color.WHITE);
Data.header.paintIcon(this,g,25,11);
//画出黑色游戏界面
g.fillRect(25,75,850,600);
}
}
数据中心
代码
//数据中心
public class Data {
//相对路径
//绝对路径 /相当于当前项目
public static URL headerURL = Data.class.getResource("static/header.png");
public static ImageIcon header = new ImageIcon(headerURL);
public static URL upURL = Data.class.getResource("static/up.png");
public static URL downURL = Data.class.getResource("static/down.png");
public static URL leftURL = Data.class.getResource("static/left.png");
public static URL rightURL = Data.class.getResource("static/right.png");
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 URL bodyURL = Data.class.getResource("static/body.png");
public static ImageIcon body = new ImageIcon(bodyURL);
public static URL foodURL = Data.class.getResource("static/food.png");
public static ImageIcon food = new ImageIcon(foodURL);
}
运行时可能会报错ClassNotFound
- 首先检查Data类中的url是否写对
- 重启Idea,再次启动即可
最后的效果
截图
绘制静态小蛇
游戏启动类
代码
public class StartGame {
public static void main(String[] args) {
JFrame frame = new JFrame("贪吃蛇");
//游戏界面-单独在面板上面
frame.add(new GamePanel());
frame.setResizable(false);//窗口大小不可变
frame.setBounds(200,200,905,720);
frame.setVisible(true);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
}
游戏面板类
代码
//游戏的面板
public class GamePanel extends JPanel {
//定义蛇的数据结构
int length;//蛇的长度
//黑色游戏界面总共大小为850*600
//所以小蛇最多的节数为850*600/25*25 = 816
//所以只需要以下两个数组比816长即可
int[] snakeX = new int[850];//蛇的X坐标
int[] snakeY = new int[850];//蛇的Y坐标
String dir;//初始方向
//当前游戏状态:开始,停止
boolean isStart = false; //默认是不开始
//构造器
public GamePanel(){
init();
}
//初始化方法
public void init(){
length = 3;
snakeX[0] = 100;snakeY[0] = 100;//脑袋的坐标
snakeX[1] = 75;snakeY[1] = 100; //第一节身体坐标
snakeX[2] = 50;snakeY[2] = 100; //第二节身体坐标
dir = "R"; //初始方向向右
}
//绘制面板
@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);
//把小蛇画上去
//蛇头初始化向右
switch (dir) {
case "R":
Data.right.paintIcon(this, g, snakeX[0], snakeY[0]);
break; case "L":
Data.left.paintIcon(this, g, snakeX[0], snakeY[0]);
break; case "U":
Data.up.paintIcon(this, g, snakeX[0], snakeY[0]);
break; case "D":
Data.down.paintIcon(this, g, snakeX[0], snakeY[0]);
break; }
for (int i = 1; i < length; i++) {
Data.body.paintIcon(this,g,snakeX[i],snakeY[i]);
}
//游戏状态
if (!isStart)
{
g.setColor(Color.white);
g.setFont(new Font("微软雅黑",Font.BOLD,40));
g.drawString("按下空格开始游戏",300,300);
}
}
}
让小蛇动起来
实现按下空格游戏开始游戏暂停
游戏面板类
代码
//游戏的面板
public class GamePanel extends JPanel implements KeyListener {
//定义蛇的数据结构
int length;//蛇的长度
//黑色游戏界面总共大小为850*600
//所以小蛇最多的节数为850*600/25*25 = 816
//所以只需要以下两个数组比816长即可
int[] snakeX = new int[850];//蛇的X坐标
int[] snakeY = new int[850];//蛇的Y坐标
String dir;//初始方向
//当前游戏状态:开始,停止
boolean isStart = false; //默认是不开始
//构造器
public GamePanel(){
init();
//获得焦点事件
this.setFocusable(true);
//获得键盘事件
this.addKeyListener(this);
}
//初始化方法
public void init(){
length = 3;
snakeX[0] = 100;snakeY[0] = 100;//脑袋的坐标
snakeX[1] = 75;snakeY[1] = 100; //第一节身体坐标
snakeX[2] = 50;snakeY[2] = 100; //第二节身体坐标
dir = "R"; //初始方向向右
}
//绘制面板
@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);
//把小蛇画上去
//蛇头初始化向右
switch (dir) {
case "R":
Data.right.paintIcon(this, g, snakeX[0], snakeY[0]);
break; case "L":
Data.left.paintIcon(this, g, snakeX[0], snakeY[0]);
break; case "U":
Data.up.paintIcon(this, g, snakeX[0], snakeY[0]);
break; case "D":
Data.down.paintIcon(this, g, snakeX[0], snakeY[0]);
break; }
for (int i = 1; i < length; i++) {
Data.body.paintIcon(this,g,snakeX[i],snakeY[i]);
}
//游戏状态
if (!isStart)
{
g.setColor(Color.white);
g.setFont(new Font("微软雅黑",Font.BOLD,40));
g.drawString("按下空格开始游戏",300,300);
}
}
//键盘监听事件
@Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_SPACE) {
isStart = !isStart;
repaint();
}
}
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
}
}
键盘控制小蛇移动
游戏面板类
代码
//游戏的面板
public class GamePanel extends JPanel implements KeyListener, ActionListener {
//定义蛇的数据结构
int length;//蛇的长度
//黑色游戏界面总共大小为850*600
//所以小蛇最多的节数为850*600/25*25 = 816
//所以只需要以下两个数组比816长即可
int[] snakeX = new int[850];//蛇的X坐标
int[] snakeY = new int[850];//蛇的Y坐标
String dir;//初始方向
//当前游戏状态:开始,停止
boolean isStart = false; //默认是不开始
Timer timer = new Timer(100,this);//监听当前对象,100ms刷新一次
//构造器
public GamePanel(){
init();
//获得焦点事件
this.setFocusable(true);
//获得键盘事件
this.addKeyListener(this);
}
//初始化方法
public void init(){
length = 3;
snakeX[0] = 100;snakeY[0] = 100;//脑袋的坐标
snakeX[1] = 75;snakeY[1] = 100; //第一节身体坐标
snakeX[2] = 50;snakeY[2] = 100; //第二节身体坐标
dir = "R"; //初始方向向右
timer.start();
}
//绘制面板
@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);
//把小蛇画上去
//蛇头图片
switch (dir) {
case "R":
Data.right.paintIcon(this, g, snakeX[0], snakeY[0]);
break; case "L":
Data.left.paintIcon(this, g, snakeX[0], snakeY[0]);
break; case "U":
Data.up.paintIcon(this, g, snakeX[0], snakeY[0]);
break; case "D":
Data.down.paintIcon(this, g, snakeX[0], snakeY[0]);
break; }
for (int i = 1; i < length; i++) {
Data.body.paintIcon(this,g,snakeX[i],snakeY[i]);
}
//游戏状态
if (!isStart)
{
g.setColor(Color.white);
g.setFont(new Font("微软雅黑",Font.BOLD,40));
g.drawString("按下空格开始游戏",300,300);
}
}
//键盘监听事件
@Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_SPACE) {
isStart = !isStart;
repaint();
}
//小蛇移动
switch (keyCode) {
case KeyEvent.VK_UP:
dir = "U";
break; case KeyEvent.VK_DOWN:
dir = "D";
break; case KeyEvent.VK_LEFT:
dir = "L";
break; case KeyEvent.VK_RIGHT:
dir = "R";
break; }
}
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
}
//实现事件监听
//实现隔一段时间按刷新,比如说一秒10次
@Override
public void actionPerformed(ActionEvent e) {
//如果游戏是开始状态,小蛇就动起来
if (isStart)
{
//右移
for (int i = length - 1; i > 0; i--) {
snakeX[i] = snakeX[i-1];
snakeY[i] = snakeY[i-1];
}
//脑袋如何移动
switch (dir) {
case "U":
snakeY[0] = snakeY[0] - 25;
break; case "D":
snakeY[0] = snakeY[0] + 25;
break; case "L":
snakeX[0] = snakeX[0] - 25;
break; case "R":
snakeX[0] = snakeX[0] + 25;
break; }
//边界判断
if(snakeX[0] > 850)
snakeX[0] = 25;
if(snakeX[0] < 25)
snakeX[0] = 850;
if(snakeY[0] > 650)
snakeY[0] = 75;
if(snakeY[0] < 75)
snakeY[0] = 650;
repaint();//重画页面
}
timer.start();
}
}
小蛇吃食物长大、失败判定及积分系统
游戏面板类
代码
//游戏的面板
public class GamePanel extends JPanel implements KeyListener, ActionListener {
//定义蛇的数据结构
int length;//蛇的长度
//黑色游戏界面总共大小为850*600
//所以小蛇最多的节数为850*600/25*25 = 816
//所以只需要以下两个数组比816长即可
int[] snakeX = new int[850];//蛇的X坐标
int[] snakeY = new int[850];//蛇的Y坐标
String dir;//初始方向
//食物坐标
int foodX;
int foodY;
Random random = new Random();
int score;
//当前游戏状态:开始,停止
boolean isStart = false; //默认是不开始
boolean isFail = false; //游戏是否失败,初始化为未失败
Timer timer = new Timer(100,this);//监听当前对象,100ms刷新一次
//构造器
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; //第二节身体坐标
dir = "R"; //初始方向向右
foodX = 25 + 25*random.nextInt(33);
foodY = 75 + 25*random.nextInt(23);
score = 0;
}
//绘制面板
@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,35);
g.drawString("分数"+score,750,50);
Data.food.paintIcon(this,g,foodX,foodY);
//把小蛇画上去
//蛇头图片
switch (dir) {
case "R":
Data.right.paintIcon(this, g, snakeX[0], snakeY[0]);
break; case "L":
Data.left.paintIcon(this, g, snakeX[0], snakeY[0]);
break; case "U":
Data.up.paintIcon(this, g, snakeX[0], snakeY[0]);
break; case "D":
Data.down.paintIcon(this, g, snakeX[0], snakeY[0]);
break; }
for (int i = 1; i < length; i++) {
Data.body.paintIcon(this,g,snakeX[i],snakeY[i]);
}
//游戏状态
if (!isStart)
{
g.setColor(Color.white);
g.setFont(new Font("微软雅黑",Font.BOLD,40));
g.drawString("按下空格开始游戏",300,300);
}
if (isFail)
{
g.setColor(Color.red);
g.setFont(new Font("微软雅黑",Font.BOLD,40));
g.drawString("失败,按下空格重新开始游戏",200,300);
}
}
//键盘监听事件
@Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_SPACE) {
if (isFail) {
//重新开始
isFail = false;
init();
} else{
isStart = !isStart;
}
repaint();
}
//小蛇移动
switch (keyCode) {
case KeyEvent.VK_UP:
dir = "U";
break; case KeyEvent.VK_DOWN:
dir = "D";
break; case KeyEvent.VK_LEFT:
dir = "L";
break; case KeyEvent.VK_RIGHT:
dir = "R";
break; }
}
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
}
//实现事件监听
//实现隔一段时间按刷新,比如说一秒10次
@Override
public void actionPerformed(ActionEvent e) {
//如果游戏是开始状态,小蛇就动起来
if (isStart && !isFail)
{
//吃食物
if(snakeX[0] == foodX && snakeY[0] == foodY)
{
//长度+1
length++;
//分数+10
score+=10;
//再次随机事物
foodX = 25 + 25*random.nextInt(33);
foodY = 75 + 25*random.nextInt(23);
}
//右移
for (int i = length - 1; i > 0; i--) {
snakeX[i] = snakeX[i-1];
snakeY[i] = snakeY[i-1];
}
//脑袋如何移动
switch (dir) {
case "U":
snakeY[0] = snakeY[0] - 25;
break; case "D":
snakeY[0] = snakeY[0] + 25;
break; case "L":
snakeX[0] = snakeX[0] - 25;
break; case "R":
snakeX[0] = snakeX[0] + 25;
break; }
//边界判断
if(snakeX[0] > 850)
snakeX[0] = 25;
if(snakeX[0] < 25)
snakeX[0] = 850;
if(snakeY[0] > 650)
snakeY[0] = 75;
if(snakeY[0] < 75)
snakeY[0] = 650;
//失败判定,撞到自己就失败
for (int i = 1; i < length; i++) {
if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i])
isFail = true;
}
repaint();//重画页面
}
timer.start();
}
}
此时,游戏启动后便可以正常运行,基础功能已经实现