Java实战项目二(超详细)---奔跑吧小恐龙

2023-11-01

界面图
奔跑吧小恐龙是一款简单的跑酷游戏(代码简单,适合初学者学习)。玩家控制小恐龙向前狂奔,躲避沿途出现的石头和仙人掌,跑的越远,分数越高。游戏内还增加了背景音乐、跳跃音乐和碰撞音乐。
本文的代码虽然长,但不难理解,希望大家能够耐心看完。
文中代码均可直接运行,完整代码请见github
(如果觉得有帮助,记得给个star,谢谢!)

系统结构设计

游戏功能架构图

项目目录结构预览

游戏模型设计

恐龙类

1.定义
Dinosaur类的成员属性绝大多数都是私有属性,只有少数公有属性用于游戏面板绘图使用。Dinosaur类的私有属性包含3张来回切换的跑步照片:
在这里插入图片描述
Dinosaur类的定义如下:

public class Dinosaur {
    public BufferedImage image;// 主图片
    private BufferedImage image1, image2, image3;// 跑步图片
    public int x, y;// 坐标
    private int jumpValue = 0;// 跳跃的增变量
    private boolean jumpState = false;// 跳跃状态
    private int stepTimer = 0;// 踏步计时器
    private final int JUMP_HIGHT = 100;// 跳起最大高度
    private final int LOWEST_Y = 120;// 落地最低坐标
    private final int FREASH = FreshThread.FREASH;// 刷新时间--刷新帧线程
    }

在Dinosaur类的构造方法中将恐龙的横坐标固定在50像素的位置,纵坐标采用落地时的坐标,具体代码如下:

public Dinosaur() {
        x = 50;         // 恐龙的横坐标
        y = LOWEST_Y;   // 恐龙的纵坐标
        try {
            image1 = ImageIO.read(new File("image/恐龙1.png"));    // 读取恐龙的图片
            image2 = ImageIO.read(new File("image/恐龙2.png"));
            image3 = ImageIO.read(new File("image/恐龙3.png"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

2.踏步
游戏中恐龙的横坐标虽然一直没变,但背景的运动会造成恐龙向前移动的假象。Step()方法就是踏步动作的方法,具体代码如下:

    /**
     * 踏步
     */
    private void step() {
        // 每过250毫秒,更换一张图片。因为共有3图片,所以除以3取余,轮流展示这三张
        int tmp = stepTimer / 250 % 3;     // 0、1、2
        switch (tmp) {
            case 1 :
                image = image1;
                break;
            case 2 :
                image = image2;
                break;
            default :
                image = image3;
        }
        stepTimer += FREASH;// 计时器递增
    }

3.跳跃
跳跃是小恐龙躲避障碍的动作,也是玩家可以控制恐龙做出的唯一动作。
具体代码如下:

    /**
     * 跳跃
     */
    public void jump() {
        if (!jumpState) {// 如果没处于跳跃状态
            Sound.jump();// 播放跳跃音效
        }
        jumpState = true;// 处于跳跃状态
    }

4.移动
move()是恐龙移动方法,move()方法不断地调用step()踏步方法
stepTimer踏步计时器会有效控制图片地切换频率
move()方法地具体代码如下:

    /**
	 * 移动
	 */
    public void move() {
        step();// 不断踏步
        if (jumpState) {// 如果正在跳跃
            if (y == LOWEST_Y) {// 如果纵坐标大于等于最低点---(越往上坐标越小)
                jumpValue = -4;// 增变量为负值--向上跳
            }
            if (y <= LOWEST_Y - JUMP_HIGHT) {// 如果跳过最高点
                jumpValue = 4;// 增变量为正值--向下跳
            }
            y += jumpValue;// 纵坐标发生变化
            if (y == LOWEST_Y) {// 如果再次落地
                jumpState = false;// 停止跳跃
            }
        }
    }

5.边界对象
java.awt.Rectangle类是举行边界类。为恐龙的头部和足部创建矩形边界对象,用于做碰撞测试
getFootBounds()方法用于获取恐龙的足部边界对象,具体代码如下:

    /**
     * 足部边界区域
     * 
     * @return
     */
    public Rectangle getFootBounds() {   // 获取恐龙的脚部边界对象
        return new Rectangle(x + 30, y + 59, 29, 18);   // 用于后续做碰撞检测
    }

getHeadBounds()方法用于获取恐龙的头部边界对象,具体代码如下:

   /**
     * 头部边界区域
     * 
     * @return
     */
    public Rectangle getHeadBounds() {	// 获取恐龙的头部边界对象
        return new Rectangle(x + 66, y + 25, 32, 22);   // new Rectangle(x, y, width, height)
    }

障碍类

游戏中的障碍有两种,一种是很矮的石头,另一种是很高的仙人掌,如下图所示。恐龙碰到任何一种障碍,都会导致游戏的结束:
在这里插入图片描述
1.定义
项目中的com.mr.modle.Obstacle类是障碍类,
因为所有的障碍都会随着背景一起移动,所以障碍的移动速度采用背景图片的速度。Obstacle类的定义如下:

public class Obstacle {
	public int x, y;// 横纵坐标
	public BufferedImage image;
	private BufferedImage stone;// 石头图片---(32,26)
	private BufferedImage cacti;// 仙人掌图片---(32,59)
	private int speed;// 移动速度--图片跟着背景走
	}

在Obstacle类的构造方法中,Obstacle对象会随机成为石头或者仙人掌。具体代码如下:

	public Obstacle() {
		try {
			stone = ImageIO.read(new File("image/石头.png")); // 石头图片
			cacti = ImageIO.read(new File("image/仙人掌.png")); // 仙人掌图片
		} catch (IOException e) {
			e.printStackTrace();
		}
		Random r = new Random();// 创建随机对象
		if (r.nextInt(2) == 0) {// 从0和1中取一值,若为0
			image = cacti;// 采用仙人掌图片
		} else {
			image = stone;// 采用石头图片
		}
		x = 800;// 初始横坐标
		y = 200 - image.getHeight();// 纵坐标--使图片处在地平线上
//		System.out.println(image.getWidth());
		speed = BackgroundImage.SPEED;// 移动速度与背景同步--BackgroundImage=背景
	}

2.移动
Obstacle类中的move()方法可以让障碍在游戏画面中移动,且移动速度与背景图片的移动速度相同。move()方法的具体代码如下:

	/**
	 * 移动
	 */
	public void move() {
		x -= speed;// 横坐标递减--障碍物的速度与背景的速度一致
	}

3.消除
当障碍移动出游戏画面之后,就不会再对游戏产生任何影响。
要及时将移出画面之外的障碍删除掉,isLive()方法用于获取障碍的有效状态。isLive()的具体代码如下:

/**
	 * 是否存活
	 * 
	 * @return
	 */
	public boolean isLive() {
		// 如果移出了游戏界面
		if (x <= -image.getWidth()) {
			return false;// 消亡
		}
		return true;// 存活
	}

4.边界对象
障碍的边界对象用于做碰撞检测计算。
用getBounds()方法返回边界对象,该方法的具体代码如下:

	/**
	 * 获取边界
	 * 
	 * @return
	 */
	// 通过getBounds()方法返回边界对象
	public Rectangle getBounds() {
		if (image == cacti) {// 如果使用仙人掌图片
			// 返回仙人掌的边界
			return new Rectangle(x + 7, y, 15, image.getHeight());
		}
		// 返回石头的边界
		return new Rectangle(x + 5, y + 4, 23, 21);
	}

音效模块设计

本节介绍如何利用java代码播放音乐文件。
JDK支持播放的音乐格式非常少,本章使用的所有音乐文件均为WAVE格式。JDK支持的具体音乐格式可以参考javax.sound.sampled.AudioFileFormat.Type类

音频播放器

项目中的com.mr.service.MusicPlayer类是音频播放器类,该类使用javax.sound.sampled包提供的混音器工具实现音频播放功能。
MusicPlayer类实现了Runnerable接口,并在成员属性中定义了一个线程对象,该线程对象用于启动混音器数据行的读写业务。MusicPlayer类的定义如下:

```java
/**
 * 音乐播放器
 */
public class MusicPlayer implements Runnable {
	File soundFile; // 音乐文件
	Thread thread;// 父线程 -- 执行run方法
	boolean circulate;// 是否循环播放

MusicPlayer类的构造方法有两个参数,filepath表示音乐文件的完整文件名,circulate表示是否重复播放。MusicPlayer类构造方法的具体代码如下:

/**
	 * 构造方法
	 * 
	 * @param filepath  音乐文件完整名称
	 * @param circulate 是否循环播放
	 * @throws FileNotFoundException
	 */

	// 构造方法--2
	public MusicPlayer(String filepath, boolean circulate) throws FileNotFoundException {
		this.circulate = circulate;
		soundFile = new File(filepath);   // File soundFile
		if (!soundFile.exists()) {// 如果文件不存在
			throw new FileNotFoundException(filepath + "未找到");  // 抛出错误,文件未找到
		}
	}

MusicPlayer类要实现Runnable接口,必须先实现run()方法。
在run()方法中声明了一个128K的缓冲区字节数组,程序以不断循环的方式将音乐文件以音频输入流格式读入缓冲区,再把缓冲区的数据写入混音器源数据行中,实现播放音乐的效果。run()方法的具体代码如下:

```java
/**
 * 音乐播放器
 */
public class MusicPlayer implements Runnable {
	File soundFile; // 音乐文件
	Thread thread;// 父线程 -- 执行run方法
	boolean circulate;// 是否循环播放

MusicPlayer类的构造方法有两个参数,filepath表示音乐文件的完整文件名,circulate表示是否重复播放。MusicPlayer类构造方法的具体代码如下:

/**
	 * 构造方法
	 * 
	 * @param filepath  音乐文件完整名称
	 * @param circulate 是否循环播放
	 * @throws FileNotFoundException
	 */

	// 构造方法--2
	public MusicPlayer(String filepath, boolean circulate) throws FileNotFoundException {
		this.circulate = circulate;
		soundFile = new File(filepath);   // File soundFile
		if (!soundFile.exists()) {// 如果文件不存在
			throw new FileNotFoundException(filepath + "未找到");  // 抛出错误,文件未找到
		}
	}

MusicPlayer类要实现Runnable接口,必须先实现run()方法。
在run()方法中声明了一个128K的缓冲区字节数组,程序以不断循环的方式将音乐文件以音频输入流格式读入缓冲区,再把缓冲区的数据写入混音器源数据行中,实现播放音乐的效果。run()方法的具体代码如下:

/*
	 * 在run()方法中声明一个128K的缓冲区字节数组
	 * 程序以不断循环的方式将音乐文件以音频输入流格式读入缓冲区
	 * 把缓冲区的数据写入混音器源数据行中
	 * */
	public void run() {
		byte[] auBuffer = new byte[1024 * 128];// 创建128k缓冲区
		do {
			AudioInputStream audioInputStream = null; // 创建音频输入流对象
			SourceDataLine auline = null; // 混频器源数据行
			try {
				// 从音乐文件中获取音频输入流
				audioInputStream = AudioSystem.getAudioInputStream(soundFile);// soundFile--音乐文件
				AudioFormat format = audioInputStream.getFormat(); // 获取音频格式
//				System.out.println(audioInputStream.getFormat());
				// 按照源数据行类型和指定音频格式创建数据行对象
				DataLine.Info info = new DataLine.Info(SourceDataLine.class, format); // 获取音乐格式
				// 利用音频系统类获得与指定 Line.Info 对象中的描述匹配的行,并转换为源数据行对象
				auline = (SourceDataLine) AudioSystem.getLine(info);
				auline.open(format);// 按照指定格式打开源数据行
				auline.start();// 源数据行开启读写活动
				int byteCount = 0;// 记录音频输入流读出的字节数
				while (byteCount != -1) {// 如果音频输入流中读取的字节数不为-1
					// 从音频数据流中读出128K的数据--auBuffer=128k缓冲区
					byteCount = audioInputStream.read(auBuffer, 0, auBuffer.length);
					if (byteCount >= 0) {// 如果读出有效数据--auline=混频器源数据行
						auline.write(auBuffer, 0, byteCount);// 将有效数据写入数据行中
					}
				}
			} catch (IOException e) {
				e.printStackTrace();
			} catch (UnsupportedAudioFileException e) {
				e.printStackTrace();
			} catch (LineUnavailableException e) {
				e.printStackTrace();
			} finally {
				auline.drain();// 清空数据行
				auline.close();// 关闭数据行
			}
		} while (circulate);// 根据循环标志判断是否循环播放
	}

现在只要将MusicPlayer类放入线程中,就可以播放音乐。创建play()方法,传入参数,然后开启线程。play()方法的具体代码如下:

     /**
	 * 播放
	 */
	public void play() {
		thread = new Thread(this);// 创建线程对象
		thread.start();// 开启线程
	}

创建stop()方法,在该方法中强制停止线程。
stop()方法的具体代码如下:

	/**
	 * 停止播放
	 */
	public void stop() {
		thread.stop();// 强制关闭线程
	}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java实战项目二(超详细)---奔跑吧小恐龙 的相关文章

  • Java Swing BoxLayout 忽略 AlignmentX

    在下面的代码中 通过调用setAlignmentX with Component LEFT ALIGNMENT我希望在居中的滑块上获得左对齐的标签 由于某种原因 标签也居中 似乎与传递给 setAlignmentX 的值无关 我必须向 se
  • 不支持的字段:将瞬间格式化为日期 ISO 时的年份[重复]

    这个问题在这里已经有答案了 我正在尝试将 Instant 格式化为 ldap 日期 ISO8601 但在 f format Instant now 处失败 String input 20161012235959 0Z DateTimeFor
  • 如何在ArrayList中的特定位置插入对象

    假设我有一个大小为 n 的对象的 ArrayList 现在我想在特定位置插入另一个对象 假设在索引位置 k 大于 0 且小于 n 并且我希望索引位置 k 处及其之后的其他对象向前移动一个索引位置 那么有没有什么方法可以直接在Java中做到这
  • 将处理项目移至 Eclipse

    我已经在处理项目上工作了一段时间 现在想将其移至 Eclipse 中 我已经在 Eclipse 环境中安装了 Proclipse 我有很多扩展名为 pde 的文件 然而 Proclipse 文件都以 java 结尾 所有 pde 文件都存在
  • Hashset - 创建 Set 后使对象相同

    如果我们在 HashSet 中添加两个不同的对象 可变的 然后通过调用 setter 更改对象的值 使它们相同 则大小仍然是 hashSet 的 2 我无法理解其原因 public static void main String args
  • 获取Android库中的上下文

    我正在编写一个 Android 应用程序 它的一些功能封装在内部库中 但是 要使此功能发挥作用 库需要一个应用程序上下文的实例 为图书馆提供这种上下文的最佳方式是什么 我看到了一些选择 但没有一个有吸引力 Have my library c
  • Maven WebApp META-INF context.xml

    我正在使用 Maven 3 并且尝试在 webapp 文件夹下添加 META INF 文件夹 所以我正在尝试执行以下操作 src main webapp META INF context xml WEB INF 下面是我的 POM 文件
  • java项目中无法加载类“org.slf4j.impl.StaticLoggerBinder”错误? [复制]

    这个问题在这里已经有答案了 我越来越Failed to load class org slf4j impl StaticLoggerBinder 错误 我想将记录器写入文件 所以我使用了 log4j jar 并使用 apache tomca
  • 绘制平滑曲线

    我想创建更平滑的曲线 而不仅仅是线角 这是我现在画的图 这是我的代码 case FREEHAND float pts float ptk ptk new float 2 imageMatrix invert inv if mCurrentS
  • 如何让“循环”泛型在 Java 中工作?

    我在编译以下涉及一些泛型的代码时遇到错误 public abstract class State
  • 是否可以手动检查 LocateRegistry 是否存在?

    I 已经发现 https stackoverflow com a 8338852 897090一种安全的方式获得LocateRegistry 即使注册表尚不存在 Registry registry null try registry Loc
  • 避免 @Secured 注释的重复值

    我正在尝试使用以下方法来保护我的服务方法 Secured如下 public interface IUserService Secured ROLE ROLE1 ROLE ROLE2 ResponseEntity saveUser Creat
  • ActiveMQ JNDI 查找问题

    尝试使用 JNDI 运行以下 ActiveMQ http activemq apache org jndi support html http ActiveMQ 20JNDI 并且我的 jboss server node lib 文件夹中有
  • jDBI中如何进行内查询?

    我怎样才能在 jDBI 中执行这样的事情 SqlQuery select id from foo where name in
  • 使用 Guava Ordering 对对象列表进行多条件排序

    我有一个类无法实现可比较 但需要根据 2 个字段进行排序 我怎样才能用番石榴实现这一目标 假设班级是 class X String stringValue java util Date dateValue 我有一个清单 List
  • 了解Kafka流groupBy和window

    我无法理解 kafka 流中的 groupBy groupById 和窗口的概念 我的目标是聚合一段时间内 例如 5 秒 的流数据 我的流数据看起来像 value 0 time 1533875665509 value 10 time 153
  • Java HashSet 是线程安全的只读吗?

    如果我通过 Collections unmodifyingSet 运行 HashSet 实例后 它是线程安全的吗 我问这个是因为 Set 文档声明它不是 但我只是执行读取操作 来自 Javadoc 请注意 此实现不是同步的 如果多个线程同时
  • JPA Web 应用程序管理策略

    我们目前正在开发一个 J2EE Web 应用程序 使用 JPA 作为我们的数据访问层 我们目前正在研究几种不同的策略来在我们的应用程序中利用缓存 Create an EntityManager per request 在请求范围内获取缓存
  • 在java中执行匿名pl/sql块并获取结果集

    我想执行匿名 PL SQL 并需要获取结果集对象 我得到了可以通过在 PL SQL 块内使用游标来完成的代码 但 PL SQL 块本身将以文本形式来自数据库 所以我无法编辑该 PL SQL 块 并且它只会返回两个值 其列名始终相同 它将返回
  • Java时区混乱

    我正在运行 Tomcat 应用程序 并且需要显示一些时间值 不幸的是 时间快到了 还有一个小时的休息时间 我调查了一下 发现我的默认时区被设置为 sun util calendar ZoneInfo id GMT 08 00 offset

随机推荐

  • CSS3有趣的渐变色

    程序设计之道无远弗届 御晨风而返 杰佛瑞 詹姆士 这里给大家分享几个有趣的渐变色 用来丰富页面 一 背景色渐变 经常用的就是给背景色增加渐变效果 这里的语法顺序不要错 牵扯到优雅降级和渐进增强 标准语法写到最后也是为了兼容各个版本浏览器 让
  • curl命令的使用

    curl是利用URL语法在命令行方式下工作的开源文件传输工具 1 CURL URL 获取当个网页源码 默认将输出打印到标准输出中 curl www baidu com 2 CURL o O选项保存下载的文件到指定的文件中 o 将文件保存为命
  • AI工程师职业规划指南——中科院大牛告诉你如何成为一名语音识别工程师

    语音识别基础知识 数学与统计学 数学是所有学科的基础 其中的高等数学 数理方程 泛函分析等课程是必要的基础知识 概率论与数理统计也是语音识别的基础学科 声学与语言学 声学基础 理论声学 声学测量等是声学方面的基础课程 有助于了解更多声学领域
  • ElasticSearch学习笔记

    一 ES核心概念 elasticsearch 是面向文档的 一切都是 Json ES关系型数据库的对比记忆 ElasticSearch 关系型数据库 索引 indices 数据库 database 类型 types 表 tables 文档
  • Hive 数据仓库基础应用---第一篇

    hive 的数据定义语句 DDL 创建数据库 语法格式如下 CREATE DATABASE IF NOT EXITS database name COMMENT database comment LOCATION hdfs path WIT
  • arm架构安装docker-compose

    先安装好Docker 华为云arm架构安装Docker arm架构下安装docker compose 官网地址 https github com docker compose releases 官方没有提供arm架构的包 自己去找arm架构
  • 迅雷下载器无限制版_无敏感_无限速

    迅雷下载器5 8 下载链接 链接 https pan baidu com s 1ZYf1aRwZvW4PUT7qO0lKIg 提取码 if5x 速度如图 转载于 https www cnblogs com yzhyingcool p 109
  • CSS使网页适应不同屏幕大小(最实用的rem基础屏幕的适配方案)

    先看代码 复制使用即可 以下代码均可复制粘贴使用 我将以注释的形式解释代码左右 如您满意请给莫成尘点个Fabulous 牛顿说过 我之所以看得远 是因为我站在巨人的肩膀上 我们充分借鉴了element antd等的方案来适配 需 要 注 意
  • kafka消息删除机制

    kafka过期消息删除过程 有时候总觉得我的消息没到7天就被删除了 我还以为是我的kafka配置没有生效 了解到 kafka删除机制后才恍然大悟 kafka消息首先由用户设定一个或多个partition 每个partition中kafka会
  • 光圈

    镜头上写 1 1 8 说明该镜头的最大光圈是f 1 8 F number 光圈值 F number 指的是focal length number aperture 光圈 光圈指得是镜头中间开孔的大小 光圈的作用在于决定镜头的进光量 光圈值越
  • 机器学习基础篇(十二)——多层感知机

    机器学习基础篇 十二 多层感知机 一 概述 多层感知机 MLP Multi Layer Perceptron 由感知机 PLA Perceptron Learning Algorithm 推广而来 它最主要的特点是有多个神经元层 因此也叫深
  • 大咖云集,EI稳定检索,第14届机器学习与计算国际会议(ICMLC 2022)

    14th ICMLC 2022 第14届机器学习与计算国际会议 2月18 21日 中国广州 关于我们 机器学习是人工智能及模式识别领域的共同研究热点 其理论和方法已被广泛应用于解决工程应用和科学领域的复杂问题 为了给机器学习与计算研究领域的
  • 自带win10系统换win7的那些坑

    自带win10系统换win7的那些坑 这两天真是经历了一个换系统的巨坑 如果说这次换系统是一部历史的话那也一定是一部血泪史 今日4000多字的记录会把这部血泪史中的血和泪一一道出 不为别的只为 前车之鉴后事之师 更多内容请关注微信公众号 u
  • QTableView如何插入图片(ICON)在文字的右边

    QTableView如何插入图片 ICON 在文字的右边方法一 QStyledItemDelegate 继承自 QAbstractItemDelegate 主要用于为 Model View 中的数据项提供显示和编辑功能 采用继承QStyle
  • JavaScript运算符优先级

    JavaScript 运算符优先级 是描述在计算机运算计算表达式时执行运算的先后顺序 先执行具有较高优先级的运算 然后执行较低优先级的运算 例如 我们常说的先执行相乘和除 再执行加减运算 JavaScript 运算符 圆括号处理Javasc
  • yarn.lock、package-lock.json、npm-shrinkwrap.json的区别

    总的来说yarn lock和package lock json起的作用相同 只不过yarn是默认的 npm到5以后才会出现lock package lock json是npm5的新特性 也不向前兼容 如果npm版本是4或以下 那得用npm
  • JavaScript HTML DOM

    JavaScript HTML DOM 文档对象模型 是一种用于访问和操作HTML文档元素的编程接口 它将HTML文档表示为一个树形结构 使开发人员可以使用JavaScript来操作和修改HTML元素 属性 样式和事件 通过使用HTML D
  • Vue研习录(04)——列表渲染详解及示例分析

    Vue研习录 04 列表渲染详解及示例分析 版权声明 一 v for 二 维护状态 三 v for 与对象 四 在 v for 里使用范围值 版权声明 本文原创作者 清风不渡 博客地址 https blog csdn net WXKKang
  • 【STM32】时钟系统RCC

    目录 一 时钟树 1 时钟源 2 高速外部时钟信号 HSE 3 低速外部时钟信号 LSE 4 系统时钟 SYSCLK 5 时钟输出 MCO 6 AHB 参考文献 一 时钟树 本文以STM32F103为例 将本人所知的关于STM32的时钟系统
  • Java实战项目二(超详细)---奔跑吧小恐龙

    奔跑吧小恐龙是一款简单的跑酷游戏 代码简单 适合初学者学习 玩家控制小恐龙向前狂奔 躲避沿途出现的石头和仙人掌 跑的越远 分数越高 游戏内还增加了背景音乐 跳跃音乐和碰撞音乐 本文的代码虽然长 但不难理解 希望大家能够耐心看完 文中代码均可