javaOOP实现跳高大挑战!手把手教你实现小游戏!

本文涉及的产品
实时计算 Flink 版,5000CU*H 3个月
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
大数据开发治理平台 DataWorks,不限时长
简介: javaOOP实现跳高大挑战!手把手教你实现小游戏!

2000元阿里云代金券免费领取,2核4G云服务器仅664元/3年,新老用户都有优惠,立即抢购>>>


阿里云采购季(云主机223元/3年)活动入口:请点击进入>>>,


阿里云学生服务器(9.5元/月)购买入口:请点击进入>>>,

跳高大挑战

由于该游戏复杂度稍高,笔者建议先学习猜拳游戏的文章之后再来完成该游戏。

题目

image.png

设计

image.png
常规:游戏主类,规则类
角色player类->因为有多个角色,所以应该有playData数据类
裁判judge类
根据题目要求需要体力能力比数据类

玩家:
属性:当前体力值,跳跃体力消耗率,能力峰值
跳跃行为:(跳跃高度、累计跳跃高度,累计跳跃次数),发现这三个量都与跳跃高度有关系,然后又发现求出跳跃高度的关键是求出率,所以就需要一个根据当前体力值获取率的方法。

体力能力比数据类:
数据:体力边界,体力能力比值(题目给的)
需要确定下界,所以需要有isMatched()方法

裁判:
单次裁决:add()方法灌输数据
多次裁决:排序

规则类:
可以先定义好常量和常规的方法(getxxx...)
一些具体的方法根据上述的需求分析再来确定。

结果呈现

image.png
image.png
image.png

运动员数据类

public class PlayerData implements Comparable<PlayerData>{
   
   
    /**
     * 玩家的数组下标
     */
    private int playerIndex;
    /**
     * 玩家的跳跃数据
     */
    private int count;



    public PlayerData(int playerIndex, int count) {
   
   
        this.playerIndex = playerIndex;
        this.count = count;
    }

    public int getPlayerIndex() {
   
   
        return playerIndex;
    }

    public int getCount() {
   
   
        return count;
    }


    @Override
    public int compareTo(PlayerData pd) {
   
   
        return count-pd.getCount();
    }
}

1 .数据类的定义:通常指包含一些get,set方法,同时也可能会实现Serialzable,Comparable等接口来实现数据类的可序列化和可比较特性,通常并不包含特殊方法。
2 .何时定义一个数据类?
数据类可以将不同种类的多个数据封装成为一个实体对象 ,当需要被多个模块或者方法使用的时候则可以定义数据类。(将数据提取出来,并进行操作)

体力能力比数据类

/**
 * 数据类:体力和能力的比值
 */
public class StrengthAbilityRatio {
   
   
    /**
     * 封装(超出基本类型范畴)
     * 体力边界值
     */
    private int strengthBound;
    /**
     * 当前体力边界值条件下所能发挥的能力比值
     */
    private float ratio;

    public StrengthAbilityRatio(int strengthBound, float ratio) {
   
   
        this.strengthBound = strengthBound;
        this.ratio = ratio;
    }

    /**
     * 没有用set的原因是 不能修改 构造器已经赋值了
     * 提取体力值边界
     * @return int
     */
    public int getStrengthBound() {
   
   
        return strengthBound;
    }

    /**
     * 提取当前体力值对应的比值
     * @return float
     */
    public float getRatio() {
   
   
        return ratio;
    }
    public boolean isMatched(float strength){
   
   
        return strength >= strengthBound;
    }
}

该类是在普通数据类的基础上添加了isMatched()方法便于将当前体力值与体力边界值的界限进行匹配,isMatched()方法本身是配合规则类中的getRatiosByStrength()方法一起使用的,通过当前体力值得到体力界限,通过体力界限和ratios数组得到跳跃高度比率。

规则类

public class Rule {
   
   
    private Scanner input;
    private Random rand;
    private final String NAME_REGEX = "^[\u4e00-\u9fa5]{2,}$";
    private final String DIGIT_REGEX = "^[0-9]{1,}$";
    /**
     * 体力和能力比值
     */
    private StrengthAbilityRatio[] ratios;
    public Rule(){
   
   
        ratios = new StrengthAbilityRatio[5];
        ratios[0] = new StrengthAbilityRatio(9,1);
        ratios[1] = new StrengthAbilityRatio(8,0.85f);
        ratios[2] = new StrengthAbilityRatio(6,0.7f);
        ratios[3] = new StrengthAbilityRatio(5,0.55f);
        ratios[4] = new StrengthAbilityRatio(0,0.3f);
        rand = new Random();//避免重复创建随机的工具对象
        input = new Scanner(System.in);
    }


    public String getInputName(String title){
   
   
        String name = null;
        System.out.printf("请输入"+title+"昵称:");
        do{
   
   
            name = input.nextLine();
            if(name.matches(NAME_REGEX))
                break;
            System.out.println("昵称必须为两个汉字以上的形式!请重新输入:");
        }while(true);
        return name;
    }

    /**
     * 根据用户剩余的体力值,获取能够发挥的能力值
     * @param strength 剩余体力
     * @return 比值
     */

    private float getRatioByStrength(float strength){
   
   
        for(StrengthAbilityRatio sar : ratios){
   
   
            if (sar.isMatched(strength)) {
   
   
                return sar.getRatio();
            }
        }
        return 0;
    }

    /**
     * 获取指定范围内的随机小数值
     * @param min 区间下限
     * @param max 区间上限
     * @return float
     */
    public float randomBetween(int min,int max){
   
   
        return min+rand.nextInt((max-min)*100)/100.0f;
    }

    /**
     * 获取控制台输入一个指定范围的整数
     * @param title
     * @param min
     * @param max
     * @return
     */
    public int getInputInt(String title,int min,int max){
   
   
        int digit = 0;
        do{
   
   
            System.out.print("请输入"+title+":");
            String num = input.next();
            if(num.matches(DIGIT_REGEX)){
   
   
                digit = Integer.parseInt(num);
                if(digit >= min && digit <= max){
   
   
                    break;
                }
                System.out.println("数值必须位于"+min+"~"+max+"之间,请重新输入:");
            }
        }while(true);
        return digit;
    }

    /**计算跳跃力和体力的消耗(选择合并为一个方法的原因:每次跳跃都会产生跳跃力消耗 并【伴随着】体力消耗 【伴随】说明【关联性】)
     * 注意临界:{
     *     如果达到了目标高度,但剩余体力为负值,说明剩余体力不足以支撑一次跳跃。
     *     如果要考虑此问题,只能按实际剩余的体力实施跳跃,而实际跳跃高度也成对应的比例关系。【临界问题】
     * }
     * @param strengthLeft 剩余体力
     * @param jumpAbility 跳跃力峰值
     * @param costRatio 体力消耗率
     * @return float[0]:实际的跳跃力 float[1]:体力的消耗值
     */
    public float[] getJumpAbilityAndStrengthCost(float strengthLeft,float jumpAbility,float costRatio){
   
   
        // 此处:strengthLeft 就是剩余体力,当剩余体力为0时,跳的高度肯定为0,但还没有达到目标高度
        float realJumpAbility = getRatioByStrength(strengthLeft)*jumpAbility;
        float realStrengthCost = realJumpAbility*costRatio;
        //如果实际消耗 超过跳前剩余的体力(最后一次的临界情况)
        if(strengthLeft < realStrengthCost){
   
   
            realJumpAbility *= strengthLeft / realStrengthCost;//比例关系!!!!!!
            realStrengthCost = strengthLeft;//只能消耗这么多的体力
        }
        return new float[] {
   
   realJumpAbility,realStrengthCost};
    }
}

关键点:在猜拳游戏中,笔者已经对getxxx方法的基本形式做出了总结,此处新增了一个常用方法,获取一个在一定范围内的随机小数。

public float randBetween(int min,int max){
   
   
    return min + (rand.randInt(max))*100/100.0f;
}

玩家类

public class Player extends Rule{
   
   
    /**
     * 昵称
     */
    private String nickName;
    /**
     * 能力峰值
     */
    private float jumpAbility;
    /**
     * 体力消耗率
     */
    private float strengthCostRatio;
    /**
     * 当期体力
     */
    private float currentStrength;
    public Player(){
   
   
        nickName = getInputName("运动员");
        jumpAbility = randomBetween(1,3);
        strengthCostRatio = randomBetween(0,1);
        currentStrength = randomBetween(5,10);
    }

    /**
     * 获取运动员姓名
     * @return String
     */
    public String getNickName() {
   
   
        return nickName;
    }

    /**
     * 根据目标高度实施跳跃并获取实际跳跃的次数
     * @param targetHeight 未知的东西选择入参 targetHeight 对运动员来说是未知的 在方法中不能改变的值一定要设置为常量
     * @return int 达到目标高度的次数 0:表示淘汰
     *                              >0: 表示成功
     */
    public int jump(final int targetHeight){
   
   
        int count = 0;
        float accJump = 0;
        /*存储累计跳跃的高度
          累计跳跃的高度小于targetHeight 是循环条件
         */
        while(accJump < targetHeight && currentStrength > 0){
   
   
            float [] arr = getJumpAbilityAndStrengthCost(currentStrength,jumpAbility,strengthCostRatio);
            // arr[0]表示单次跳跃高度,arr[1]单次表示消耗体力
            accJump += arr[0];
            currentStrength -= arr[1];
            count++;
            System.out.println(nickName+"本次跳了"+ arr[0] +"米 累计跳跃高度 " + accJump + "米 用了"+ count +" 次 剩余体力 "+currentStrength);
        }
        return currentStrength <=0 ? 0 : count;
    }


}

裁判类

public class Judge extends Rule {
   
   
    private PlayerData[] pds;// 运动员数据
    private int size;// 下标
    private int targetHeight = getInputInt("目标高度",10,15);//目标高度 目标高度的范围通过测试大概得出比较合适(有一定概率能够达到)的目标高度的范围
    public Judge(int size){
   
   //运动员数量
        pds = new PlayerData[size];
        this.size = 0;
    }

    /**获取目标高度
     * @return int 目标高度
     */

    public int getTargetHeight() {
   
   
        return targetHeight;
    }

    /**
     * 添加完成比赛的运动员的数据(此处的数据表示玩家相关联的下标和跳跃次数 如何使其相关联?---构造一个类和对象数组)
     * @param playerIndex 玩家下标
     * @param count 玩家跳跃的次数
     */
    public void add(int playerIndex,int count){
   
   
        pds[size++] = new PlayerData(playerIndex, count);
    }

    /**
     * 归并排序法 对所有玩家数据中的跳跃数据进行降序排序
     * @param pds
     * @param begin
     * @param end
     * @return
     */

    private PlayerData[] mergeSort(int begin,int end,PlayerData[] pds){
   
   
        if(begin == end){
   
   
            return new PlayerData[]{
   
   pds[begin]};
        }
        int mid = begin + (end - begin)/2;
        final PlayerData[] leftPds = mergeSort(begin, mid, pds);
        final PlayerData[] rightPds= mergeSort(mid + 1, end, pds);
        int left = 0,right = 0,mix = 0;
        PlayerData[] mixPds = new PlayerData[end-begin+1];
        while(left<leftPds.length && right<rightPds.length){
   
   
            mixPds[mix++] = leftPds[left].compareTo(rightPds[right]) < 0?leftPds[left++] : rightPds[right++];
        }
        while(left<leftPds.length){
   
   
            mixPds[mix++] = leftPds[left++];
        }
        while(right<rightPds.length){
   
   
            mixPds[mix++] = rightPds[right++];
        }
        return mixPds;
    }
    public PlayerData[] mergeSort(){
   
   
        return mergeSort(0,size-1,pds);
    }
    /**
     * 这里用一个无参的重载来调用mergeSort的原因是 mergeSort的其中一个参数是pds数组(运动员数据)
     * 而在外部调用的时候 是无法拿到运动员数据的 所以选择通过一个无参构造使得在JumpGame中能够进行排序
     */

}

1 .裁判的裁决可以分为单次裁决(在单次游戏进程之后,通过add()方法灌输运动员数据),多次裁决(进行排序),这点和猜拳游戏相类似。
2 .信息的权限:目标高度只有裁判知道,玩家并不知道。

游戏主类

public class JumpGame extends Rule {
   
   
    /**
     * 玩家信息
     */
    private Player[] players;
    /**
     * 裁判信息
     */
    private Judge judge;

    public JumpGame(){
   
   
        final int playerNum = getInputInt("运动员数量",1,10);
        players = new Player[playerNum];
        for (int i = 0; i < playerNum; i++) {
   
   //利用遍历型循环的目的 是能够通过下标进行关联型地存储数据
            players[i] = new Player();
        }
        judge = new Judge(playerNum);
    }
    public void start() {
   
   
        System.out.println("跳跃比赛开始...");
        for (int i = 0; i < players.length; i++) {
   
   //遍历型循环 使下标能够存入PlayerData的数据类中
            Player player = players[i];
            System.out.println("-------" + player.getNickName() + " 开始跳跃-------");
            final int count = player.jump(judge.getTargetHeight());
            System.out.println("-------" + player.getNickName() + " 完成跳跃-------\n");
            judge.add(i, count);
        }
        System.out.println("------- 最终结果 -------");
        PlayerData[] pds = judge.mergeSort();
        int rank = 0,index = 0;
        final Player[] eliminatedPlayers = new Player[pds.length];
        for(PlayerData pd : pds){
   
   
            if(pd.getCount() > 0){
   
   
                rank++;
                Player player = players[pd.getPlayerIndex()];//通过玩家的下标 获取玩家的信息 从而才能输出结果的nickName等信息
                String rst = "跳跃"+pd.getCount()+"次";
                System.out.println("第"+rank+"名\t"+player.getNickName()+"\t"+rst);
            }else{
   
   
                eliminatedPlayers[index++] = players[pd.getPlayerIndex()];
            }
        }
        for(Player player:eliminatedPlayers){
   
   
            if(null != player){
   
   
                String rst = "淘汰";
                System.out.println("       \t"+player.getNickName() + "\t"+rst);
            }
        }
    }
}
目录
相关文章
|
7月前
|
人工智能 搜索推荐 开发者
|
12月前
|
机器学习/深度学习 存储 自然语言处理
不讲废话普通人了解 ChatGPT——基础篇第一课
不知道大家在第一次会使用 ChatGPT 并尝试和他对话时有没有感到震惊。当ChatGPT首次推出时,我立即被它的功能所吸引。
173 0
|
开发工具
想学做游戏到底该怎么学
嗨!大家好,我是小蚂蚁。 遇到过很多想学习做游戏却又不得章法的人,有些人可能只是有个想法,有些人真的付诸了行动。但是大部分人最终都是以失败而告终的,不是说最终没有做出来一个游戏,而是连第一步的门槛也没迈的过去。 做游戏做了这么多年,也教了不少的学员,我觉得我至少有一定的经历,可以来说一下,想学习做游戏到底该怎么学。
164 0
|
小程序 索引
如何做一个塔防小游戏(二)
为了方便设置炮塔的位置,可以使用一个精灵图片作为标识,图中我使用了黄色的五角星作为炮塔位置的标识,可以在场景区中直接拖拽五角星对炮塔的位置进行设置。然后在游戏启动后我们将使用炮塔创建按钮来替换这些五角星的位置。
77 0
|
小程序 索引
如何做一个塔防小游戏(四)
嗨!大家好,我是小蚂蚁。今天我们继续分享制作一个塔防小游戏的第四节,如何实现炮塔的升级和出售功能。 在炮塔升级时,我们简单地做一些属性的提升以及外表的变化,例如当炮塔升级后,攻击速度提升,攻击范围增大,炮塔变成红色的。
80 0
|
弹性计算 Kubernetes 关系型数据库
初入阿里云,上手走一波
简述个人体验的阿里云产品及相关的操作内容,包括ECS、Mysql、DMS、OSS、Linux等
初入阿里云,上手走一波
|
数据可视化 Windows
推荐5款实用小工具,第五款更是小白最爱
作为一个黑科技软件爱好者,电脑里肯定是不会缺少这方面的东西,今天的5款实用小工具闪亮登场了。
170 0
推荐5款实用小工具,第五款更是小白最爱
|
移动开发 小程序 JavaScript
手撸一个在线学习在线教育小程序
最近有小伙伴找小孟开发了一个在线教育的小程序项目。
124 0
手撸一个在线学习在线教育小程序
|
前端开发 Java Serverless
玩转《天猫精灵技能平台》,搞一个诗词问答小游戏
看了中国诗词大会,必须要自己搞一个诗词问答小游戏。 文章最后还有一个思路,非常简单的思路,大家可以试着去实现一下。
13822 5
玩转《天猫精灵技能平台》,搞一个诗词问答小游戏
|
Web App开发 存储 弹性计算
阿里云体验与感悟
简介:?本次是对阿里云的初步了解,包括对云服务器的相关术语包括实例,镜像,安全组等,在阿里云里进行本实例磁盘,重置密码,实例详情等一系列操作,对服务器已经有了一个基本的认识,学会了简单的搭建服务器和部署项目,对当下互联网的发展有了更为清晰的认识。
http://www.vxiaotou.com