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

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

跳高大挑战

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

题目

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);
            }
        }
    }
}
目录
相关文章
|
域名解析 弹性计算 网络协议
阿里云轻量应用服务器使用教程(2023新版)
阿里云轻量应用服务器使用教程(2023新版)阿里云轻量应用服务器怎么使用?阿里云轻量应用服务器使用教程:轻量应用服务器购买、重置密码、远程连接、宝塔面板的Web环境搭建、WordPress网站程序安装到网站上线,阿里云服务器网分享轻量应用服务器从购买、配置建站环境、轻量服务器应用服务器远程连接、开端口到网站上线全流程
|
8月前
|
算法 API 网络架构
如何高效对接新加坡股票(SGX)实时数据 API
新加坡股市(SGX)汇聚优质REITs、DBS/UOB银行股及航运企业。本文详解如何用StockTV全球金融API(countryId=15)快速获取实时行情、K线、STI指数等数据,支持REST/WS,低门槛、高时效,助力东南亚金融产品开发。(239字)
|
9月前
|
弹性计算 安全 Linux
阿里云服务器镜像解析:公共、自定义、共享、云市场及社区镜像对比与选择参考
阿里云服务器ESC镜像包括公共、自定义、共享、云市场及社区五大类型,每种镜像具有不同的特性和适用场景。公共镜像安全稳定;自定义镜像量身定制,可快速部署;共享镜像可跨账号协作;云市场镜像一键部署,省时省心;社区镜像开放共享,满足个性化需求。选择镜像时,用户需考虑操作系统、初始配置、安全性、稳定性及成本。
|
4月前
|
存储 自然语言处理 供应链
2026年电商行业有哪些agent应用落地?主流客服agent产品推荐与实战案例
本文详解瓴羊四大智能体(Quick Service、Quick BI、Dataphin、Data Agent)在电商客服、营销、数据分析与战略决策中的实战应用,覆盖售前转化、售后挽回、实时归因、库存预测、数据治理及价格推演等场景,构建“感知—决策—行动—优化”智能闭环。(239字)
|
6月前
|
人工智能 JavaScript 安全
【新版本新人操作】OpenClaw 部署教程 2026 版操作指南
OpenClaw是一款AI聊天网关一键部署平台,支持多模型接入,自动检测安装依赖,无需技术基础。2026最新版仅需49.7MB空间,纯英文路径安装,含7×24小时技术支持。
|
8月前
|
人工智能 前端开发 开发者
拒绝夸大!AI编程工具真实使用体验(附案例)
开源、轻量、易部署的AI编程助手,支持Docker一键安装(1核2GB即可),适配Python/Vue/React等主流技术栈。本文以开发者视角分享其真实使用体验:含Excel批量分析、Vue3→React组件转译两大实操案例,并客观剖析优缺点,干货满满,无广告。
拒绝夸大!AI编程工具真实使用体验(附案例)
|
8月前
|
网络安全 文件存储 开发工具
飞牛NAS笔记本盒盖不休眠
笔记本装飞牛NAS(fnOS)后,合盖即休眠导致服务中断?本文详解如何通过修改`/etc/systemd/logind.conf`中`HandleLidSwitch*`参数为`ignore`,并重启`systemd-logind`,实现合盖不休眠——Web、Docker、下载全正常运行。安全、有效、一步到位!
1068 4
|
8月前
|
人工智能 数据可视化 安全
大模型微调评测入门指南:指标解析、实践操作与避坑宝典
当大模型完成微调后,如何科学地评估其效果,而非仅凭主观感觉判断“好不好”?许多人将大量精力投入调参,却在评测环节仅凭直觉,导致上线后问题频发。实际上,有效的评测是驱动模型持续优化的闭环起点。本文将系统梳理从分类到生成任务的评测方法论,结合关键指标解读与实战代码,带你走出“凭感觉”的误区,用数据驱动模型迭代。
|
8月前
|
编解码 监控 测试技术
FurMark_2.9.0.0_Win64安装步骤详解(附显卡烤机与温度测试教程)
FurMark 2.9.0.0 Win64 是专业显卡压力测试工具,用于满载烤机、检测温度与稳定性。支持实时监控FPS/温度/功耗,操作简单:管理员运行安装包→自定义路径→创建桌面快捷方式→启动后选择分辨率与AA即可开始测试,适合游戏玩家与硬件爱好者。(239字)
|
9月前
|
机器学习/深度学习 数据采集 人工智能
一文掌握AI时代的“造血”神技:合成数据实战
本文深入解析大模型落地核心瓶颈——高质量数据匮乏,并系统介绍“合成数据+微调”双引擎方案:从数据稀缺、隐私合规、标注成本等现实困境切入,详解合成数据原理(GAN/扩散模型)与微调机制,辅以Python实战四步法(生成→清洗→微调→评估),助力开发者低成本打造领域专属模型。
738 8