轉自 http://blog.csdn.net/zh379835552/article/details/37969205
現在在做的項目大致分為兩塊:戰斗系統和除戰斗系統之外的(簡稱外圍系統),而我一直在做的是外圍系統的開發,至少在6月份返校畢業答辯之前沒有動過戰斗系統。答辯回來之后很長一段時間內也是在做外圍系統的bug修復,可是由於種種原因項目趕不上所謂的進度了,上周五主管問我和另外一個也主要負責外圍系統開發的同事誰更忙,我一句我沒啥事干,結果主管說戰斗系統的主動技能讓我來做。這周一開會負責人一紙任務安排扔下來,上面寫着XX同事這周完成主動技能的開發及相關系統的bug修復,還說沒完成任務就XXX,此處省略XX字。
再稍微說下目前本人的情況:不是計算機相關專業畢業的,去年學了兩個月C++,一個月Cocos2dx,然后十月份入職現在的這個公司,因此相關技能水平就。。。現在在做的項目加上被砍掉的項目,共經歷過兩個項目開發。本着一腔熱情加上強大的互聯網資源,到目前為止項目組安排的任務都按時完成了。所以可以說是個稍微合格的游戲開發從業者吧。
扯了這么遠回到正題吧。技能的設計,首先我來理解一番游戲中的技能。在回合制游戲中,主動技能的流程大致為:釋放技能,得到技能影響值(項目組天天喊的buff)並確定技能對象,技能檢測+影響值添加到技能對象。而這次的主動技能設計我也是照着這個思路來的,因此本文可以據此大致分為三塊來講:技能釋放部分,buff生成部分(不添加至對象),技能檢測部分。(本項目的技能相對來說較簡單,技能釋放不考慮魔法值,buff生成不考慮別的影響因素,技能檢測不涉及命中率)
戰斗:
己方隊伍+敵方隊伍,說白了就相當於一個卡牌游戲。
一、釋放技能
本項目的釋放流程大致為:戰斗場景(BattleScene)中,當前操控的己方隊員達到技能CD時間,玩家主動執行某個操作釋放當前戰斗隊員的技能。釋放的技能可能是一個組合技能也可能是單個技能。
在這里要控制的是:技能信息(影響值、技能釋放者、技能作用者、技能影響回合),技能管理(觸發檢測、產生技能影響、清除影響)。因此我首先想到的時候要在該場景里生成一個技能管理器,負責處理這里要做的技能控制。
寫了一個技能管理類:skill_command(稱為技能控制器吧),大致成員如下:
class skill_command
{
public:
/**
*這個成員函數返回值用來做對是否是即時生效型技能的判讀
**/
bool init();
private:
skill_info _info; //技能的信息
role* _user; //技能使用者
role* _obj; //技能作用者
}
因為技能是在BattleScene釋放,且需要在該場景下做一系列的檢測,所以我的想法是將技能管理作為該場景內的成員,在該場景內執行技能管理類的檢測函數。而考慮到技能可能是組合技能,所以在場景類中,我以一個vector存儲技能管理對象。
class BattleScene
{
private:
vector<skill_command*> vecSkillCmd;
}
采取了這樣的設計之后,每一次操作隊員釋放某個技能,先分解技能組合,產生一個技能信息則new一個指針存儲在vecSkillCmd中:
vec = analyze(skill)
for(//做個循環)
{
auto skill_cmd = new skill_command();
skill_cmd->init();//在這里還需要設置技能信息及作用對象等
vecSkillCmd.push_back(skill_cmd)
}
做這樣的設計考慮的是:釋放一個技能,將其組合技能分解出來單獨控制,化繁為簡。也因為我已經得到了一個技能產生作用需要的信息(記錄了技能信息、技能作用者和使用者),所以我在技能作用、檢測階段就不需要額外再向用戶需要諸如技能對象等信息,只需要在場景中提供檢測條件即可,條件達成了就可以直接在skill_command內部實現技能影響。想當初上一個項目我也參與到了戰斗系統后期的維護工作,做的一塊就是優化技能的效果表現,當初碰到最大的麻煩就是很難確定技能對象,所以那時候好苦逼的在找技能對象。所以這一次我的想法是,不管技能如何我首先要做的就是拆解開來,單獨的確定其使用者和作用者,這樣每次一次做技能檢測和產生技能效果我都能找到對象。
設計案中也提到,技能可以分為即時作用和延時作用。指的是某些技能是一使用就會產生效果(如發射子彈、發射激光等),而有些是需要達到某個條件(如碰撞到了敵方隊員)才會產生效果。所以基於化繁為簡的考慮,在分解技能信息,產生技能控制器的階段我就將這兩類技能分開處理。具體做法是在BattleScene場景內再添加一個成員專門用來存儲即時作用的技能:
class BattleScene
{
private:
vector<skill_command*> vecSkillCmd;
vectpr<skill_command*> vecInstanceSkillCmd;
}
這樣處理之后,在生成技能控制器階段依據一技能生效類型分開存儲至不同的管理容器內。在檢測階段就只需要在不同的條件階段,循環檢測不同的容器內容,就可以達到我們的設計目的。如對於即時生效的技能,我只需要在釋放完技能后對vecInstanceSkillCmd的成員做檢測即可。當然會說直接將vecSkillCmd放在這里檢測不就可以了么?這就是我如何將一個技能管理器skill_command存儲的考慮了。
上文提到了,依據技能的作用類型分別會將技能控制器存儲至vecSkillCmd和vecInstanceSkillCmd。這里考慮了其作用時間,同時我也考慮了其作用條件。因為有的技能是即時無條件作用,而有的是即時有條件作用。針對即使無條件作用的,因為此種技能即時無條件作用了,所以我不需要在釋放完之后再做管理,因此我沒有存儲至vec中。而即時有條件作用的才會存儲至vec中以備之后做檢測,這里我們用了一個成語函數releaseSkill表示技能釋放動作:
void BattleScene::releaseSkill()
{
vec = analyze(skill);
for(//做個循環)
{
auto skill_cmd = new skill_command();//生成一個技能控制器
skill_cmd->init();//在這里還需要設置技能信息及作用對象等
if(//如果不是立即生效)
{
vecSkillCmd.push_back(skill_cmd);
}
else//即時生效
{
if(//無條件觸發) //執行觸發效果
{
skill_cmd->triggerSkill();
CC_SAFE_DELETE(_cmd);//刪除該指針
}
else
{
vecInstanceSkillCmd.push_back(skill_cmd);
}
}
}
}
進行到這一步,關於技能的釋放部分就差不多了。而在項目中我做的技能釋放也就是這樣一個操作。
二、buff生成
其實上周五安排我做技能的時候,我一開始想的是如何做buff的生成。同事也提到要寫個通用的接口,可以做道具的處理(加血、加速等道具),這樣就可以省時省力。所以首先做的工作便是做buff,當然也沒辦法,因為那個時候沒策划跟進我該如何做技能,直到這周三勉勉強強才把方案拿過來,此處又要省略XX字了。
因為我們已經有了一個role角色類,我想到buff不就是加到角色上的么,因此我的設計方案是如之前寫技能控制器一樣,在role角色類內添加成員作為buff控制器。也是基於化繁為簡的考慮,一個buff就生成一個控制器,buff控制器內部也是包含buff信息,作用者和使用者等信息:
class buff_command
{
private:
buff_info _info; //buff的信息
buff_user _user; //buff的使用者
buff_obj _obj; //buff的作用者
}
每一次產生了一個buff,就生成一個控制器,與技能控制器的設計思路類似:在role角色類內部以容器管理這些控制器,每次使用技能或者使用道具而得到了一個buff,就生成一個buff存儲至容器內,以便做后面的技能檢測和buff效果生成:
role角色類一個buff管理成員和一個buff的生成函數。buff的生成函數在技能控制器的初始化函數內部調用,其大致內容如下:
void role::addBuff()
{
//檢測重復buff
check();
auto buff_cmd = new buff_command();
buff_cmd->init();
vecBuffCmd.push_back(buff_cmd);
}
跟技能控制器的生成類似,這里有一步的內容是做buff重復性檢測:已經觸發的buff刪除其buff效果,而沒有觸發的則直接從容器中刪除。
結合第一部分的內容大致做個流程介紹:
1、首先在SceneBattle場景觸發技能使用:
-
battle_role->releaseSkill();
經過以上三步,簡單的一個buff便已生成。
三、技能檢測
這一部分我感覺有點復雜,也是我拿不准的地方。周六剛測試了一遍整個釋放-添加-檢測邏輯,在現有的幾套技能中是沒有問題的,就是不知道之后的會怎么樣。
項目的技能檢測設計為:首先要檢測觸發條件,再檢測生效條件。既首先一個技能要因某個條件而被觸發,如己方隊員碰了一下對方,或者己方隊員碰到了某個場景元素,那么該技能便達到了觸發條件;滿足了觸發條件之后,接着去做技能的生效條件,如該技能是要釋放者碰到了對方才會釋放,或者碰撞到了己方隊友才會釋放等等。所以在技能控制器skill_command內部寫了兩個成員函數:
技能的檢測主要就是靠這這兩個成員函數來實現,當然實際上這兩個函數是帶參數的。調用函數的地方是在BattleScene場景中:
BattleScene:://某個動作
for(//)
{
auto skill_cmd = vecSkillCmd[i];
//每次先做條件檢測,達到條件了之后控制器內部的bool型成員為true
skill_cmd->checkTriggerCondition();
//內部bool型成員為true的時候執行該成員函數的內部動作
skill_cmd->triggerSkill();
}
因為之前的設計中在技能控制器內部有技能作用者這一成員_obj,所以實際上技能的觸發是調用_obj的成員函數來實現:
而我設計的觸發buff這個成員函數,里面的實際動作是對role這個類里面的buff容器進行管理:
void role::triggerBuff(//帶能找到該buff的一個參數)
{
for(//)
{
if(//是該buff)
{
auto buff_cmd = vecBuffCmd[i];
buff_cmd->trigger();
}
}
}
這一步便實現了一個buff的觸發。回到我們之前提到的buff_command這個控制器:
class buff_command
{
public:
void trigger();
private:
buff_info _info; //buff的信息
buff_user _user; //buff的使用者
buff_obj _obj; //buff的作用者
}
成員函數trigger實際上是對buff的作用者_obj執行buff結算。依據buff類型(血、速)等,對角色的相應數值進行調整。而在實際的設計過程中,考慮到buff信息的獨立性,以一個獨立的結構體:buff_info作為role的成員變量,每次做buff結算的時候實際上調整的是該成員的值,然后再將這些值添加到角色role的相應信息上去。這樣相對獨立的設計,一方面適合直觀的展現每次技能使用帶來的技能影響(當然是對我們開發人員來說);一方面因為是改別人的代碼,這樣獨立的設計一個buff信息不會影響到已有代碼所實現的功能(改代碼好苦逼,主要是容易偏離原有設計者的思路)。
這樣buff的檢測+產生效果功能便已實現,最后的就是buff的清理工作了。
回合制游戲的一大特點就是一回合一回合的走下去,buff的影響效果也是有回合影響的;而技能的釋放也只是會在當前回合有效:即釋放了一個技能,只會在當前回合做有效性的檢測——vecSkillCmd/vecInstanceCmd執行成員的內部檢測函數,而當前回合結束后要刪除所有成員(因為釋放動作已經結束了,當然還要內存釋放)。
技能控制器的控制很簡單,只需要在每次回合結束的時候清理vecSkillCmd的成員及內存釋放,而vecInstanceCmd由於是管理的即時生效型技能,所以這個應該在釋放完技能(即技能特效播放完)就需要清理及釋放。
而對於buff控制器則稍微復雜點,因為涉及到buff有沒有被觸發:
1、對於觸發了的buff,要做回合檢測:達到了回合限制則開始清理buff——調整角色類role的成員buff_info的值(即清理buff的影響值,假如造成了10點扣血,則清理的時候要加回10點血)。這里可能需要解釋一番buff的生效機制:假如觸發了10點扣血的buff,那么role的成員buff_info里面的_hp數值就要調整為-10,表示扣血。每次對角色的數值信息進行結算的時候加上了這個buff值-10,就相當於實現了扣血功能,所以在清理buff的時候要加回10,buff_info里面的_hp數值就成為了0.
2、對於沒有觸發的buff,則直接清理刪除掉,這個跟技能控制器的清除管理思路類似。
這個清理工作是在每回合結束時進行,清理函數設計為角色類role的成員函數,對於場景中的每個角色都執行內部設計的清理檢查函數。
至此這一周所做的工作便是這些了,寫這篇博客相當於是對一周工作的總結,也不知道設計上哪些地方不合理了。不合理也木有辦法,我所會的就是這些了。。。其實本來還想結合設計模式相關的知識來梳理一番這周的工作,可是還沒有看相關的書籍所以不好怎么去梳理。總之設計模式相關方面的書肯定是要看的,最近在看《STL源碼剖析》和《Programming in Lua 3》這兩本書,看完其中一本之后便需要看關於設計模式相關的資料了。
總結:
在寫完之后其實自己也想了一些改進:針對buff的最終影響類型——血、速、攻、魔等,寫不同的類,然后統一讓buff_command進行管理,這樣是否便達到了可擴展的要求呢?今后如果還需要有額外的buff影響,那么只需要再添加額外的類讓buff_command類進行管理即可?
不管這次的設計思路如何,總歸是對自己的一種鍛煉吧,之前畢竟沒做過這些而今后總是會要做這些工作的~所謂的一些術語如:代碼耦合度、代碼復用、可擴展性等都是需要進一步的理解,在工作中需要再多注意的。
