前言
责任链模式可以用来对对关卡系统的重构。
将关卡数据使用类加以封装
封装的信息根据需求而定:如要出场的敌方角色的设置、通关条件、下一关的记录。
也就是让每一关都是一个对象并加以管理。而关卡系统则是在这群对象中寻找“条件符合”的关卡,让玩家进入挑战。
等到关卡完成后,再进入到下一个条件符合的关卡。
GOF对责任链模式(Chain of Responsibility)的定义
“让一群对象都有机会来处理一项请求,以减少请求发送者与接收者之间的耦合度(即依赖度)。将所有的接收者对象串接起来,让请求沿着串接传递,直到有一个对象可以处理为止”
实现思路
1、可以解决请求的接收者对象:这些类对象能够了解“请求”信息的内容,并判断本身能否解决
2、接收者对象间的串接:利用一个串接机制,将每一个可能可以解决问题的接收者对象给串接起来。对于被串接的接收者对象来说,当本身无法解决这个问题时,就利用这个串来接机制,让请求能不断传递下去;或使用其他管理方式,让接收对象得以链接
3、请求自动转移传递:发出请求后,请求会自动往下转移传递,过程之中,发送者不需特别转换接口。
游戏实现
关卡接口
public abstruct class IStateHandler{
protected IStateData m_StageData = null;
protected IstateScore m_StageScore = null;
protected IStageHandler m_NextHandler = null;
public IStateHandler SetNextHandler(IStageHandler NextHandler){
m_NexHandler = NextHander;
retun m_NexHandler;
}
public abstruct IStageHandler CheckStage();
public abstruct void Update();
public abstruct void Reset();
public abstruct void IsFinished();
}
CheckStage 用来判断当前游戏所处的关卡是哪一个,所以会返回一个IStageHandler 引用给关卡系统。
常规关卡
public class NormalStageHandler : IStageHandler{
//设置分数和关卡数据
public NormalStageHandler(IStageScore stateScore ,IStageData stageData){
m_StageScore = stageScore;
m_StageData = stageData;
}
//设置下一关卡
public IStageHandler SetNextHandler(IStageHandler NextHandler){
m_NextHandler = NextHandler;
return m_NextHandler;
}
//确认关卡
public override IStageHandler CheckStage(){
//分数是否足够
if(m_StageScore.CheckScore() == false)
return this;
//已经是最后一关
if(m_NextHandler == null)
return this
//确认下一个关卡
return m_NextHandler.CheckStage();
}
public override void Update(){
m_StageData.Update();
}
public override void Reset(){
m_StageData.Reset();
}
public override bool IsFinished(){
return m.StageData.IsFinished();
}
}
在关卡初始化时,会将关卡所使用的“过关条件”和“关卡内容”设置给关卡对象,并且利用SetNextHandler来设置连接的下一个关卡。
在确认关卡CheckStage()方法中,判断当前游戏状态是否符合关卡过关的条件判断。如果已满足,代表可前往下一个关卡,该方法最后返回有当前可以使用的关卡对给关卡系统。
关卡分数确认
public abstract class IStageScore{
public abstruct bool CheckScore();
}
public class StageScoreEnemyKilledCount:IStageScore{
private int m_EnemyKilledCount = 0;
private StageSystem m_StageSystem = null;
public StageScoreEnemyKilledCount(int killedCount,StageSystem theStageSystem){
m_EnemyKillCount = killCount;
m_StageSystem = theStageSystem;
}
//确认关卡分数是否达到
public override bool CheckScore(){
return (m_StageSystem.GetEnemyKilledCount() >= m_EnemyKilledCount);
}
}
当前的关卡分数对象需要定期更新
//在上面的常规关卡类中...
public override void Update(){
m_StageData.Update();
}
关卡内容接口
public abstract class IStageData{
public abstract void Update();
public abstract bool IsFinished();
public abstract void Reset();
}
关卡内容接口类主要负责将关卡的“内容”呈现给玩家
关卡内容一般指的是玩家要挑战的项目,这些项目可能是出现3个敌人角色,让玩家击退;也可能是出现3个道具让玩家去搜索获取;或是设计特殊任务关卡让玩家去完成。
而这些设置内容都会放进IStageData的子类中,并且通过Game Loop 更新机制,让关卡内容可以顺利产生给玩家挑战
常规关卡内容
public class NormalStageData:IStageData{
private float m_CoolDown = 0; //产生角色的时间间隔
private float m_MaxCoolDown = 0;
private Vector3 m_SpawnPosition = Vector3.zero;
private Vector3 m_AttackPosition = Vector3.zero;
private bool m_AllEnemyBorn = false;
//常规关卡要产生的敌人单位
private List<StageData> m_StageData = new List<StageData>();
class StageData{
public ENUM_Enemy emEnemy = ENUM_Enemy.Null;
public ENUM_Weapon emWeapon = ENUM_Weapon.Null;
public bool bBorn = false;
public StageData(ENUM_Enemy emEnemy ,ENUM_Weapon emWeapon){
this.emEnemy = emEnemy;
this.emWeapon = emWeapon;
}
}
//设置多久产生一个敌方单位
public NormalStageData(float CoolDown,Vector3 SpawnPosition,Vector3 Attack AttackPosition){
m_MaxCoolDown = CoolDown;
m_CoolDown = m_MaxCoolDown;
m_SpawnPosition = SpawnPosition;
m_AttackPosition = AttackPosition;
}
//增加关卡的敌方单位
public void AddStageData(ENUM_Enemy emEnemy,ENUM_Weapon,int Count){
for(int i = 0; i < Count ; ++i){
m_StageData.Add(new StageData(emEnemy,emWeapon);
}
}
//重置
public override void Reset(){
foreach(StageData pData in m_StageData){
pData.bBorn = false;
m_AllEnemyBorn = false;
}
}
//更新
public override void Update(){
if(m_StageData.Count == 0)
return;
//是否可以产生
m_CoolDown -= Time.deltaTime;
if(m_CoolDown > 0)
return;
//获取上场的角色
StageData theNewEnemy = GetEnemy();
if(theNewEnemy == null)
return
//一次产生一个单位
ICharacterFactory Factory = PBDFactory.GetCharacterFactory();
Factory.CreateEnemy(theNewEnemy.emEnemy.theNewEnemy.emWeapon,m_SpawnPosition,m_AttackPosition);
}
//获取还没产生的关卡
private StageData GetEnemy(){
foreach(StageData pData in m_StageData){
if(pData.bBorn == false){
pData.bBorn = true;
return pData;
}
}
m_AllEnemyBorn = true;
return null;
}
}
以上的关卡内容就是根据需求来定义实现。
NormalStageData类内定义了整个与派送敌方角色上场有关的设置参数。
当角色可以产生时,就调用角色工厂的方法,将对象产出并放入战场。
关卡控制系统
关卡系统类有基本的关卡信息
public class StageSystem : IGameSystem{
public const int MAX_HEART = 3;
private int m_NowHeart = MAX_HEART; //当前玩家阵地情况
private int m_NowStageLv = 1; //当前的关卡
private IStageHandler m_NowStageHandler = null;
private IStageHandler m_RootStageHandler = null;
private List<Vector3> m_SpawnPosition = null;
private Vector3 m_AttackPos = Vector3.zero;
private bool m_bCreateStage = false; //是否产生关卡
public StageSystem(PBaseDefense PBDGame):base(PBDGame){
Initialize();
}
//设置关卡
public override void Initialize(){
//设置关卡
InitalizeStageData();
//指定第一个关卡
m_NowStageHandler = m_RootStageHandler;
m_NowStageLv = 1;
}
public override void Relealse(){
base.Release();
m_SpawnPosition.Clear();
m_SpawnPosition = null;
m_NowHeart = MAX_HEART;
m_EnemyKilledCount = 0;
m_AttackPos = Vector3.zero;
}
//更新
public override void Update(){
//更新当前关卡
m_NowStageHandler.Update();
//是否要切换下一个关卡
if(m_PBDGame.GetEnemyCount() == 0){
//是否结束
if(m_NowStageHandler.IsFinished() == false)
return;
//获取下一关
IStageHandler NewStageData = m_NowStageHandler.CheckStage();
//是否为旧关卡
if(m_NowStageHandler == NewStageData)
m_NowStageHandler.Reset();
else{
m_NowStageHandler = NewStageData;
}
//通知进入下一关
NotifNewStage();
}
}
//...若干字段属性计算方法、Get方法
//获得当前击杀数
public int GetEnemyKilledCount(){
return m_EnemyKilledCount;
}
//设置当前击杀数
public void SetEnemyKilledCount(int killedCount){
m_EnemyKilledCount = killedCount;
}
//增加当前击杀数
public voi AddEnemyKilledCount(){
m_EnemyKilledCount++;
}
//通知新关卡
private void NotiyNewStage(){
m_PBDGame.ShowGameMsg("新的关卡");
m_NowStageLv++;
//显示
m_PBDGame.ShowNowStageLv(m_NowStageLv);
//通知Soldier升级
m_PBDGame.UpgateSoldier();
//事件通知
m_PBDGame.NotifyGameEvent(ENUM_GameEvennt.NewStage,null);
}
//初始化所有关卡
private void InititalizeStageData(){
if(m_RootStageHandler != null)
return;
//引用点
Vector3 AttackPosition = GetAttackPosition();
NormalStageData StageData = null;
IStageScore StageScore == null;
IStageHandler NewStage = null;
//第一关
StageData = new NormalStageData(3f,GetSpawnPosition(),AttackPosition);
StageData.AddStageData(ENUM_Enemy.Elf,ENUM_Weapon.Gun,3);
StageScore = new StageScoreEnemykilledCount(3,this);
NewStage = new NormalStageHandler(StageScore,StageData);
//设置为起始关卡
m_RootStageHandler = NewStage;
StageData = new NormalStageData(3f,GetSpawnPosition(),AttackPosition);
StageData.AddStageData(ENUM_Enemy.Elf,ENUM_Weapon.Rifle,3);
StageScore = new StageScoreEnemykilledCount(6,this);
NewStage = new NormalStageHandler(StageScore,StageData);
//...关卡设置,一次性初始化
}
}
代码较多…,主要是偏于实现了,关键点在于在定期更新方法中,
将切换关卡的判断交给一群关卡对象串接起来的链表来负责。
需要切换关卡时,询问关卡对象链表,就可以获取当前可以进行的关卡。
初始化时,就将所有关卡一次设置完成,包含关卡要出现的敌方角色等级,数量,武器等级,过关的判断分数以及连接下一关卡。
可以看出关卡设置需要较多的设置,更好的方式是将关卡的信息也写成一个属性类,使用“企划设置工具”,让企划人员来设置。然后做出文件输出,让关卡系统读入。
总结
使用责任链模式的优点:
使用关卡对象来代替以前的旧方法,使得冗长式写法就获得改善。并且将关卡内容、过关条件类化,可使得关卡类型有多种形式的组合。
责任链模式让一群信息接收者被串联管,让信息判断上能有一致的操作接口,不必因为不同的接收者而执行类转换操作;并且让所有信息接收者都可以判断是否提供服务或需求移往下一个信息接收者,在后续的系统维护上,也可以轻易地增加接收者类(不同的关卡类型)。