从零开始手写mmo游戏从框架到爆炸(二十一)— 战斗系统二
导航:从零开始手写mmo游戏从框架到爆炸(零)—— 导航-CSDN博客
上一章(从零开始手写mmo游戏从框架到爆炸(二十)— 战斗系统一-CSDN博客)我们只是完成了基本的战斗,速度属性并没有真正的发生作用。现在我们加入速度属性。上一章我们说过,比如速度是1000的时候是每隔2秒钟攻击一次,但是服务器不能真的等两秒再计算攻击的结果,那么这个战斗的时长将会超过几分钟,用户也可能等这么久。那么这里要解决几个问题:
第一个就是速度和出手间隔的换算,我们使用一个比较简单的公式,就是
这样可以保证最短的出手时间是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;}@Overridepublic int intervalTime() {return intervalTime;}@Overridepublic 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());}@Overridepublic 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;}@Overridepublic 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));}// 再放入listBlistB.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集合的生命值都还大于0while(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的intervalif(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上点个星星,谢谢!
相关文章:
从零开始手写mmo游戏从框架到爆炸(二十一)— 战斗系统二
导航:从零开始手写mmo游戏从框架到爆炸(零)—— 导航-CSDN博客 上一章(从零开始手写mmo游戏从框架到爆炸(二十)— 战斗系统一-CSDN博客)我们只是完成了基本的战斗,速度属性并没有…...
强化学习入门(Matlab2021b)-创建环境【3】
目录 1 前言2 根据类模板创建自定义环境2.1 创建类模板2.2 Environment properties(环境特性)2.3 Required Functions(需要的环境方法)2.3.1 Constructor function(构造函数)2.3.2 reset function2.3.3 step function2.4 Optional Functions(可选的环境方法)2.5 Environment Vi…...
C++如何避免float误差?
C如何避免float误差? 在开始前我有一些资料,是我根据网友给的问题精心整理了一份「c的资料从专业入门到高级教程」, 点个关注在评论区回复“888”之后私信回复“888”,全部无偿共享给大家!!! …...
深信服技术认证“SCCA-C”划重点:深信服超融合HCI
为帮助大家更加系统化地学习云计算知识,高效通过云计算工程师认证,深信服特推出“SCCA-C认证备考秘笈”,共十期内容。“考试重点”内容框架,帮助大家快速get重点知识 划重点来啦 *点击图片放大展示 深信服云计算认证(S…...
Vue3路由元信息
路由元信息即定义路由时的meta信息 使用路由元信息定义页面在浏览器显示的标题 定义路由 const router createRouter({history:createWebHistory(import.meta.env.BASE_URL),routes:[{path:"/",component:()>import("/components/Login.vue"),meta:{…...
实用区块链应用:去中心化投票系统的部署与实施
一、需求分析背景 随着技术的发展,传统的投票系统面临着越来越多的挑战,如中心化控制、透明度不足和易受攻击等问题。为了解决这些问题,我们可以利用区块链技术去中心化、透明性和安全性来构建一个去中心化投票系统。这样的系统能够确保投票过…...
Flink中的双流Join
1. Flink中双流Join介绍 Flink版本Join支持类型Join API1.4innerTable/SQL1.5inner,left,right,fullTable/SQL1.6inner,left,right,fullTable/SQL/DataStream Join大体分为两种:Window Join 和 Interval Join 两种。 Window Join又可以根据Window的类型细分为3种…...
协程源码 launch 流程跟踪学习
为了更深入学习协程的底层实现原理,了解协程线程切换的根本本质。也为了以后在工作中可以根据不同的需求场景,更加随心所欲的使用不同的协程。 今天通过 launch 跟踪一下协程的执行流程。 fun getData() {Trace.beginSection("getData");Log.…...
苍穹外卖Day02——总结2
前期文章 文章标题地址苍穹外卖Day01——总结1https://blog.csdn.net/qq_43751200/article/details/135466359?spm1001.2014.3001.5501苍穹外卖Day01——解决总结1中存在的问题https://lushimeng.blog.csdn.net/article/details/135473412 总结2 前期文章1. 新增员工模块1.1 …...
探索Nginx:一款高效、稳定的Web服务器和反向代理工具
在网站性能优化和架构设计中,Nginx以其高性能、低资源消耗和良好的扩展性成为了许多开发者和服务器管理员的首选。本文将为您详细介绍Nginx的概念、特点、安装、配置和使用,帮助您更好地了解并运用这款优秀的工具。 一、Nginx简介 Nginx(发…...
文案改写软件,高效改文案的方法
在互联网飞速发展的今天,文案的重要性在营销和传播中变得越发突出。而高质量的文案内能够吸引人们的注意力,打动他们的心弦,这样更能促使他们产生需求。然而,想要写一篇引人入胜的文案并不容易,需要时间和经验来调整和…...
黑马头条-day10
文章目录 app端文章搜索1、文章搜索1.1 ElasticSearch环境搭建1.2 索引库创建①需求分析②ES导入数据场景分析③创建索引和映射 1.3 索引数据同步①app文章历史数据导入ES②文章实时数据导入ES 1.4 文章搜索多条件复合查询①关键词搜索②搜索接口定义 2、搜索历史记录2.1 需求说…...
C++的stack容器->基本概念、常见接口
#include<iostream> using namespace std; #include <stack> //栈stack容器常用接口 void test01() { //创建栈容器 栈容器必须符合先进后出 stack<int> s; //向栈中添加元素,叫做 压栈 入栈 s.push(10); s.push(20); s…...
VUE中引入外部jquery.min.js文件
jquery官网:https://jquery.com/ cdn链接:https://code.jquery.com/jquery-3.7.1.js <template><div class"">测试jq<div id"jq">这是一个盒子</div></div> </template><script> import…...
MongoDB聚合运算符:$avg
$avg运算符返回给定数值的平均值 $avg可用于以下阶段: $addFields阶段(从MongoDB 3.4开始可用)$bucket阶段$bucketAuto阶段$group阶段包含$expr表达式的$match阶段$project阶段$replaceRoot阶段(从MongoDB 3.4开始可用)$replaceWith阶段(从MongoDB 4.2开始可用)$s…...
Web 前端 UI 框架Bootstrap简介与基本使用
Bootstrap 是一个流行的前端 UI 框架,用于快速开发响应式和移动设备优先的网页。它由 Twitter 的设计师和工程师开发,现在由一群志愿者维护。Bootstrap 提供了一套丰富的 HTML、CSS 和 JavaScript 组件,可以帮助开发者轻松地构建和定制网页和…...
【Python笔记-设计模式】惰性评价模式
一、说明 将某些对象的创建或计算延迟到真正需要它们的时候,以减少不必要的资源消耗和提高性能。 惰性评价在Python中实现也成为生成器,一般通过yield关键字实现。 (一) 解决问题 在处理大量数据时,使用惰性加载可以避免一次性加载所有数…...
每日学习总结20240221
每日总结 20240221 花自飘零水自流。一种相思,两处闲愁。 —— 李清照「一剪梅红藕香残玉簟秋」 1. stat 在Linux中,stat 是一个用于显示文件或文件系统状态的命令行工具。它提供了关于文件的详细信息,包括文件类型、权限、大小、所有者、修…...
学生成绩管理系统(C语言课设 )
这个学生成绩管理系统使用C语言编写,具有多项功能以方便管理学生信息和成绩。首先从文件中读取数据到系统中,并提供了多种功能(增删改查等)选项以满足不同的需求。 学生成绩管理系统功能: 显示学生信息增加学生信息删除学生信息…...
ChatGPT提示词(最新)
它能干什么? 包括但不限于: 类别描述学术论文它可以写各种类型的学术论文,包括科技论文、文学论文、社科论文等。它可以帮助你进行研究、分析、组织思路并编写出符合学术标准的论文。创意写作它可以写小说、故事、剧本、诗歌等创意性的文学作品&#…...
从WWDC看苹果产品发展的规律
WWDC 是苹果公司一年一度面向全球开发者的盛会,其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具,对过去十年 WWDC 主题演讲内容进行了系统化分析,形成了这份…...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...
Ascend NPU上适配Step-Audio模型
1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统,支持多语言对话(如 中文,英文,日语),语音情感(如 开心,悲伤)&#x…...
vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...
LeetCode - 199. 二叉树的右视图
题目 199. 二叉树的右视图 - 力扣(LeetCode) 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...
免费PDF转图片工具
免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...
逻辑回归暴力训练预测金融欺诈
简述 「使用逻辑回归暴力预测金融欺诈,并不断增加特征维度持续测试」的做法,体现了一种逐步建模与迭代验证的实验思路,在金融欺诈检测中非常有价值,本文作为一篇回顾性记录了早年间公司给某行做反欺诈预测用到的技术和思路。百度…...
