记录一次回合制游戏简单框架

公司有个GameJam活动,要做个demo。这个GameJam最开始只是凭借想法和演示视频就能评比。后面根据评比结果来做demo。因为球体荒野乱斗的难度比较大,选了做回合制的游戏。

GameJam不适合做联机游戏。当时非要装大尾巴狼,做联网的demo。最后demo功能包括匹配,进房间,各个回合对战,结算,还算比较完善。

这里记录一下回合制游戏的框架是什么样的。

回合制游戏的流程

如果是个PVP回合制游戏,一局游戏的过程基本上是这样的。

  • 点击匹配。服务器维护一个list等其他玩家也点击匹配。

  • 匹配成功后服务器开房间。向房间内的玩家广播匹配成功以及房间内的成员信息。

  • 客户端切换到战斗场景。当切换完毕,通知服务器可以开始游戏了。

  • 服务器向房间内玩家广播双方的站位,英雄以及装扮等信息。

  • 服务器接收到客户端可以开始游戏的请求后,启动回合的状态机,之后会由状态机来驱动游戏运行。

  • 当回合内符合结算条件后,比如将对方都击败,或是达到最大回合数,开始结算发奖励。之后将房间关闭。

回合是如何驱动的

回合制游戏的回合在服务器上是个状态机。每个状态实现了进入,执行,退出,更新四个方法。

public interface ITBSState
{
    void Enter(TBSRoom room);
    void Execute(TBSRoom room);
    void Exit(TBSRoom room);
    void Update(TBSRoom room, float deltaTime);
}

这里我把一局游戏分成了四种状态

  • 回合开始

    给所有客户端推送回合开始,包括回合数,回合等待时间。

    判断是否超过最大回合数,如果超过最大回合数,广播战斗结束并且关闭战斗房间。

  • 回合准备

    在update中检查是否接收到每个玩家的操作,如果没有接收全部玩家操作就等待,如果等待超过规定时间后,给玩家默认操作,并进入战斗计算状态。

  • 回合战斗计算

    计算战斗结果,把结果发送到每个客户端,进入回合结束状态

  • 回合结束

    等待每个客户端播放动画完成,当每个客户端都告诉服务器播放完动画,则进入回合开始状态。

目前游戏中就是这四个状态之间顺序切换,各个状态之间的切换都加了最大等待时间,避免某个状态抛异常导致状态切换停转。

战斗计算大概是什么样

回合制的战斗计算中很重要的部分就是战斗技能。buff也可以看做持续n个回合的技能。

比如一个技能是战斗回合前给自己加血,等自己行动时给最低血量友方加血,自身回合结束后加血,被打时候加血,打别人时候加血。。。

这其中就包含

  • 回合开始前
  • 自身回合开始
  • 造成伤害前
  • 。。。

计算伤害的时候按顺序把这些阶段都算一遍就OK了。

由此可见每个技能在计算伤害的时候都可以抽象出一个IAbility接口。

public interface IAbility
{
    void OnTurnBefore(){} // 战斗回合开始前调用的方法,用于执行回合开始前的准备工作。
    void OnTurnStart(){} // 战斗回合开始时调用的方法,用于执行回合开始时的逻辑。
    void OnTurnEnd(){} // 战斗回合结束时调用的方法,用于执行回合结束时的逻辑。
    void OnBeforeDealDamage(){} // 造成伤害前调用的方法,用于执行造成伤害前的准备工作。
    void DealDamage(){} // 造成伤害时调用的方法,具体实现应包含伤害计算和应用逻辑。
    void OnAfterDealDamage(){} // 造成伤害后调用的方法,用于执行造成伤害后的逻辑,如治疗、状态应用等。
    void OnBeforeTakeDamage(){} // 承受伤害前调用的方法,用于执行承受伤害前的准备工作。
    void TakeDamage(){} // 承受伤害时调用的方法,具体实现应包含伤害计算和应用逻辑。
    void OnAfterTakeDamage(){} // 承受伤害后调用的方法,用于执行承受伤害后的逻辑,如治疗、状态应用等。

    ...

}

每个战斗单位都可以携带多个技能,在计算前先把各个单位按速度排序,每个战斗单位必须有个速度的属性,才能合理确定先执行谁的技能。

其中每次执行技能都会产生一个伤害数据,包括a打b扣了c血量加了buff等信息。当产生数据后,需要缓存起来广播到各个客户端。

返回顶部