java课程设计

1. 团队课程设计博客链接

https://www.cnblogs.com/choco1ate/p/12172223.html

2.本组课题及本人任务

本组课题:泡泡堂(炸弹人)游戏

本人任务:Box类(游戏地图中的每个方格)

                   Bomb类(游戏过程中的)

                   游戏玩家输赢信息的文件储存

3.需求分析

  1. Box类:该类为地图Map中的每一个格子,利用该Box设置该位置是空地或者障碍,设置该位置是否有道具和道具类型。
  2. Bomb类:该类为炸弹类,实现在地图中的某一个Box中放置炸弹、炸弹爆炸及清除、设置爆炸后产生的道具或地雷、地雷爆炸及清除、判断玩家是否被炸弹炸伤、设置爆炸音效等功能。
  3. 游戏用户输赢信息的储存即排行榜:开始游戏前,从文件读入文件内的用户信息,若无当前用户,则创建一个新的用户,当一个玩家生命为0时,当前用户的失败场次-1,另一用户胜利场次+1,计算胜率,写入文件。排行榜中同样文件读入文件内的用户信息,同过重写compareTo方法对用户的胜率进行排序。

4.总体设计(概要设计)

5.本人负责的主要功能展示与代码分析

(1)Box类

public Box(int x, int y, int n, int index, boolean canDestroy)// *障碍存在*
    {
        this.canDestroy = canDestroy;
        // 设置该box大小、位置
        this.setBounds(80 * x, 80 * y, 80, 80);

        // 根据传入的参数设置图片
        setIcon("images/" + n + "/" + index + ".png", this);

        // 如果该box可以破坏
        if (canDestroy)
        {
            // 设置该box内藏的宝物
            setTreasure();

        }
    }
    /**不存在*/
    public Box(int x, int y)
    {
        // 设置该box障碍不存在
        isExist = false;
        // 设置该box大小、位置
        this.setBounds(80 * x, 80 * y, 80, 80);
        // 设置该box处图片为默认的图片(透明)
        setIcon("images/default.png", this);
    }

 

 

分析:当该Box为障碍物时,根据传入的障碍物编号设置相应图片,当该Box为道路时,设置为透明,显示背景地图。

public void setTreasure()
    {
        Random r = new Random();
        int isTreasure = r.nextInt(10) + 1;
        // 是宝物的几率为40%
        int noTreasure = 6;
        if (isTreasure > noTreasure) {
            // 读取一个1-5的随机数
            int treasureNum = r.nextInt(6) + 1;

            switch (treasureNum) {
            // 速度+1
            case 1:
                treasureIndex = 1;
                treasurePath = "images/speed+.gif";
                break;
            // 泡泡+1
            case 2:
                treasureIndex = 2;
                treasurePath = "images/bombnum+.gif";
                break;
            // 威力
            case 3:
                treasureIndex = 3;
                treasurePath = "images/power+.gif";
                break;
            //无敌
            case 4:
                treasureIndex = 4;
                treasurePath = "images/invincible.png";
                break;
            //生命值
            case 5:
                treasureIndex = 5;
                treasurePath = "images/live+.png";
                break;
            //地雷
            case 6:
                treasureIndex = 6;
                break;
            default:
                break;
            }
        }
    }

分析:在每一个存在障碍物的Box中,都有一定几率出现道具,随机生成一个1~10的整数,设置当该随机数>6时(即40%的可能性出现道具)再随机生成一个1~6的随机数,根据已设定的道具编号把当前的Box图片地址设置为相应的道具图片。

2)Bomb类

玩家扔下炸弹:

public synchronized void dropBoom(Player pl) {
        // 如果当前位置没有炸弹并且玩家所放炸弹数也没有达到上限,便放置一颗炸弹
        if (!box.isExistBomb && player.getBombexist() < player.bombnum) {
            // 将box处定义炸弹的参数赋值true
            box.isExistBomb = true;
            box.needDetonate = true;
            box.isExistPlayer = true;

            player.thisbomb = box;
            // 设置玩家当前炸弹为当前所在box
            box.boom = this;
            // 设置当前所在box处的boom值

            player.setBombexist(player.getBombexist() + 1);
            // 玩家当前存在的炸弹数+1
            setIcon("images/bomb.gif", box);
            // 将当前box图片设置为炸弹
            MyThread1 ph1 = new MyThread1();
            ph1.start();
        }
    }
private class MyThread1 extends Thread {
        @Override
        public void run() {
            try {
                Thread.sleep(2000);
                explodeSound();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

分析:如果当前位置没有炸弹并且玩家所放炸弹数也没有达到上限,便放置一颗炸弹,将当前Box设为炸弹图片,并开启一个炸弹爆炸线程。

地雷:

public synchronized void hideBoom(Box box1) {
        box1.isExistBomb = true;
        box1.needDetonate = true;
        box1.isExistPlayer = true;
        box1.boom = this;
        // 设置当前所在box处的boom值
        // setIcon("images/bomb.gif", box1);// 将当前box图片设置为炸弹

        MyThread2 ph2 = new MyThread2(box1);
        ph2.start();
    }
private class MyThread2 extends Thread {
        private Box box1;

        private MyThread2(Box box1) {
            this.box1 = box1;
        }

        @Override
        public void run() {
            try {
                // 将被清除的路径上的爆炸块恢复成透明块
                setIcon("images/default.png", box1);
                Thread.sleep(2700);
                // 将当前box图片设置为炸弹
                setIcon("images/bomb.gif", box1);
                Thread.sleep(250);
                box1.isExistBomb = false;
                box1.needDetonate = false;
                // 设置炸弹中心的图片
                setIcon("images/UDLR.png", box1);
                // 判断是否炸到了玩家
                explodePlayer(box1);
                Thread.sleep(300);
                // 将被清除的路径上的爆炸块恢复成透明块
                setIcon("images/default.png", box1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

分析:当有障碍的Box爆炸后,有一定几率出现地雷,开启一个地雷爆炸线程,地雷的爆炸时间和威力都比正常的炸弹小。

设置爆炸方法:

    public synchronized void explode() throws InterruptedException
    // 爆炸
    {
        // 将box处定义炸弹的参数赋值false
        box.isExistBomb = false;
        box.needDetonate = false;
        player.setBombexist(player.getBombexist() - 1);
        // 玩家当前炸弹数-1
        setIcon("images/UDLR.png", box);
        // 设置炸弹中心的图片
        explodePlayer(box);
        // 判断是否炸到了玩家

        for (int n = 1; n <= power; n++)
        // 向上爆炸
        {
            boolean flag = true;
            int x = this.x;
            int y = this.y - n;
            if (y >= 0) {
                Box temp = GameFrame.thismap.getBoxbyLocation(x, y);
                flag = explode1(temp);
                if(flag == false) {
                    break;
                }
            }
        }
        // 向下爆炸
        for (int s = 1; s <= power; s++) {
            boolean flag = true;
            int x = this.x;
            int y = this.y + s;
            if (y < 12) {
                Box temp = GameFrame.thismap.getBoxbyLocation(x, y);
                flag = explode1(temp);
                if(flag == false) {
                    break;
                }
            }
        }
        // 向左爆炸
        for (int w = 1; w <= power; w++) {
            boolean flag = true;
            int x = this.x - w;
            int y = this.y;
            if (x >= 0) {
                Box temp = GameFrame.thismap.getBoxbyLocation(x, y);
                flag = explode1(temp);
                if(flag == false) {
                    break;
                }
            }
        }
        // 向右爆炸
        for (int e = 1; e <= power; e++) {
            boolean flag = true;
            int x = this.x + e;
            int y = this.y;
            if (x < 15) {
                Box temp = GameFrame.thismap.getBoxbyLocation(x, y);
                flag = explode1(temp);
                if(flag == false) {
                    break;
                }
            }
        }

    }

    public boolean explode1(Box temp) {
        if (temp.isExist)
            // 如果爆炸的路线上遇到方块了
            {
                if (temp.isdestroyshowT)
                // 如果是宝物
                {
                    temp.isExist = false;
                    return true;
                    // 宝物消失
                } else
                // 如果是障碍,则终止爆炸
                {
                    showTreasure(temp);
                    // 显示该box处隐藏的宝物
                    return false;
                }
            } else if (temp.isExistBomb)
            // 如果该爆炸处有炸弹
            {

                MyThread3 ph3 = new MyThread3(temp);
                ph3.start();
                continuousBomb.add(temp);
                // 将该炸弹添加到被引爆的列表里
            }
            explodePlayer(temp);
            // 判断是否炸到了玩家
            bombCache.add(temp);
            // 将该处添加到爆炸缓存中
            setIcon("images/UD.png", temp);
            // 设置该处的爆炸图片
            return true;
    }

分析:根据当前玩家的威力,从当前Box分别向上下左右威力内的Box遍历,若遇到不可炸毁的障碍,则结束当前方向的遍历,否则,将遍历的Box设置爆炸。

判断是否炸到玩家:

    /** 判断是否炸到玩家 */
    public synchronized void explodePlayer(Box b) {
        if (b.getRect().intersects(GameFrame.player1.getRect()) && !GameFrame.player1.isInvincible()
                && GameFrame.player1.isalive)
        // 判断玩家一是否和爆炸块重叠,不处于无敌状态,并且存活
        {
            GameFrame.player1.beInjured();// 玩家一受到1点伤害
        }
        if (b.getRect().intersects(GameFrame.player2.getRect()) && !GameFrame.player2.isInvincible()
                && GameFrame.player2.isalive)
        // 判断玩家二是否和爆炸块重叠,不处于无敌状态,并且存活
        {
            GameFrame.player2.beInjured();
            // 玩家二受到1点伤害
        }
    }

分析:判断玩家是否和爆炸块重叠,不处于无敌状态,并且存活,若符合,玩家受到1点伤害。

(3)游戏前输入玩家名称以进行胜率排行

分析:创建一个用户User类,包括用户姓名,胜利的场次、失败的场次和胜率,开始游戏前,使用ArrayList从文件读入文件内的用户信息,若无当前用户,则创建一个新的User,创建一个线程监听当前游戏,当一个玩家生命为0时,当前用户的失败场次-1,另一用户胜利场次+1,加入ArrayList,结束时将ArrayList写入文件。排行榜中同样使用ArrayList从文件读入文件内的用户信息,同过重写compareTo方法对用户的胜率进行排序。

测试、改进与感想

测试与改进

(1)在扔炸弹、炸弹爆炸、地雷爆炸方法中改进使用synchronized,当两个并发线程访问同一个对象中的synchronized代码块时,在同一时刻只能有一个线程得到执行,另一个线程受阻塞,必须等待当前线程执行完这个代码块以后才能执行该代码块。在执行synchronized代码块时会锁定当前的对象,只有执行完该代码块才能释放该对象锁,下一个线程才能执行并锁定该对象。 所以若玩家1和玩家2同时使用丢炸弹方法,也不会产生混乱。

 

(2)读取用户信息时实现实现serializabel接口接口,serializabel接口的作用是就是可以把对象存到字节流,然后可以恢复,方便进行User对象的直接储存和读取。

 

(3)

 

测试用户场次信息的储存和读取,读取文件用户信息,游戏前a的胜率为87%,b为25%,令a胜,则a的胜率场次+1,b的sb场次+1,a的胜率增加,b的胜率减少,正确。

 

感想

通过这次面向对象课程设计,我对于多线程和Serializable 接口的理解和掌握更加深刻,特别是掌握了synchronized关键字的用法,使用synchronized关键字让两个玩家同时丢炸弹和炸弹爆炸时不会产生混乱,而虽然可以使用synchronized来定义方法,但synchronized并不属于方法定义的一部分,因此,synchronized关键字不能被继承。通过实现 java.io.Serializable 接口以启用序列化功能,未实现此接口的类将无法使其任何状态序列化或反序列化。此次课程设计,我们付出了很多的辛苦和精力,但是也学到了很多东西,不仅巩固了以前学过的知识,还学到了很多书本上没有的知识,同时我也懂得了理论与实际相结合的重要性,只有把学的理论知识与实践相结合,才能提高自己的实际动手能力和思考的能力,也深刻体会到了团队间合作的重要性。

posted @ 2020-01-09 22:19  适野  阅读(251)  评论(0编辑  收藏  举报