项目演示
项目实战
1. 游戏的主启动类
2. 游戏的面板
3. 数据中心
4. 绘制静态面板
5. 绘制静态小蛇
6. 绘制动态小蛇
7. 设置游戏状态
8. 让蛇动起来
9. 绘制食物布局
10. 游戏失败判定
11. 积分获取系统
12. 游戏优化
项目演示项目演示地址
项目实战 1. 游戏的主启动类作为贪吃蛇游戏的主启动类,构建了顶级窗口,可以容纳各种面板,
package Snake;
import javax.swing.*;
/**
* 游戏的主启动类
*/
public class StartGame {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setBounds(10,10,900,720);
frame.setResizable(false); //窗口大小不可变
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
//正常游戏界面都应该在面板上
frame.setVisible(true);
}
}
2. 游戏的面板
若是没有super.paintComponent(g);
,则会出现闪屏,
在主启动类StartGame
中添加frame.add(new GamePanel());
,
package Snake;
import javax.swing.*;
import java.awt.*;
/**
* 游戏的面板
*/
public class GamePanel extends JPanel {
//绘制面板,游戏中所有东西都用这个画笔来画
@Override
protected void paintComponent(Graphics g){
super.paintComponent(g); //清屏
this.setBackground(Color.BLACK);
}
}
3. 数据中心
创建一个Data
类作为数据中心,用于调用statics
包里的资源,
package Snake;
import javax.swing.*;
import java.net.URL;
/**
* 数据中心,用于调用资源
*/
public class Data {
public static URL headerURL = Data.class.getResource("/statics/header.webp");
public static ImageIcon header = new ImageIcon(headerURL);
public static URL downURL = Data.class.getResource("/statics/down.webp");
public static URL leftURL = Data.class.getResource("/statics/left.webp");
public static URL rightURL = Data.class.getResource("/statics/right.webp");
public static URL upURL = Data.class.getResource("/statics/up.webp");
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("/statics/body.webp");
public static ImageIcon body = new ImageIcon(bodyURL);
public static URL foodURL = Data.class.getResource("/statics/food.webp");
public static ImageIcon food = new ImageIcon(foodURL);
}
4. 绘制静态面板
在GamePanel
类中,构建一个初始的静态面板,添加如下代码,
/**
* 绘制静态面板
*/
this.setBackground(Color.WHITE);
Data.header.paintIcon(this,g,25,11); //头部广告栏
g.fillRect(25,75,850,600); //默认游戏界面
5. 绘制静态小蛇
依然是在类GamePanel
中,先是绘制好小蛇的初始形态,
//定义蛇的数据结构
int length; //蛇的长度
int[] snakeX = new int[600]; //蛇的x坐标 25*25
int[] snakeY = new int[500]; //蛇的y坐标 25*25
//构造器
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; //蛇第二节身体的坐标
}
然后再把绘制好的小蛇画到面板上去,即在paintComponent(Graphics g)
方法中添加如下代码,
/**
* 绘制静态小蛇
*/
Data.right.paintIcon(this,g,snakeX[0],snakeY[0]); //蛇脑袋的坐标
Data.body.paintIcon(this,g,snakeX[1],snakeY[1]); //蛇第一节身体的坐标
Data.body.paintIcon(this,g,snakeX[2],snakeY[2]); //蛇第二节身体的坐标
6. 绘制动态小蛇
小蛇在动起来之后,蛇头会进行上下左右的移动,身体也会变长,因此不能局限于固定的坐标,需要对静态小蛇的代码做如下改动,
添加一个名为fx
的String
对象,存储小蛇的方向,使用if
语句进行判断,
对于小蛇身体节数的增长使用for
循环语句进行控制,
String fx;
//初始化方法
public void init(){
length = 3;
snakeX[0] = 100; snakeY[0] = 100; //蛇脑袋的坐标
snakeX[1] = 75; snakeY[1] = 100; //蛇第一节身体的坐标
snakeX[2] = 50; snakeY[2] = 100; //蛇第二节身体的坐标
fx = "L";
}
/**
* 绘制小蛇
*/
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]); //蛇第一节身体的坐标
}
7. 设置游戏状态
游戏状态主要分为开始
和停止
两种,我们默认游戏状态为停止,
依旧是在类GamePanel
中进行设置,
添加一个boolean
对象,
//游戏状态:开始,停止
boolean isStart = false; //默认游戏不开始
在paintComponent(Graphics g)
方法中添加如下代码,
/**
* 游戏状态
*/
if(isStart == false){
g.setColor(Color.CYAN);
g.setFont(new Font("微软雅黑",Font.BOLD,40)); //设置字体
g.drawString("按下空格开始游戏",300,350);
}
8. 让蛇动起来
让蛇能够动起来就是为程序添加监听事件,内部类或者外部类都可,
空格键获得响应
设置键盘的监听事件,先设置空格的监听事件,
接上接口KeyListener
,重写它的三个方法,
//键盘监听事件
@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 GamePanel(){
init();
//获得焦点和键盘事件
this.setFocusable(true); //获得焦点事件
this.addKeyListener(this); //获得键盘监听事件
}
初始化状态
点击空格后
设置定时器
通过对固定事件的高频率刷新,实现动画效果,即创建定时器Timer
,
//定时器,以ms为单位,1s = 1000ms
Timer timer = new Timer(100, this); //100毫秒执行一次
接下来设置事件监听,先以右移为例,
//事件监听——需要通过固定事件来刷新,比如10次/s
@Override
public void actionPerformed(ActionEvent e) {
if(isStart){ //如果游戏是开始状态,就让小蛇动起来
//右移
for (int i = length-1; i > 0; i--) { //后一节移动到前一节的位置 snakeX[1] = snakeX[0];
snakeX[i] = snakeX[i-1];
snakeY[i] = snakeY[i-1];
}
snakeX[0] += 25;
//边界判断
if (snakeX[0] > 850){
snakeX[0] = 25;
}
repaint(); //重画页面
}
timer.start(); //定时器开启
}
同时不要忘记在构造器中添加启动语句timer.start();
,启动程序,
方向键获得响应
在键盘监听事件keyPressed(KeyEvent e)
中,添加上下左右键盘监听,类似于空格键获得的响应,
/**
* 小蛇移动
*/
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";
}
然后再对定时器进行修改,
//事件监听——需要通过固定事件来刷新,比如10次/s
@Override
public void actionPerformed(ActionEvent e) {
if(isStart){ //如果游戏是开始状态,就让小蛇动起来
//移动
for (int i = length-1; i > 0; i--) { //后一节移动到前一节的位置 snakeX[1] = snakeX[0];
snakeX[i] = snakeX[i-1];
snakeY[i] = snakeY[i-1];
}
//走向
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;
}
}
repaint(); //重画页面
}
timer.start(); //定时器开启
}
9. 绘制食物布局
先是创建食物的坐标,
//食物的坐标
int foodX;
int foodY;
在初始化方法中添加如下语句,随机产生食物的位置,
//把食物随机分布在界面上
foodX = 25 + 25*random.nextInt(34);
foodY = 75 + 25*random.nextInt(24);
在绘制面板方法paintComponent(Graphics g)
中,将食物画上去,
Data.food.paintIcon(this,g,foodX,foodY);
再在事件监听actionPerformed(ActionEvent e)
中,将小蛇吃了食物会使身体变长的语句写上去,
//吃食物
if (snakeX[0] == foodX && snakeY[0] == foodY){
length++; //小蛇身体长度增加一节
//再次随机分配食物
foodX = 25 + 25*random.nextInt(34);
foodY = 75 + 25*random.nextInt(24);
}
10. 游戏失败判定
先设置一个失败标志,
//游戏失败判定
boolean isFail = false; //游戏失败状态
然后在绘制画板paintComponent(Graphics g)
中设置一个失败回显,
if (isFail){
g.setColor(Color.RED);
g.setFont(new Font("微软雅黑",Font.BOLD,40)); //设置字体
g.drawString("游戏结束,按下空格重新开始",300,350);
}
再在键盘监听事件keyPressed(KeyEvent e)
里重写空格键的监听事件,
if (keyCode == KeyEvent.VK_SPACE){ //如果按下空格键
if(isFail){
//重新开始
isFail = false;
init();
}else {
isStart = !isStart; //取反
}
repaint();
}
然后再在事件监听actionPerformed(ActionEvent e)
中再写对失败的判断,
//失败判定,撞到自己游戏结束
for (int i = 1; i < length; i++) {
if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]){
isFail = true;
}
}
11. 积分获取系统
先定义一个用于存储积分的对象score
,然后在绘制面板paintComponent(Graphics g)
中显示出积分来,
/**
* 显示积分
*/
g.setColor(Color.white);
g.setFont(new Font("微软雅黑",Font.BOLD,18)); //设置字体
g.drawString("长度: "+length,750,30);
g.drawString("分数: "+score,750,55);
然后重写事件监听actionPerformed(ActionEvent e)
里的吃食物代码块,
//吃食物
if (snakeX[0] == foodX && snakeY[0] == foodY){
//小蛇身体长度增加一节
length++;
//一个食物加十点积分
score += 10;
//再次随机分配食物
foodX = 25 + 25*random.nextInt(34);
foodY = 75 + 25*random.nextInt(24);
}
12. 游戏优化
移动优化
对蛇头的移动进行了优化,避免了蛇头与第一节蛇身的碰撞,即如果蛇头向右前进,这时候按向左是无效的,
/**
* 小蛇移动
*/
if(keyCode == KeyEvent.VK_UP && !fx.equals("D")){
fx = "U";
}else if (keyCode == KeyEvent.VK_DOWN && !fx.equals("U")){
fx = "D";
}else if (keyCode == KeyEvent.VK_LEFT && !fx.equals("R")){
fx = "L";
}else if (keyCode == KeyEvent.VK_RIGHT && !fx.equals("L")){
fx = "R";
}
速度优化
随着蛇身越来越长,小蛇移动速度会越来越快,这里蛇身每增加5节,速度提升一个等级,
//判断是否吃到食物
boolean foodEat = false;
//蛇身越长,蛇的移动速度越快
if (foodEat == true && length % 5 ==0 && foodColor.equals("Blue")){
grade++;
}
timer.setDelay(150 - grade*10);
食物优化
避免食物的位置与蛇身的位置重叠,而造成食物被蛇身所覆盖,
因此修改原先的食物分配布局,加入判定代码块,
//判断食物是否与蛇身重叠
boolean flag = false; //默认为重叠状态
//把食物随机分布在界面上
while (flag == false){
flag = true;
foodX = 25 + 25*random.nextInt(34);
foodY = 75 + 25*random.nextInt(24);
for (int i = 1; i < length; i++) {
if(foodX == snakeX[i] && foodY == snakeY[i]){
flag = false;
}
}
}
对食物的种类进行多样化,每种颜色代表不同的功能,其中,
蓝色:增加一节蛇的身体,分数+10 绿色:减少一节蛇的身体,分数+10 紫色:加快蛇的移动速度,分数+10 橘色:减慢蛇的移动速度,分数+10
通过随机数对食物种类进行分配,其中,
蓝色:[0.1,0.85) 绿色:[0.85,0.95) 且蛇的长度length
>=2 紫色:[0,0.1) 且timer
的Delay
值>=80 橘色:[0.95,1) 且timer
的Delay
值<=100
//食物的种类
String foodColor;
boolean foodFlag = false;
public static URL foodURL;
public static ImageIcon food;
foodFlag = false;
while (foodFlag == false){
double num = random.nextDouble();
if(0.1 <= num && num < 0.85){
foodURL = GamePanel.class.getResource("statics/foodB.webp");
foodColor = "Blue";
foodFlag = true;
break;
}else if (0.85 <= num && num < 0.95 && length >= 2){
foodURL = GamePanel.class.getResource("statics/foodG.webp");
foodColor = "Green";
foodFlag = true;
break;
}else if (0.0 <= num && num < 0.1 && timer.getDelay() >= 90){
foodURL = GamePanel.class.getResource("statics/foodP.webp");
foodColor = "Purple";
foodFlag = true;
break;
}else if (0.95 <= num && num < 1.0 && timer.getDelay() <= 130){
foodURL = GamePanel.class.getResource("statics/foodO.webp");
foodColor = "Orange";
foodFlag = true;
break;
}
}
food = new ImageIcon(foodURL);
if (foodColor.equals("Blue")){
//小蛇身体长度增加一节
length++;
}else if (foodColor.equals("Green")){
//如果蛇身长度正好是5的倍数会进行降速处理
if (length % 5 ==0){
grade--;
}
//小蛇身体长度减少一节
length--;
}else if (foodColor.equals("Purple")){
//小蛇移动速度加快
grade++;
}else if (foodColor.equals("Orange")){
//小蛇移动速度加快
grade--;
}
以上就是Java实现贪吃蛇游戏的示例代码的详细内容,更多关于Java贪吃蛇的资料请关注易知道(ezd.cc)其它相关文章!