8.4 随机生成食物&&检测自身&&积分系统
8.4.1 随机生成食物
我们在写随机生成食物之前,要学习一个 Math下面的方法,叫做 radom()。
Math.radom() * 最大范围数 就可以取到 0 ~ 最大范围数的 随机双浮点数数字!切记,返回的是双浮点数字,而并非 整数,如果你要求的是整数!请用 int 进行强转。
- 我们知道,X 轴方向的格子 是 850 / 25 = 34个
- 我们知道,Y 轴方向的格子 是 600 / 25 = 24个
所以我们可以 先随机的取 x轴和y轴的 第几个格子,然后再计算坐标值,这样就可以获取到 随机的食物坐标了!
foodX = 25 + ((int)(1+Math.random()*(34))) * 25;
foodY = 75 + ((int)(1+Math.random()*(24))) * 25;
- 如果 蛇头坐标和现在食物坐标相同
那么就证明,我们要进行 吃 食物的操作。
- 蛇身长度 + 1
- 把所有坐标往后移动一位,把食物坐标当作蛇头的坐标,进行坐标的更新!
if(snakeX[0] == foodX && snakeY[0] == foodY)
{
//更新蛇身
length++;//蛇身 + 1
for(int i = length -1;i>0;--i)
{
//把所有坐标往后移动一位
snakeX[i] = snakeX[i-1];
snakeY[i] = snakeX[i-1];
}
snakeX[0] = foodX;
snakeY[0] = foodY;
}
- 随机生成食物避免与蛇身位置重复
- 我们需要有个死循环,来一直检测 现在生成的随机食物坐标是否 不与 蛇身位置重复!
- 而检测 是否重复的时候,需要 遍历 所有的蛇身,包括 蛇头的 坐标,所以在死循环的内部,还要嵌套一个 for 循环。
- 利用 Lael: 标签跳转,实现 当 有坐标和食物坐标重复时,重新随机生成食物的坐标,再跳转到 死循环的首部,进行重检测。
boolean flag = false;
Label:while(!flag)
{
for(int i = 0;i<length;++i)
{
if(snakeX[i] == foodX || snakeY[i] == foodX)
{
flag = false;
foodX = 25 + ((int)(1+Math.random()*(34-1+1))) * 25;
foodY = 75 + ((int)(1+Math.random()*(24-1+1))) * 25;
continue Label;
}
}
flag = true;
}
- 整体代码
//如果吃到了食物,我们就进行随机生成
if(snakeX[0] == foodX && snakeY[0] == foodY)
{
//更新蛇身
length++;//蛇身 + 1
for(int i = length -1;i>0;--i)
{
//把所有坐标往后移动一位
snakeX[i] = snakeX[i-1];
snakeY[i] = snakeX[i-1];
}
snakeX[0] = foodX;
snakeY[0] = foodY;
//随机再生成一个
// 850 / 25 = 34 600 / 25 = 24
foodX = 25 + ((int)(1+Math.random()*(34))) * 25;
foodY = 75 + ((int)(1+Math.random()*(24))) * 25;
boolean flag = false;
Label:while(!flag)
{
for(int i = 0;i<length;++i)
{
if(snakeX[i] == foodX || snakeY[i] == foodX)
{
flag = false;
foodX = 25 + ((int)(1+Math.random()*(34-1+1))) * 25;
foodY = 75 + ((int)(1+Math.random()*(24-1+1))) * 25;
continue Label;
}
}
flag = true;
}
}
8.4.2 检测自身碰撞
当自身碰撞的时候,就意味着 吃到了自己,那么游戏就会失败!此时 需要 把 isStart = false,再 调用 init() 初始化 函数!让其重新 加载游戏。
需要 做的是,判断 蛇头的坐标是否 与 蛇身的坐标 相等,如果相等 则 为 自身碰撞!
//检测自身碰撞
for(int i =1;i<length;++i)
{
if(snakeX[0] == snakeX[i] && snakeY[i] == snakeY[0])
{
isStart = false;
JDialog jDialog = new JDialog();
jDialog.setBounds(500,500,400,200);
jDialog.setVisible(true);
jDialog.setTitle("信息框");
JLabel label = new JLabel("这都能吃到自己?你的游戏失败!");
Font font = new Font("微软雅黑",Font.PLAIN,20);
label.setFont(font);
label.setBounds(20,20,400,100);
jDialog.setLayout(null);
jDialog.add(label);
try {
add(jDialog);
}catch (Exception HH)
{
init();
}
init();
}
}
这里就是 添加了 一个 dialog 信息框窗口,显得 更加的 完美!
8.4.3 积分系统
这个其实也很简单哈~ 就是 再画两个 String 字符串 显示 出来。就是 什么 积分系统了。因为 咱们还没学 文件操作呢,所以 存 不了数据。
//画积分系统
Color color = g.getColor();
g.setColor(Color.white);
Font font = new Font("微软雅黑",Font.PLAIN,18);
g.setFont(font);
g.drawString("当前蛇身长度:"+length, 700,50 );
g.setColor(color);
- GamePanel 全部代码!
package com.muquanyu.snake;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class GamePanel extends JPanel implements KeyListener, ActionListener {
//蛇的长度
int length;
//蛇头的 X坐标 和 Y坐标
int[] snakeX = new int[900];
int[] snakeY = new int[900];
//蛇头方向
String snakeDirection = null;
//游戏当前的状态:开始、停止 默认为 "停止"
boolean isStart = false;
//创建一个时钟 进行 监听
Timer timer = new Timer(100,this);
//食物的 X坐标 和 Y坐标
int foodX;
int foodY;
//定义积分变量
int score = 0;
//初始化
public void init() {
//初始化蛇的长度
length = 3;
//初始化蛇头 坐标
snakeX[0] = 125;snakeY[0] = 100;
//初始化蛇身 坐标
snakeX[1] = 100;snakeY[1] = 100;
snakeX[2] = 75;snakeY[2] = 100;
//蛇头方向
snakeDirection = "right";
//初始化食物 坐标
foodX = 500;
foodY = 250;
}
//绘制面板,我们游戏中的所有东西,都是用画笔来 画
@Override
public void paintComponent(Graphics g) {
//清屏,不会出现闪烁。
super.paintComponent(g);
//绘制静态的面板
this.setBackground(Color.BLACK);
//第一个参数是画到哪个设备上,那肯定是我们 创建的 面板上呀!
Data.header.paintIcon(this, g, 25, 11);
//画积分系统
Color color = g.getColor();
g.setColor(Color.white);
Font font = new Font("微软雅黑",Font.PLAIN,18);
g.setFont(font);
g.drawString("当前蛇身长度:"+length, 700,50 );
g.setColor(color);
//画游戏区域
g.fillRect(25, 75, 850, 600);
//画静态小蛇
switch(snakeDirection)
{
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:break;
}
for(int i = 1;i<length;++i)
{
Data.body.paintIcon(this, g, snakeX[i], snakeY[i]);
}
//游戏状态
if(!isStart)
{
g.setColor(Color.white);
//设置字体
Font 微软雅黑 = new Font("微软雅黑", Font.BOLD, 40);
g.setFont(微软雅黑);
g.drawString("按下空格开始游戏",300,300);
}
//绘画 食物
Data.food.paintIcon(this, g, foodX, foodY);
}
public GamePanel() {
init();
//获得焦点和键盘事件
this.setFocusable(true);//获得焦点事件
this.addKeyListener(this);//获得键盘监听事件
timer.start();//我们直接 就把 定时器 开启
}
//键盘监听事件
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if(keyCode == KeyEvent.VK_SPACE)
{
System.out.println("按下了空格!");
isStart = !isStart;
repaint();//开始重画
}
if(keyCode == KeyEvent.VK_UP)
{
System.out.println("上方向键!");
snakeDirection = "up";
}
if(keyCode == KeyEvent.VK_DOWN)
{
System.out.println("下方向键!");
snakeDirection = "down";
}
if(keyCode == KeyEvent.VK_LEFT)
{
System.out.println("左方向键!");
snakeDirection = "left";
}
if(keyCode == KeyEvent.VK_RIGHT)
{
System.out.println("右方向键!");
snakeDirection = "right";
}
}
@Override
public void keyReleased(KeyEvent e) {
}
@Override
public void actionPerformed(ActionEvent e) {
//如果游戏开始的话
if(isStart)
{
//宽度:850 高度:600
//检测自身碰撞
for(int i =1;i<length;++i)
{
if(snakeX[0] == snakeX[i] && snakeY[i] == snakeY[0])
{
isStart = false;
JDialog jDialog = new JDialog();
jDialog.setBounds(500,500,400,200);
jDialog.setVisible(true);
jDialog.setTitle("信息框");
JLabel label = new JLabel("这都能吃到自己?你的游戏失败!");
Font font = new Font("微软雅黑",Font.PLAIN,20);
jDialog.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
super.keyPressed(e);
if(e.getKeyCode() == KeyEvent.VK_SPACE)
{
jDialog.dispose();
}
}
});
label.setFont(font);
label.setBounds(20,20,400,100);
jDialog.setLayout(null);
jDialog.add(label);
init();
}
}
//如果吃到了食物,我们就进行随机生成
if(snakeX[0] == foodX && snakeY[0] == foodY)
{
//更新蛇身
length++;//蛇身 + 1
for(int i = length -1;i>0;--i)
{
//把所有坐标往后移动一位
snakeX[i] = snakeX[i-1];
snakeY[i] = snakeX[i-1];
}
snakeX[0] = foodX;
snakeY[0] = foodY;
//随机再生成一个
// 850 / 25 = 34 600 / 25 = 24
foodX = 25 + ((int)(1+Math.random()*(34))) * 25;
foodY = 75 + ((int)(1+Math.random()*(24))) * 25;
boolean flag = false;
Label:while(!flag)
{
for(int i = 0;i<length;++i)
{
if(snakeX[i] == foodX || snakeY[i] == foodX)
{
flag = false;
foodX = 25 + ((int)(1+Math.random()*(34-1+1))) * 25;
foodY = 75 + ((int)(1+Math.random()*(24-1+1))) * 25;
continue Label;
}
}
flag = true;
}
}
switch(snakeDirection)
{
case "right":
{
if(snakeX[0] == 850)
{
//那就直接让它 从左边 穿过来就行了
snakeX[0] = 25;
}
else{
//编程思想的写法,并不是什么高深算法!
//我们通过自己画图,也会发现这个 现象
//就是 蛇身的每一块 在移动的时候,恰好移动到了 前一个块的坐标!!!
//你们 自己画图 观察,就能看出来!那么我们只需要 从 最后一块 开始遍历
//然后 让它每次刷新 都 变成 前一块的 坐标不就完事了!!
for(int i = length-1;i>0;i--)
{
snakeX[i] = snakeX[i-1];
snakeY[i] = snakeY[i-1];
}
snakeX[0] = snakeX[0] + 25;
}
repaint();
break;
}
case "left":
{
if(snakeX[0] == 25)
{
snakeX[0] = 850;
}
else{
for(int i = length-1;i>0;i--)
{
snakeX[i] = snakeX[i-1];
snakeY[i] = snakeY[i-1];
}
snakeX[0] = snakeX[0] - 25;
}
repaint();
break;
}
case "up":
{
if(snakeY[0] == 75)
{
snakeY[0] = 600;
}
else{
for(int i = length-1;i>0;i--)
{
snakeX[i] = snakeX[i-1];
snakeY[i] = snakeY[i-1];
}
snakeY[0] = snakeY[0] - 25;
}
repaint();
break;
}
case "down":
{
if(snakeY[0] == 650)
{
snakeY[0] = 75;
}
else{
for(int i = length-1;i>0;i--)
{
snakeX[i] = snakeX[i-1];
snakeY[i] = snakeY[i-1];
}
snakeY[0] = snakeY[0] + 25;
}
repaint();
break;
}
default:break;
}
}
}
}