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

news2024/11/18 6:24:04

导航:从零开始手写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上点个星星,谢谢!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

学习数仓工具 dbt

DBT 是一个有趣的工具&#xff0c;它通过一种结构化的方式定义了数仓中各种表、视图的构建和填充方式。 dbt 面相的对象是数据开发团队&#xff0c;提供了如下几个最有价值的能力&#xff1a; 支持多种数据库通过 select 来定义数据&#xff0c;无需编写 DML构建数据时&#…

Facebook的未来蓝图:数字社交的下一个篇章

在数字化时代&#xff0c;社交媒体已经成为人们日常生活中不可或缺的一部分。而在众多的社交媒体平台中&#xff0c;Facebook一直处于领先地位&#xff0c;不断探索着数字社交的新领域和新形式。随着科技的不断发展和社会的不断变革&#xff0c;Facebook正在谱写着数字社交的未…

Vue路由缓存问题

路由缓存问题的产生 VueRouter允许用户在页面中创建多个视图&#xff08;多级路由&#xff09;&#xff0c;并根据路由参数来动态的切换视图。使用带参数的路由时&#xff0c;相同的组件实例将被重复使用。因为两个路由都渲染同一个组件&#xff0c;比起销毁再创建&#xff0c;…

谷歌掀桌子!开源Gemma:可商用,性能超过Llama 2!

2月22日&#xff0c;谷歌在官网宣布&#xff0c;开源大语言模型Gemma。 Gemma与谷歌最新发布的Gemini 使用了同一架构&#xff0c;有20亿、70亿两种参数&#xff0c;每种参数都有预训练和指令调优两个版本。 根据谷歌公布的测试显示&#xff0c;在MMLU、BBH、GSM8K等主流测试…

数据结构·顺序表

1数据结构简介 学习数据结构与算法之前&#xff0c;一般是先学数据结构&#xff0c;方便之后学习算法&#xff0c;那么数据结构拆开介绍&#xff0c;就是数据 和 结构&#xff0c;数据&#xff0c;生活中到处都是&#xff0c;结构&#xff0c;就是数据存储的方式&#xff0c;即…

React 事件处理 ( this问题 参数传递 ref)

React事件的命名采用小驼峰方式&#xff08;cameCase&#xff09;,而不是小写 使用JSX语法时你需要传入一个函数作为事件处理函数&#xff0c;而不是一个字符串 你不能通过返回false 的方式阻止默认行为。你必须显示式的使用preventDefault 1 this 需要谨慎对待JSX回调函数中的…

第13讲实现自定义logout处理

默认logout请求实现是有状态的&#xff0c;返回到login请求页面&#xff1b;我们现在是前后端分离处理&#xff0c;所以需要自定义实现logout 新建JwtLogoutSuccessHandler /*** 自定义Logout处理* author java1234_小锋 &#xff08;公众号&#xff1a;java1234&#xff09;…

camunda源代码编译运行(二):构建并运行camunda源代码工程

接上一篇文章&#xff1a;camunda源代码编译运行&#xff08;一&#xff09;&#xff1a;下载编译camunda源代码 Camunda 7.19源代码一共有178个maven工程和1个angular前端工程&#xff0c;这么多工程中包括了大量的QA测试包、JDK不同版本适配&#xff08;比如&#xff1a;Jav…

Vue3之ref与reactive的基本使用

ref可以创建基本类型、对象类型的响应式数据 reactive只可以创建对象类型的响应式数据 接下来让我为大家介绍一下吧&#xff01; 在Vue3中&#xff0c;我们想让数据变成响应式数据&#xff0c;我们需要借助到ref与reactive 先为大家介绍一下ref如何使用还有什么注意点 我们需…

【快速搞定Webpack5】处理样式资源(三)

本次内容我们将学习使用webpack如何处理css、less、sass、scss等样式资源 介绍 webpack本身是不能识别样式资源的&#xff0c;所以我们需要借助loader包来帮助webpack解析样式资源 我们找loader都应该去官方文档中查找对应的loader&#xff0c;然后学习使用。 官方文档找不到…

windows server设置桌面显示此电脑

我开发的chatgpt网站&#xff1a; https://chat.xutongbao.top

Sora - 探索AI视频模型的无限可能-官方报告解读与思考

一、引言 最近SORA火爆刷屏&#xff0c;我也忍不住找来官方报告分析了一下&#xff0c;本文将深入探讨OpenAI最新发布的Sora模型。Sora模型不仅仅是一个视频生成器&#xff0c;它代表了一种全新的数据驱动物理引擎&#xff0c;能够在虚拟世界中模拟现实世界的复杂现象。本文将重…

内网穿透——NPS突然无法连接

温馨提示 &#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f32d;&#x1f32d;&#x1f32d;&#x1f32d;&#x1f32d;&#x1f32d;&#x1f32d;❤️❤️❤️❤️❤️❤️❤️&#x1f968;&#x1f968;&#x1f9…

JavaSprintBoot中一些运维方面的知识

1.配置文件四级分类 例如以下yml配置文件&#xff0c;权限一共有四级&#xff0c;高等级覆盖低等级并叠加&#xff08;权限向下兼容&#xff09; 2.自定义配置文件 可以自定义配置文件的名称&#xff0c;因为实际开发环境中可能不会就简单的叫做application.yml之类的&#x…

《穿越科技的前沿:计算机专业必看的电影盛宴》

文章目录 每日一句正能量前言电影推荐推荐一&#xff1a;《黑客帝国》推荐二&#xff1a;《社交网络》推荐三&#xff1a;《源代码》推荐四&#xff1a;《谍影重重》系列推荐五&#xff1a;《旋转木马》 技术与主题后记 每日一句正能量 一个人的一生&#xff0c;就是一座有了年…

【Linux】MySQL数据库的使用

【Linux】MySQL数据库的使用 一、访问MySQL数据库二、创建及删除库和表1、创建新的库2、创建新的表3、删除一个数据表4、删除一个数据库 三、管理表中的数据记录1、插入数据记录2、查询数据记录3、修改数据记录4、删除数据记录 四、数据库用户授权1、授予权限2、查看权限3、撤销…

每日OJ题_二叉树dfs④_力扣98. 验证二叉搜索树

目录 力扣98. 验证二叉搜索树 解析代码 力扣98. 验证二叉搜索树 98. 验证二叉搜索树 难度 中等 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左子树只包含 小于 当前节点的数。节点的右子树…

仿12306校招项目-项目业务和架构

目录 业务图 用户管理 业务难点 1. 如何确定用户注册信息的真实性 2. 面对亿级用户量 3. 支持多种登录方式会造成读请求扩散&#xff0c;需要解决用户定位问题 4. 高并发场景下缓存穿透问题需要有效解决&#xff0c;避免数据库压力过大 5. 明文存储用户敏感信息会造成安…

深度学习基础——SSD目标检测

SSD网络介绍 使用多个特征图作为特征预测层。 SSD (Single Shot MultiBox Detector)于2016年提出。当网络输入为300300大小时&#xff0c;在VOC2007测试集上达到74.3%的mAP;当输入是512512大小时&#xff0c;达到了76.9%的mAP SSD_Backbone部分介绍 不变的部分 特征提取网…

H5移动端文件预览pdf

H5移动端文件预览pdf 需求&#xff1a;H5页面嵌入浙政钉&#xff0c;需要文件预览Pdf。 试用了多个插件&#xff0c;踩了很多坑&#xff0c;如果小伙伴有类似填坑经历&#xff0c;并成功解决&#xff0c;感谢留言指点&#xff01;&#xff01;&#xff01; 先讲最终方案&#x…