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

界面图
奔跑吧小恐龙是一款简单的跑酷游戏(代码简单,适合初学者学习)。玩家控制小恐龙向前狂奔,躲避沿途出现的石头和仙人掌,跑的越远,分数越高。游戏内还增加了背景音乐、跳跃音乐和碰撞音乐。
本文的代码虽然长,但不难理解,希望大家能够耐心看完。
文中代码均可直接运行,完整代码请见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();// 强制关闭线程
	}
  • 8
    点赞
  • 82
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
Java实战项目大全是一个汇总了各种Java实战项目的项目集合。Java实战项目旨在通过实际的项目开发来提高学习者的编程能力和技术实践水平。这些项目涵盖了各个领域和应用,包括Web开发、移动应用开发、大数据处理、人工智能等。 其中,Web开发领域的实战项目包括电子商务平台开发、社交网络网站开发、博客系统开发等。通过这些项目的实战,学习者可以掌握Java的Web开发框架(如Spring、Struts、Hibernate等)和相关技术(如HTML、CSS、JavaScript等),了解并熟悉Web开发的工作流程和方法。 移动应用开发领域的实战项目包括Android应用开发、iOS应用开发、跨平台应用开发等。通过实际的项目实践,学习者可以学习和运用Java相关的移动应用开发框架(如Android SDK、JavaFX等),熟悉移动应用的生命周期和发布流程。 在大数据处理领域,Java实战项目可以涵盖Hadoop平台的搭建与配置、数据分析和处理的相关算法实现等。通过这些项目,学习者可以了解分布式计算和大数据处理的基本概念与技术,并能够使用Java语言进行大数据的处理和分析。 此外,还有人工智能领域的实战项目,包括机器学习算法的实现、图像识别和处理的应用开发等。通过这些项目实战,学习者可以深入了解和应用Java相关的人工智能技术,提高机器学习、深度学习和图像处理等方面的实践能力。 综上所述,Java实战项目大全提供了丰富的实践项目,帮助学习者通过实际项目的开发来提高编程能力和技术实践水平,从而更好地掌握和应用Java编程语言。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值