恐龙小游戏-Java从入门到精通(第5版)

实现后的样式:
1
恐龙小游戏-Java从入门到精通(第5版)_第1张图片

2恐龙小游戏-Java从入门到精通(第5版)_第2张图片
准备部分:
恐龙小游戏-Java从入门到精通(第5版)_第3张图片
恐龙小游戏-Java从入门到精通(第5版)_第4张图片
Start.java

package com.mr.main;

import com.mr.view.MainFrame;

//启动类
public class Start {
    public static void main(String[] args){
        MainFrame frame = new MainFrame();//创建主窗体
        frame.setVisible(true);//显示主窗体
    }
}

Dinosaur.java

package com.mr.modle;

import com.mr.service.FreshThread;
import com.mr.service.Sound;

import javax.imageio.ImageIO;
import java.awt.Rectangle;//矩形边界类
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

//恐龙类
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;//刷新时间

    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();
            }
    }
//踏步
    private void step(){
        //每过250毫秒,更换一张图片.因为一共有3张,所以除以3取余,轮流展示这3张
        int tmp = stepTimer / 250 % 3;
        switch(tmp){
            case 1:
                image = image1;
                break;
            case 2:
                image = image2;
                break;
            case 3:
                image = image3;
                break;
        }
        stepTimer += FREASH;//计时器递增
    }
//跳跃
    public void jump(){
        if (!jumpState){//如果没有处于跳跃状态
            Sound.jump();//播放跳跃音乐
        }
        jumpState = true;//处于跳跃状态
    }
//移动
    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;//停止跳跃
            }
        }
    }
    public Rectangle getFootBounds(){//获取恐龙脚部边界对象
        return new Rectangle(x + 30,y+59,29,18);
    }
    public Rectangle getHeadBounds(){//获取恐龙头部边界对象
        return new Rectangle(x + 66,y+25,32,22);
    }
}

Obstacle.java

package com.mr.modle;

import com.mr.view.BackgroundImage;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Random;

//障碍类
public class Obstacle {
    public int x,y;//纵横坐标
    public BufferedImage image;
    private BufferedImage stone;//石头图片
    private BufferedImage cacti;//仙人掌图片
    private int speed;//移动速度
    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();//纵坐标
        speed = BackgroundImage.SPEED;//移动速度与背景同步
    }

//移动
    public void move(){
        x -= speed;//横坐标递减
    }

//获取边界
    public Rectangle getBounds(){
        if (image == cacti){//如果使用仙人掌的图片
            return new Rectangle(x + 7,y,15,image.getHeight());//返回仙人掌的边界

       }
        return new Rectangle(x + 5,y + 4,23,21);//返回石头的边界
    }

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

FreahThread.java

package com.mr.service;

import com.mr.view.GamePanel;
import com.mr.view.MainFrame;
import com.mr.view.ScoreDialog;

import java.awt.Container;

//刷新帧线程
public class FreshThread extends Thread{
    public static final int FREASH = 20;//刷新时间
    GamePanel p;//刷新面板

    public FreshThread(GamePanel p) {
        this.p = p;
    }
    public void run() {
        while (!p.isFinish()) {//如果游戏未结束
            p.repaint();//重绘游戏面板
            try {
                Thread.sleep(FREASH);//按照刷新时间休眠
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        Container c = p.getParent();//获取面板父容器
        while (!(c instanceof MainFrame)) {//如果父容器不是主窗体类
            c = c.getParent();//继续获取父容器的父容器
        }
        MainFrame frame = (MainFrame) c;//将容器强制转换为主窗体类
        new ScoreDialog(frame);//弹出的成绩对话框
        frame.restart();//主窗体重载开始游戏
    }
}

MusicPlayer.java

package com.mr.service;

import javax.sound.sampled.*;//混音器工具
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound. sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.UnsupportedAudioFileException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;

//音乐播放器类
public class MusicPlayer implements Runnable{
    File soundFile;//音乐文件
    Thread thread;//父线程
    boolean circulate;//是否循环播放

//构造方法,默认不循环播放
    //音乐文件完整
    public MusicPlayer(String filepath)throws FileNotFoundException {
        this(filepath, false);
    }

//构造方法
//音乐文件完整名称
//是否循环播放
        public MusicPlayer(String filepath,boolean circulate)throws FileNotFoundException{
        this.circulate = circulate;
        soundFile = new File(filepath);
        if (!soundFile.exists()){
            throw new FileNotFoundException(filepath+"未找到");//如果文件不存在
        }
    }

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

//停止播放
    public void stop(){
        thread.stop();//强制关闭线程
    }

//重写线程执行方法
    @Override
    public void run() {
        byte[] auBuffer = new byte[1024 * 128];//创建128k缓冲区
        do {
            AudioInputStream audioInputStream = null;//创建音频输入流对象
            SourceDataLine auline = null;//混频器源数据行
            try {
                //从音乐文件中获取音频输入流
                audioInputStream = AudioSystem.getAudioInputStream(soundFile);
                AudioFormat format = 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
                    byteCount = audioInputStream.read(auBuffer,0,auBuffer.length);//从音频数据流中读出128k的数据
                    if (byteCount >= 0){//如果读出有效数据
                        auline.write(auBuffer,0,byteCount);//将有效数据写入数据行中
                    }
                }
            } catch (LineUnavailableException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (UnsupportedAudioFileException e) {
                e.printStackTrace();
            }finally {
                auline.drain();//清空数据行
                auline.close();//关闭数据行
            }
        }while (circulate);//根据循环标志判断是否循环播放
    }
}

ScoreRecorder.java

package com.mr.service;

import java.io.*;
import java.util.Arrays;

//分数记录类
public class ScoreRecorder {
    private static final String SCOREFILE = "data/soure";//成绩记录
    private  static  int scores[] = new int[3];//当前得分最高前三名
//分数初始化
    public static void init(){
        File f = new File(SCOREFILE);//创建记录文件
        if (!f.exists()){//如果文件不存在
            try {
                f.createNewFile();//创建新文件
            }catch (IOException e){
                e.printStackTrace();
            }
            return;//停止方法
        }
        FileInputStream fis = null;
        InputStreamReader isr = null;
        BufferedReader br = null;
        try {
            fis = new FileInputStream(f);//文件字节输入流
            isr = new InputStreamReader(fis);//字节流转字符流
            br = new BufferedReader(isr);//缓冲字符流
            String value = br.readLine();//读取一行
            if (!(value == null || "".equals(value))){//如果不为空值
                String vs[] = value.split(",");//分割字符串
                if (vs.length < 3){//如果分割结果 < 3
                    Arrays.fill(scores,0);//数组填充0
                }else{
                    for (int i = 0; i < 3;i++){
                        scores[i] = Integer.parseInt(vs[i]);//将记录文件中的值赋给当前分数数组
                    }
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }catch (IOException e){
            e.printStackTrace();
        }finally {//依次关闭流
            try{
                br.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try{
                isr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

//保存分数
    public static void saveScore(){
        String value = scores[0] + "," + scores[1] + "," + scores[2];//拼接得分数组
        FileOutputStream fos = null;
        OutputStreamWriter osw = null;
        BufferedWriter bw = null;
        try{
            fos = new FileOutputStream(SCOREFILE);//文件字节输出流
            osw = new OutputStreamWriter(fos);//字节流转字符流
            bw = new BufferedWriter(osw);//缓冲字符流
            bw.write(value);//写入拼接后的字符串
            bw.flush();//字符流刷新
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {//依次关闭流
            try{
                bw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try{
                osw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try{
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

//添加分数  (如果新添加的分数比排行榜分数高,则会将新分数计入排行榜)
//新分数
    public static void addNewScore(int score){//向成绩数组中添加新成绩,score参数就是要添加的新成绩数值
        //在的分数组基础上创建一个长度为4的临时数组
        int tmp[] = Arrays.copyOf(scores,4);
        tmp[3] = score;//将新分数赋值给第四个元素
        Arrays.sort(tmp);//临时数组降序排列
        scores = Arrays.copyOfRange(tmp,1,4);//将最后三个元素赋值给的分数组
    }
//获取分数
//因为成绩数组是私有属性,所以必须通过getScores()方法获取
    static public  int[] getScores(){
        return scores;//返回当前成绩数组的值
    }
}

Sound.java

package com.mr.service;

import java.io.FileNotFoundException;

//音效类
public class Sound {
    static  final String DIR = "music/";//音乐文件夹
    static  final String BACKGROUD = "background.wav";//背景音乐
    static  final String JUMP = "jump.wav";//跳跃音效
    static  final String HIT = "hit.wav";//撞击音效
//    /**
//     * 播放声音
//     * @param file 音乐文件是否完整名称
//     * @param circulate 是否循环播放
//     */
    private static void play(String file,boolean circulate){
        try{
            MusicPlayer player = new MusicPlayer(file,circulate);//创建播放器
            player.play();//播放器开始播放
        }catch (FileNotFoundException e){
            e.printStackTrace();
        }
    }
    /**
     * 播放跳跃音效
     */
    static public void jump(){
        play(DIR + JUMP,false);//播放一次跳跃音效
    }
    /**
     * 播放撞击音效
     */
    static public void hit(){
        play(DIR + HIT,false);//播放一次撞击音效
    }
    /**
     * 播放背景音乐
     */
    static public void backgroud(){
        play(DIR + BACKGROUD,true);//循环播放背景音乐
    }
}

BackgroundImage.java

package com.mr.view;

import javax.imageio.ImageIO;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

//滚动背景
public class BackgroundImage {
    public BufferedImage image;//背景图片
    private BufferedImage image1, image2;//滚动的两个图片
    private Graphics2D g;//背景图片的绘图对象
    public int x1, x2;//两个滚动图片的坐标
    public static final int SPEED = 4;//滚动速度

    public  BackgroundImage(){
        try {
            image1 = ImageIO.read(new File("image/背景.png"));
            image2 = ImageIO.read(new File("image/背景.png"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        //主图片采用宽800高300的彩色图片
        image = new BufferedImage(800,300,BufferedImage.TYPE_INT_BGR);
        g = image.createGraphics();//获取主图片绘图对象
        x1 = 0;//第一幅图片初始横坐标为0
        x2 = 800;//第二幅图片初始横坐标为800
        g.drawImage(image1,x1,0,null);
    }
    public void roll(){
        x1 -= SPEED;//第一幅图片左移
        x2 -= SPEED;//第二幅图片左移
        if (x1 <= -800){//如果第一幅图片移出屏幕
            x1 = 800;//回到屏幕右侧
        }
        if (x2 <= -800){//如果第二幅图片移出屏幕
            x2 = 800;//回到屏幕右侧
        }
        g.drawImage(image1,x1,0,null);//在主图片中绘制两幅图片
        g.drawImage(image2,x2,0,null);
    }
}

GamePanel.java

package com.mr.view;

import com.mr.modle.Dinosaur;
import com.mr.modle.Obstacle;
import com.mr.service.FreshThread;
import com.mr.service.ScoreRecorder;
import com.mr.service.Sound;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;

//游戏面板
public class GamePanel extends JPanel implements KeyListener {
    private BufferedImage image;//主图片
    private BackgroundImage background;//背景图片
    private Dinosaur golden;//恐龙图片
    private Graphics2D g2;//主图片绘图对象
    private int addObstacleTimer = 0;//添加障碍计时器
    private boolean finish = false;//游戏结束标志
    private List<Obstacle> list = new ArrayList<Obstacle>();//障碍集合
    private final int FREASH = FreshThread.FREASH;//刷新时间

    int score = 0;//得分
    int scoreTime = 0;//分数计时器

    public GamePanel() {
        //主图片采用宽800,高300的彩色图片
        image = new BufferedImage(800, 300, BufferedImage.TYPE_INT_BGR);
        g2 = image.createGraphics();//获取主图片绘图对象
        background = new BackgroundImage();//初始化滚动背景
        golden = new Dinosaur();//初始化小恐龙
        list.add(new Obstacle());//添加第一个障碍
        FreshThread t = new FreshThread(this);//刷新帧线程
        t.start();//启动线程
    }

    //绘制主图片
    private void paintImage() {
        background.roll();//背景图片开始滚动
        golden.move();//恐龙开始移动
        g2.drawImage(background.image, 0, 0, this);//绘制滚动背景
        if (addObstacleTimer == 1300) {//每过1300毫秒
            if (Math.random() * 100 > 40) {//60%概率会出现障碍
                list.add(new Obstacle());
            }
            addObstacleTimer = 0;//重新计时
        }

        for (int i = 0; i < list.size(); i++) {//遍历障碍集合
            Obstacle o = list.get(i);//获取障碍对象
            if (o.isLive()) {//如果是有效障碍
                o.move();//障碍移动
                g2.drawImage(o.image, o.x, o.y, this);//绘制障碍
                //如果恐龙头脚碰到障碍
                if (o.getBounds().intersects(golden.getFootBounds()) || o.getBounds().intersects(golden.getHeadBounds())) {
                    Sound.hit();//播放撞击声音
                    gameOver();//游戏结束
                }
            } else {//如果不是有效障碍
                list.remove(i);//删除此障碍
                i--;//循环变量前移
            }
        }
        g2.drawImage(golden.image, golden.x, golden.y, this);//绘制恐龙
        if (scoreTime >= 500) {//每过500毫秒
            score += 10;//加10分
            scoreTime = 0;//重新计时
        }
        g2.setColor(Color.BLACK);//使用黑色
        g2.setFont(new Font("黑体", Font.BOLD, 24));//设置字体
        g2.drawString(String.format("%06d", score), 700, 30);//绘制分数

        addObstacleTimer += FREASH;//障碍计时器递增
        scoreTime += FREASH;//分数计时器递增
    }

//重写绘制组件方法
    public void paint(Graphics g) {
        paintImage();//绘制主图片内容
        g.drawImage(image, 0, 0, this);
    }

//判断游戏是否结束
    public boolean isFinish() {
        return finish;
    }

//使游戏结束
    public void gameOver() {
        ScoreRecorder.addNewScore(score);//记录当前分数
        finish = true;
    }

//实现按下键盘按键方法
    public void keyPressed(KeyEvent e){
        int code = e.getKeyCode();//获取按下的按键值
        if (code == KeyEvent.VK_SPACE){//如果是空格
            golden.jump();//恐龙跳跃
        }
    }
    @Override
    public void keyReleased(KeyEvent e) {

    }
    @Override
    public void keyTyped(KeyEvent e) {

    }
}

MainFrame.java

package com.mr.view;

import com.mr.service.ScoreRecorder;
import com.mr.service.Sound;

import java.awt.Container;
import javax.swing.JFrame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

//主窗体(主窗口类)
public class MainFrame extends JFrame {

    public MainFrame() {
        restart();//开始
        setBounds(340, 150, 821, 260);//设置横纵坐标和宽高
        setTitle("奔跑吧!小恐龙!");//设置标题
        Sound.backgroud();//播放背景音乐
        ScoreRecorder.init();//读取得分记录
        addListener();//添加监听
        setDefaultCloseOperation(EXIT_ON_CLOSE);//关闭窗体则停止程序
    }
//重新开始
    public void restart(){
            Container c = getContentPane();//获取主容器对象
            c.removeAll();//删除容器中的所有组件
            GamePanel panel = new GamePanel();//创建新的游戏面板
            c.add(panel);
            addKeyListener(panel);//添加键盘事件
            c.validate();//容器重新验证所有组件
        }
//添加监听
        private void addListener(){
            addWindowListener(new WindowAdapter(){//添加窗口监听
                public void windowClosing(WindowEvent e){//窗体关闭前
                    ScoreRecorder.saveScore();//保存得分记录
                }
            });
        }
}

ScoreDialog.java

package com.mr.view;


import com.mr.service.ScoreRecorder;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

//成绩对话框
public class ScoreDialog extends JDialog{

//构造方法
    public ScoreDialog(JFrame frame){//调用父类构造方法,阻塞父窗体
    super(frame,true);//获取当前的前三名成绩
    int scores[] = ScoreRecorder.getScores();//获取当前的前三名成绩
    JPanel scoreP = new JPanel(new GridLayout(4,1));//成绩面板,4行1列
    scoreP.setBackground(Color.WHITE);//白色背景
    JLabel title = new JLabel("得分排行榜",JLabel.CENTER);//标题标签,居中
    title.setFont(new Font("黑体",Font.BOLD,20));//设置字体
    title.setForeground(Color.RED);//红色字体
    //第一名标签,居中显示
    JLabel first = new JLabel("第一名:" + scores[2],JLabel.CENTER);
    //第二名标签,居中显示
    JLabel second = new JLabel("第二名:" + scores[1],JLabel.CENTER);
    //第三名标签,居中显示
    JLabel third = new JLabel("第三名:" + scores[0],JLabel.CENTER);
    JButton restart = new JButton("重新开始");//重新开始按钮
    restart.addActionListener(new ActionListener(){//按钮添加事件监听
        @Override
            public void actionPerformed(ActionEvent e){// 当点击时
                dispose();//销毁对话框
            }
        });

        scoreP.add(title);//成绩面板添加标签
        scoreP.add(first);
        scoreP.add(second);
        scoreP.add(third);

        Container c = getContentPane();//获取主容器
        c.setLayout(new BorderLayout());//使用边界布局
        c.add(scoreP,BorderLayout.CENTER);//成绩面板放中间
        c.add(restart,BorderLayout.SOUTH);//按钮放底部

        setTitle("游戏结束"); //对话框标题

        int width, height;
        width = height = 200;//对话框宽高均为200
        //获得主窗体中居中位置的横坐标
        int x= frame.getX() + (frame.getWidth() - width) / 2;
        //获得主窗体中居中位置的纵坐标
        int y= frame.getY() + (frame.getHeight() - height) / 2;
        setBounds(x, y, width, height);//设置坐标和宽高
        setVisible(true);//显示对话框
    }
}

你可能感兴趣的:(游戏,java)