导航:从零开始手写mmo游戏从框架到爆炸(零)—— 导航-CSDN博客
规则
我们现在设计并尝试开发战斗系统。战斗系统我们先设定几个规则
1、多人对多人:英雄可以携带宝宝,面对的野怪也可以是成群的,故要设计成多人对多人的战斗引擎;
2、速度快的先出手:这点应该没有异议,根据速度来判定出手间隔,但是这里要有一个数据转换的问题,比如并不是速度100的就比速度50的出手速度快一倍,应该有一个计算公式来根据英雄的速度属性计算出手间隔。
3、急速打架:考虑到不能在后台慢慢的让两方打,这样耗时又费机器,用户也等不了这么久,所以最好的方法是在后台迅速的计算出结果,然后把打斗的过程返回给前端,前端再进行展示。笔者玩了一些小游戏也是这么做的。
4、要有底线:如果双方攻击力都不足,或者奶量都十足,那么很有可能永远打不完,就死循环要导致内存溢出了故而应该设定一个最长打斗时间,如果到了最长打斗时间还没有分出胜负就是平局。
5、技能的问题:技能的问题很复杂,有光环型的技能,有召唤型的技能,有单伤,都群伤,有被动,有主动,所以目前我们暂时先不考虑技能,先让咱们的游戏可以动起来,可以打怪,然后掉落宝物。之后我们再根据技能的设定重新来完善战斗系统。
基于以上几点规则,我们先尝试写几个demo来模拟打斗的场面。
代码
首先我们创建测试的英雄类,简化战斗模型,只有姓名、攻击、生命值、速度这四个属性。并且增加一个攻击方法。
Hero.java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Hero {
String name = "hero";
int attack = 10;
int speed = 10;
int hp = 100;
public Hero(String name) {
this.name = name;
}
/***
* 模拟攻击
* @param hero
*/
public void attack(Hero hero) {
if(hero.getHp() > 0) {
hero.setHp(hero.getHp() - getAttack());
System.out.println(getName() + "攻击," + hero.getName() + "生命值减少" + getAttack());
}
}
}
创建一个动作接口,用于封装攻击的数据- Action
public interface Action {
void run();
/***
* 是否继续
* @return
*/
boolean checkContinue();
int speed();
}
先实现1V1- BaseAttack
public class BaseAttack implements Action {
private Hero heroA;
private Hero heroB;
public BaseAttack(Hero heroA, Hero heroB) {
this.heroA = heroA;
this.heroB = heroB;
}
@Override
public void run() {
heroA.attack(heroB);
}
@Override
public boolean checkContinue() {
return heroA.getHp()>0;
}
@Override
public int speed() {
return heroA.getSpeed();
}
}
1v1的打斗服务 - BattleOneToOne
public class BattleOneToOne {
// 正式战斗是以秒为单位,但是我们不可能真的按照秒来进行战斗,会大大的影响服务器的性能,所以我们应该要做的就是缩短打架的过程把结果返回给客户端即可,并把实际打斗时间告诉给客户端,然后客户端再进行时延的播放即可
// 设计一个队列,在
private LinkedList<Action> actions = new LinkedList<>();
private int addAction(Action action){
actions.offer(action);
return actions.size();
}
public void fight(Hero A,Hero B) throws InterruptedException {
// 根据速度放入队列
if(A.getSpeed() > B.getSpeed()){
addAction(new BaseAttack(A,B));
addAction(new BaseAttack(B,A));
}else{
addAction(new BaseAttack(B,A));
addAction(new BaseAttack(A,B));
}
while(A.getHp() > 0 && B.getHp() > 0) {
Action pop = actions.pop();
pop.run();
// 再放进去
if (pop.checkContinue()) {
addAction(pop);
}
}
if(A.getHp() > 0) {
System.out.println(A.getName() + "获胜:" + A.getHp());
}else{
System.out.println(B.getName() + "获胜:" + B.getHp());
}
}
public static void main(String[] args) throws InterruptedException {
BattleOneToOne battleOneToOne = new BattleOneToOne();
Hero A = new Hero("刘备");
Hero B = new Hero("关羽");
B.setSpeed(20);
B.setAttack(20);
battleOneToOne.fight(A,B);
}
}
效果如下:
关羽攻击,刘备生命值减少20
刘备攻击,关羽生命值减少10
关羽攻击,刘备生命值减少20
刘备攻击,关羽生命值减少10
关羽攻击,刘备生命值减少20
刘备攻击,关羽生命值减少10
关羽攻击,刘备生命值减少20
刘备攻击,关羽生命值减少10
关羽攻击,刘备生命值减少20
关羽获胜:60
我们再来写一个多人对战的- GroupAttack
public class GroupAttack implements Action {
private Hero heroA;
private List<Hero> defenceList;
public GroupAttack(Hero heroA, List<Hero> defenceList) {
this.heroA = heroA;
this.defenceList = defenceList;
}
@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);
}
@Override
public int speed() {
return heroA.getSpeed();
}
}
BattleManyToMany
public class BattleManyToMany {
// 队列不变
private LinkedList<Action> actions = new LinkedList<>();
private int addAction(Action action){
actions.offer(action);
return actions.size();
}
public void fight(List<Hero> listA, List<Hero> listB) throws InterruptedException {
// 先放入listA
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++) {
addAction(new GroupAttack(listB.get(i),listA));
}
// 如果A集合和B集合的生命值都还大于0
while(listA.stream().anyMatch(e -> e.getHp() > 0) && listB.stream().anyMatch(e -> e.getHp() > 0)) {
Action pop = actions.pop();
boolean run = pop.run();
if(run) {
// 再放进去
if (pop.checkContinue()) {
addAction(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));
}
}
public static void main(String[] args) throws InterruptedException {
BattleManyToMany battle = new BattleManyToMany();
Hero A = new Hero("A");
Hero B = new Hero("B");
B.setSpeed(20);
B.setAttack(20);
Hero C = new Hero("C");
C.setSpeed(5);
C.setAttack(20);
Hero D = new Hero("D");
D.setSpeed(10);
D.setAttack(15);
battle.fight(Arrays.asList(A,C),Arrays.asList(B,D));
}
}
运行结果如下:
A攻击,B生命值减少10
A集团 :[{"attack":10,"hp":100,"name":"A","speed":10},{"attack":20,"hp":100,"name":"C","speed":5}]
B集团 :[{"attack":20,"hp":90,"name":"B","speed":20},{"attack":15,"hp":100,"name":"D","speed":10}]
C攻击,B生命值减少20
A集团 :[{"attack":10,"hp":100,"name":"A","speed":10},{"attack":20,"hp":100,"name":"C","speed":5}]
B集团 :[{"attack":20,"hp":70,"name":"B","speed":20},{"attack":15,"hp":100,"name":"D","speed":10}]
B攻击,A生命值减少20
A集团 :[{"attack":10,"hp":80,"name":"A","speed":10},{"attack":20,"hp":100,"name":"C","speed":5}]
B集团 :[{"attack":20,"hp":70,"name":"B","speed":20},{"attack":15,"hp":100,"name":"D","speed":10}]
D攻击,A生命值减少15
A集团 :[{"attack":10,"hp":65,"name":"A","speed":10},{"attack":20,"hp":100,"name":"C","speed":5}]
B集团 :[{"attack":20,"hp":70,"name":"B","speed":20},{"attack":15,"hp":100,"name":"D","speed":10}]
A攻击,B生命值减少10
A集团 :[{"attack":10,"hp":65,"name":"A","speed":10},{"attack":20,"hp":100,"name":"C","speed":5}]
B集团 :[{"attack":20,"hp":60,"name":"B","speed":20},{"attack":15,"hp":100,"name":"D","speed":10}]
C攻击,B生命值减少20
A集团 :[{"attack":10,"hp":65,"name":"A","speed":10},{"attack":20,"hp":100,"name":"C","speed":5}]
B集团 :[{"attack":20,"hp":40,"name":"B","speed":20},{"attack":15,"hp":100,"name":"D","speed":10}]
B攻击,A生命值减少20
A集团 :[{"attack":10,"hp":45,"name":"A","speed":10},{"attack":20,"hp":100,"name":"C","speed":5}]
B集团 :[{"attack":20,"hp":40,"name":"B","speed":20},{"attack":15,"hp":100,"name":"D","speed":10}]
D攻击,A生命值减少15
A集团 :[{"attack":10,"hp":30,"name":"A","speed":10},{"attack":20,"hp":100,"name":"C","speed":5}]
B集团 :[{"attack":20,"hp":40,"name":"B","speed":20},{"attack":15,"hp":100,"name":"D","speed":10}]
A攻击,B生命值减少10
A集团 :[{"attack":10,"hp":30,"name":"A","speed":10},{"attack":20,"hp":100,"name":"C","speed":5}]
B集团 :[{"attack":20,"hp":30,"name":"B","speed":20},{"attack":15,"hp":100,"name":"D","speed":10}]
C攻击,B生命值减少20
A集团 :[{"attack":10,"hp":30,"name":"A","speed":10},{"attack":20,"hp":100,"name":"C","speed":5}]
B集团 :[{"attack":20,"hp":10,"name":"B","speed":20},{"attack":15,"hp":100,"name":"D","speed":10}]
B攻击,A生命值减少20
A集团 :[{"attack":10,"hp":10,"name":"A","speed":10},{"attack":20,"hp":100,"name":"C","speed":5}]
B集团 :[{"attack":20,"hp":10,"name":"B","speed":20},{"attack":15,"hp":100,"name":"D","speed":10}]
D攻击,A生命值减少15
A集团 :[{"attack":10,"hp":-5,"name":"A","speed":10},{"attack":20,"hp":100,"name":"C","speed":5}]
B集团 :[{"attack":20,"hp":10,"name":"B","speed":20},{"attack":15,"hp":100,"name":"D","speed":10}]
C攻击,B生命值减少20
A集团 :[{"attack":10,"hp":-5,"name":"A","speed":10},{"attack":20,"hp":100,"name":"C","speed":5}]
B集团 :[{"attack":20,"hp":-10,"name":"B","speed":20},{"attack":15,"hp":100,"name":"D","speed":10}]
D攻击,C生命值减少15
A集团 :[{"attack":10,"hp":-5,"name":"A","speed":10},{"attack":20,"hp":85,"name":"C","speed":5}]
B集团 :[{"attack":20,"hp":-10,"name":"B","speed":20},{"attack":15,"hp":100,"name":"D","speed":10}]
C攻击,D生命值减少20
A集团 :[{"attack":10,"hp":-5,"name":"A","speed":10},{"attack":20,"hp":85,"name":"C","speed":5}]
B集团 :[{"attack":20,"hp":-10,"name":"B","speed":20},{"attack":15,"hp":80,"name":"D","speed":10}]
D攻击,C生命值减少15
A集团 :[{"attack":10,"hp":-5,"name":"A","speed":10},{"attack":20,"hp":70,"name":"C","speed":5}]
B集团 :[{"attack":20,"hp":-10,"name":"B","speed":20},{"attack":15,"hp":80,"name":"D","speed":10}]
C攻击,D生命值减少20
A集团 :[{"attack":10,"hp":-5,"name":"A","speed":10},{"attack":20,"hp":70,"name":"C","speed":5}]
B集团 :[{"attack":20,"hp":-10,"name":"B","speed":20},{"attack":15,"hp":60,"name":"D","speed":10}]
D攻击,C生命值减少15
A集团 :[{"attack":10,"hp":-5,"name":"A","speed":10},{"attack":20,"hp":55,"name":"C","speed":5}]
B集团 :[{"attack":20,"hp":-10,"name":"B","speed":20},{"attack":15,"hp":60,"name":"D","speed":10}]
C攻击,D生命值减少20
A集团 :[{"attack":10,"hp":-5,"name":"A","speed":10},{"attack":20,"hp":55,"name":"C","speed":5}]
B集团 :[{"attack":20,"hp":-10,"name":"B","speed":20},{"attack":15,"hp":40,"name":"D","speed":10}]
D攻击,C生命值减少15
A集团 :[{"attack":10,"hp":-5,"name":"A","speed":10},{"attack":20,"hp":40,"name":"C","speed":5}]
B集团 :[{"attack":20,"hp":-10,"name":"B","speed":20},{"attack":15,"hp":40,"name":"D","speed":10}]
C攻击,D生命值减少20
A集团 :[{"attack":10,"hp":-5,"name":"A","speed":10},{"attack":20,"hp":40,"name":"C","speed":5}]
B集团 :[{"attack":20,"hp":-10,"name":"B","speed":20},{"attack":15,"hp":20,"name":"D","speed":10}]
D攻击,C生命值减少15
A集团 :[{"attack":10,"hp":-5,"name":"A","speed":10},{"attack":20,"hp":25,"name":"C","speed":5}]
B集团 :[{"attack":20,"hp":-10,"name":"B","speed":20},{"attack":15,"hp":20,"name":"D","speed":10}]
C攻击,D生命值减少20
A集团 :[{"attack":10,"hp":-5,"name":"A","speed":10},{"attack":20,"hp":25,"name":"C","speed":5}]
B集团 :[{"attack":20,"hp":-10,"name":"B","speed":20},{"attack":15,"hp":0,"name":"D","speed":10}]
A集团 获胜:[{"attack":10,"hp":-5,"name":"A","speed":10},{"attack":20,"hp":25,"name":"C","speed":5}]
Process finished with exit code 0
全部源码详见:
gitee : eternity-online: 多人在线mmo游戏 - Gitee.com
分支:step-11
请各位帅哥靓女帮忙去gitee上点个星星,谢谢!