从零开始手写mmo游戏从框架到爆炸(二十一)— 战斗系统二

导航:从零开始手写mmo游戏从框架到爆炸(零)—— 导航-CSDN博客    

        上一章(从零开始手写mmo游戏从框架到爆炸(二十)— 战斗系统一-CSDN博客)我们只是完成了基本的战斗,速度属性并没有真正的发生作用。现在我们加入速度属性。上一章我们说过,比如速度是1000的时候是每隔2秒钟攻击一次,但是服务器不能真的等两秒再计算攻击的结果,那么这个战斗的时长将会超过几分钟,用户也可能等这么久。那么这里要解决几个问题:

        第一个就是速度和出手间隔的换算,我们使用一个比较简单的公式,就是

interval = 500 + (int) (((1 - (speed) * 1.0 / (2000 + speed)) * (1 - (speed) * 1.0 / (2000 + speed))) * 5000);

        这样可以保证最短的出手时间是500,最长也不会超过5000。

       第二个问题就是根据速度插入到队列的问题,首先我们看下对于LinkedList队列的插入demo

public class Main {
    public static void main(String[] args) {
        LinkedList<Integer> queue = new LinkedList<>(); // 创建一个空的队列

        // 添加初始元素
        for (int i = 1; i <= 5; i++) {
            queue.addLast(i);
        }

        System.out.println("原始队列:" + queue);

        int targetIndex = 2; // 目标索引为2(从0开始计算)
        int elementToInsert = 99; // 要插入的元素值

        ListIterator<Integer> iterator = queue.listIterator();
        while (iterator.hasNext()) {
            if (targetIndex == 0) {
                iterator.next(); // 跳过第一个元素
                break;
            } else {
                iterator.next();
                targetIndex--;
            }

            if (!iterator.hasNext() && targetIndex > 0) {
                throw new IndexOutOfBoundsException("目标索引超出了队列长度");
            }
        }

        iterator.add(elementToInsert); // 在指定位置插入新元素

        System.out.println("插入元素后的队列:" + queue);

    }
}

运行后结果如下:

原始队列:[1, 2, 3, 4, 5]
插入元素后的队列:[1, 2, 3, 99, 4, 5]

那么根据这个方法我们来尝试改造战斗引擎。

       首先Action接口中增加一个interval()的方法,用于获取时间间隔,这个时间间隔是预计攻击时间距离战斗开始时间的间隔,例如计算出来的攻击间隔是500,那么每次计算的结果就是500,1000,1500,2000...以此类推。

public interface Action {

    boolean run();

    /***
     * 是否继续
     * @return
     */
    boolean checkContinue();

    int speed();

    /***
     *
     * @return
     */
    int intervalTime();
}

   同时创建一个抽象类来抽象部分功能:

public abstract class Attack implements Action{

    private int intervalTime;

    private int speed;

    public int getIntervalTime() {
        return intervalTime;
    }

    public void setIntervalTime(int intervalTime) {
        this.intervalTime = intervalTime;
    }

    public int getSpeed() {
        return speed;
    }

    public void setSpeed(int speed) {
        this.speed = speed;
    }

    @Override
    public int intervalTime() {
        return intervalTime;
    }

    @Override
    public int speed() {
        return speed;
    }

    public int computeInterval(int speed) {
        return 500 + (int) (((1 - (speed) * 1.0 / (2000 + speed))
                * (1 - (speed) * 1.0 / (2000 + speed))) * 5000);
    }
}

         修改 GroupAttack 在创建的时候要不是速度和间隔两个字段

public class GroupAttack extends Attack {

    private Hero heroA;

    private List<Hero> defenceList;

    public GroupAttack(Hero heroA, List<Hero> defenceList) {
        this.heroA = heroA;
        this.defenceList = defenceList;
        setIntervalTime(computeInterval(heroA.getSpeed()));
        setSpeed(heroA.getSpeed());
    }

    @Override
    public boolean run() {
        // 自己血量少于0 返回
        if(heroA.getHp() > 0) {        // 遍历并找到血量最少的攻击
            defenceList = defenceList.stream().filter(e -> e.getHp() > 0).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(defenceList)) {
                defenceList.sort(Comparator.comparing(Hero::getHp));
                heroA.attack(defenceList.get(0));
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean checkContinue() {
        return heroA.getHp() > 0 && defenceList.stream().anyMatch(e -> e.getHp() > 0);
    }

}

 最后我们再创建一个战斗服务:

public class BattleManyToManyTwo {

    // 队列不变
    private final LinkedList<Attack> actions = new LinkedList<>();

    private int addAction(Attack action){
        actions.offer(action);
        return actions.size();
    }

    public void fight(List<Hero> listA, List<Hero> listB) throws InterruptedException {
        // 先初始化
        listA.sort(Comparator.comparing(Hero::getSpeed).reversed());
        for (int i = 0; i < listA.size(); i++) {
            addAction(new GroupAttack(listA.get(i),listB));
        }

        // 再放入listB
        listB.sort(Comparator.comparing(Hero::getSpeed).reversed());
        for (int i = 0; i < listB.size(); i++) {
            GroupAttack attack = new GroupAttack(listB.get(i), listA);
            insertAction(attack);
        }

        // 如果A集合和B集合的生命值都还大于0

        while(listA.stream().anyMatch(e -> e.getHp() > 0) && listB.stream().anyMatch(e -> e.getHp() > 0)) {
            Attack pop = actions.pop();
            boolean run = pop.run();
            if(run) {
                // 再放进去
                if (pop.checkContinue()) {
                    // 要重新计算interval的时间
                    pop.setIntervalTime(pop.getIntervalTime() + pop.computeInterval(pop.speed()));
                    insertAction(pop);
                }
                // 打印
                System.out.println("A集团 :" + JSON.toJSONString(listA));
                System.out.println("B集团 :" + JSON.toJSONString(listB));
            }

        }

        if(listA.stream().anyMatch(e -> e.getHp() > 0)) {
            System.out.println("A集团 获胜:" + JSON.toJSONString(listA));
        }else{
            System.out.println("B集团 获胜:" + JSON.toJSONString(listB));
        }
    }

    private void insertAction(Attack attack) {
        int intervalTime = attack.getIntervalTime();

        // 如果第一个就大于attack的interval
        if(actions.get(0).getIntervalTime() > attack.intervalTime()){
            // 在头插入一个
            actions.push(attack);
        }
        else {
            ListIterator<Attack> iterator = actions.listIterator();
            while (iterator.hasNext()) {
                Attack next = iterator.next();

                if (next.getIntervalTime() > intervalTime) {
                    break;
                }
            }
            // 在指定位置插入新元素
            iterator.add(attack);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        BattleManyToManyTwo battle = new BattleManyToManyTwo();

        Hero A = new Hero("A");
        Hero B = new Hero("B");
        B.setSpeed(2000);
        B.setAttack(20);

        Hero C = new Hero("C");
        C.setSpeed(500);
        C.setAttack(20);

        Hero D = new Hero("D");
        D.setSpeed(10);
        D.setAttack(15);
        battle.fight(Arrays.asList(A,C),Arrays.asList(B,D));
    }

}

 运行main方法,查看效果:

B攻击,C生命值减少20
B攻击,C生命值减少20
C攻击,B生命值减少20
A攻击,B生命值减少10
B攻击,C生命值减少20
D攻击,C生命值减少15
C攻击,B生命值减少20
B攻击,C生命值减少20
B攻击,C生命值减少20
B攻击,A生命值减少20
A攻击,B生命值减少10
D攻击,A生命值减少15
B攻击,A生命值减少20
B攻击,A生命值减少20
B攻击,A生命值减少20
A攻击,B生命值减少10
D攻击,A生命值减少15
B集团 获胜:[{"attack":20,"hp":30,"name":"B","speed":2000},{"attack":15,"hp":100,"name":"D","speed":10}]

Process finished with exit code 0

全部源码详见:

gitee : eternity-online: 多人在线mmo游戏 - Gitee.com

分支:step-11

请各位帅哥靓女帮忙去gitee上点个星星,谢谢!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/404851.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

前端数据可视化:ECharts使用

可视化介绍 ​  ​  应对现在数据可视化的趋势&#xff0c;越来越多企业需要在很多场景(营销数据&#xff0c;生产数据&#xff0c;用户数据)下使用&#xff0c;可视化图表来展示体现数据&#xff0c;让数据更加直观&#xff0c;数据特点更加突出。   ​  数据可视化主要目…

读取7400MB/s!华为发布eKitStor Xtreme M.2闪存条

今日&#xff0c;华为举行数据存储新春新品发布会&#xff0c;不仅发布全新数据湖解决方案&#xff0c;华为还针对商业市场与分销市场发布了全闪存存储新品。 据介绍&#xff0c;面向游戏加速、影视编辑、户外作业等场景&#xff0c;华为发布eKitStor Xtreme系列高性能M.2闪存条…

Leetcode3035. 回文字符串的最大数量

Every day a Leetcode 题目来源&#xff1a;3035. 回文字符串的最大数量 解法1&#xff1a;哈希 排序 由于可以随意交换字母&#xff0c;先把所有字母都取出来&#xff0c;然后考虑如何填入各个字符串。 如果一个奇数长度字符串最终是回文串&#xff0c;那么它正中间的那…

一文读懂Linux内核中的Device mapper映射机制

一、 简介 本文总结Device mapper的映射机制。Device mapper是Linux2.6内核中提供的一种逻辑设备到物理设备的映射框架机制&#xff0c;在该机制下&#xff0c;用户可以很方便的根据自己的需要指定实现存储资源的管理策略&#xff0c;当前比较流行的Linux的逻辑卷管理器比如&a…

轻松打造智能化性能测试监控平台:【JMeter+Grafana+Influxdb】的优化整合方案

在当前激烈的市场竞争中&#xff0c;创新和效率成为企业发展的核心要素之一。在这种背景下&#xff0c;如何保证产品和服务的稳定性、可靠性以及高效性就显得尤为重要。 而在软件开发过程中&#xff0c;性能测试是一项不可或缺的环节&#xff0c;它可以有效的评估一个系统、应…

igolang学习3,golang 项目中配置gin的web框架

1.go 初始化 mod文件 go mod init gin-ranking 2.gin的crm框架 go get -u github.com/gin-gonic/gin 3.go.mod爆红解决

渗透测试之RCE漏洞

RCE&#xff08;remote command execute&#xff09;远程命令执行。应用程序的某些功能需要调用可以执行的系统命令的函数&#xff0c;如果这些函数或者函数的参数被用户控制&#xff0c;就可能通过命令连接符将恶意的命令拼接到函数中&#xff0c;从而执行系统命令。 常见的命…

WordPress使用

WordPress功能菜单 仪表盘 可以查看网站基本信息和内容。 文章 用来管理文章内容&#xff0c;分类以及标签。编辑文章以及设置分类标签&#xff0c;分类和标签可以被添加到 外观-菜单 中。 分类名称自定义&#xff1b;别名为网页url链接中的一部分&#xff0c;最好别设置为中文…

mybatis 集成neo4j实现

文章目录 前言一、引入jar包依赖二、配置 application.properties三、Mybatis Neo4j分页插件四、Mybatis Neo4j自定义转换器handler五、MybatisNeo4j代码示例总结 前言 MyBatis是一个基于Java语言的持久层框架&#xff0c;它通过XML描述符或注解将对象与存储过程或SQL语句进行…

【C++私房菜】面向对象中的多重继承以及菱形继承

文章目录 一、多重继承1、多重继承概念2、派生类构造函数和析构函数 二、菱形继承和虚继承2、虚继承后的构造函数和析构函数 三、has-a 与 is-a 一、多重继承 1、多重继承概念 **多重继承&#xff08;multiple inheritance&#xff09;**是指从多个直接基类中产生派生类的能力…

Open CASCADE学习|绘制砂轮

今天绘制一个砂轮&#xff0c;其轮廓由两条直线段和两段圆弧构成&#xff0c;圆弧分别与直线相切&#xff0c;两条圆弧之间相交而非相切。建模思路是&#xff1a;先给定两条直线段的起始点及长度&#xff0c;画出直线段&#xff0c;然后给定其中一圆弧的半径及圆心角&#xff0…

Elastic Stack--02--核心概念、倒排索引

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 1.核心概念1.1 索引1.2 类型1.3 文档1.4 字段 2.倒排索引此处ID是唯一标识 具体拆解的案例 1.核心概念 mysqlESDatabases 数据库索引 indicesTable 数据表类型 Type…

探索D咖智能饮品机器人的工作原理:科技、材料与设计的相互融合

智能饮品机器人是近年来随着人工智能和自动化技术的发展而崭露头角的一种创新产品。它将科技、材料和设计相互融合&#xff0c;为消费者带来了全新的饮品体验。下面D咖来探索智能饮品机器人的工作原理&#xff0c;以及科技、材料和设计在其中的作用。 首先&#xff0c;智能饮品…

抖音小店无货源真的靠谱吗?发展前景如何?2024年值得做吗?

大家好&#xff0c;我是电商花花。 我们通常说的抖音小店无货源就是利用产品之间的信息差、利润差来赚取商品的差价。 无货源模式就是即使没有货源&#xff0c;也能做抖音小店&#xff0c;前期店铺起店&#xff0c;我们需要大量的出单量和数据&#xff0c;我们才能快速把店铺…

Spring Boot应用集成Actuator端点自定义Filter解决未授权访问的漏洞

一、前言 我们知道想要实时监控我们的应用程序的运行状态&#xff0c;比如实时显示一些指标数据&#xff0c;观察每时每刻访问的流量&#xff0c;或者是我们数据库的访问状态等等&#xff0c;需要使用到Actuator组件&#xff0c;但是Actuator有一个访问未授权问题&#xff0c;…

QT基本组件

四、基本组件 Designer 设计师&#xff08;重点&#xff09; Qt包含了一个Designer程序&#xff0c;用于通过可视化界面设计开发界面&#xff0c;保存文件格式为.ui&#xff08;界面文件&#xff09;。界面文件内部使用xml语法的标签式语言。 在Qt Creator中创建文件时&#xf…

蓝桥杯C++竞赛常用库函数介绍

文章目录 前言一、二分查找1. 二分查找的前提2.binary_search函数3.lower_bound函数和upper_bound函数4.蓝桥杯例题 二、最值查找1. min和max函数2.min_element和max_element函数3.nth_element函数4.蓝桥杯例题 三、排序1.sort函数2.sort自定义比较函数,或lambda表达式(匿名函数…

金和OA UploadFileBlock接口任意文件上传漏洞

声明 本文仅用于技术交流&#xff0c;请勿用于非法用途 由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;文章作者不为此承担任何责任 1. 产品简介 金和数字化智能办公平台&#xff08;简称JC6&#xff09;是…

运维SRE-06 阶段性复习软件管理体系

那些年运维必会操作-第一弹 操作 文件&#xff1a;增删改查 增&#xff1a;touch,vim,>,>>,cp删除&#xff1a;rm修改&#xff1a;内容&#xff1a;vi/vim,>,>> 文件名&#xff1a;mv查看&#xff1a;内容&#xff1a;cat/vim/less/more/head/tail/sed/awk/…

编写LLVM Pass的一个小问题

在阅读官方文档时发现一个很有趣的细节&#xff0c;官方给出了一个测试用例&#xff0c;此处有一个小问题&#xff08;%无法复制&#xff09;。但是我在使用自己编译的ll文件时&#xff0c;我发现该pass无法正常使用。最后经过测试发现是利用-O0编译产生的ll文件有optnone的fla…