濟(jì)南做外貿(mào)網(wǎng)站新冠咳嗽怎么辦
更新日期:2024年7月4日。
項目源碼:第五章發(fā)布(正式開始游戲邏輯的章節(jié))
索引
- 簡介
- 一、尋路系統(tǒng)
- 二、尋路規(guī)則(角色移動)
- 三、尋路規(guī)則(角色攻擊)
- 四、角色移動尋路
- 1.自定義尋路規(guī)則
- 2.尋角色的所有可行走地塊
- 3.尋角色到達(dá)指定地塊的路徑
- 五、角色攻擊尋路
- 1.自定義尋路規(guī)則
- 2.尋找角色的攻擊范圍內(nèi)的地塊
- 六、角色登場尋路
- 七、整合
簡介
尋路系統(tǒng)
是整個游戲最核心的功能之一,角色的移動
和戰(zhàn)斗
都是基于尋路系統(tǒng)來進(jìn)行的,畢竟我們的游戲有三分之一的戰(zhàn)棋血統(tǒng)。
一、尋路系統(tǒng)
由于HTFrameworkAI
模塊正好支持如下我們游戲需要的尋路核心功能:
- 1.兩點間尋路;
- 2.尋可行走節(jié)點。
所以首先就是引入該模塊,更多信息請參閱:【Unity】 HTFramework框架(二十七)A*尋路。
二、尋路規(guī)則(角色移動)
對于我們角色的移動和攻擊而言,移動速度
和攻擊距離
便是其尋路計算時的最大依據(jù)。
比如移動速度=10
,則角色初始移動能力=10
,每移動一格(地塊),移動能力-1
,當(dāng)移動能力
減至0時,角色無法再繼續(xù)移動。
同時,不同類型的地塊對移動能力
還會產(chǎn)生額外的削減:
- 1.地面:-0;
- 2.山體:-1;
- 3.森林:-1;
- 4.湖泊:-1;
- 5.雪地:-2;
- 6.障礙:不可通行;
- 7.敵方占領(lǐng)地塊:不可通行。
這也是角色行走到山體
上會被減速的功能點的實現(xiàn)方式。
不過,一些特殊加成型要訣
能夠抵消地塊的額外削減,但是,在我們的進(jìn)程中,特殊加成型要訣
尚在構(gòu)思階段,所以,具體的實現(xiàn)我們后續(xù)一步步來。
三、尋路規(guī)則(角色攻擊)
角色攻擊是同理的,不過角色攻擊尋路規(guī)則跟地塊類型的關(guān)系有所不同:
- 1.地面:可以跨越攻擊;
- 2.山體:可以跨越攻擊;
- 3.森林:可以跨越攻擊;
- 4.湖泊:可以跨越攻擊;
- 5.雪地:可以跨越攻擊;
- 6.障礙:不可跨越攻擊;
- 7.敵方占領(lǐng)地塊:可以跨越攻擊。
由于角色攻擊尋路幾乎只針對遠(yuǎn)程攻擊
(近程攻擊
只能攻擊身邊的4格,用不著尋路),所以這里的可以跨越攻擊
及不可跨越攻擊
也即是指攻擊時是否能夠跨越該地塊攻擊敵人。
同理,一些特殊加成型要訣
能夠改變?nèi)缟系囊?guī)則。
四、角色移動尋路
1.自定義尋路規(guī)則
要做到如上這么多自由的想法,自定義尋路規(guī)則
是必須的,所幸HTFrameworkAI
的A*尋路
支持自定義尋路規(guī)則,那么我們便立即開始吧(繼承至AStarRule
即可):
/// <summary>/// 尋路規(guī)則(角色移動)/// </summary>public class MoveRule : AStarRule{/// <summary>/// 當(dāng)前的關(guān)卡/// </summary>public Level CurrentLevel;/// <summary>/// 當(dāng)前尋路的角色/// </summary>public Role CurrentRole;/// <summary>/// 目標(biāo)地塊/// </summary>public Block TargetBlock;//尋路前,對所有A*節(jié)點應(yīng)用自定義規(guī)則public override void Apply(AStarNode node){//通過節(jié)點索引找到其對應(yīng)的地塊Block block = CurrentLevel.Blocks[node.XIndex, node.YIndex];//如果地塊上存在敵人(陣營不同)if (block.StayRole != null && block.StayRole.Camp != CurrentRole.Camp){//則該地塊不可行走node.IsCanWalk = false;return;}switch (block.Type){case BlockType.Ground:// OCost 為該節(jié)點的額外估價,尋路計算時將造成【移動能力】的額外削減// 此處 = 0,則表明無額外削減node.OCost = 0;node.IsCanWalk = true;break;case BlockType.Moutain://山體:將造成【移動能力】額外 -1node.OCost = 1;node.IsCanWalk = false;break;case BlockType.Forest:node.OCost = 1;node.IsCanWalk = true;break;case BlockType.Water:node.OCost = 1;node.IsCanWalk = false;break;case BlockType.Snow:node.OCost = 2;node.IsCanWalk = true;break;case BlockType.Obstacle://障礙:將造成該地塊不可行走node.IsCanWalk = false;break;}}}
如上的代碼應(yīng)該很好理解了,足以可見,自定義尋路規(guī)則是何其的簡單。
2.尋角色的所有可行走地塊
角色移動前,能夠根據(jù)角色自身移動速度
,周圍地塊屬性
等,尋找出所有可以移動的地塊以供玩家選擇:
private static MoveRule _moveRule;private static List<Block> _resultBlocks = new List<Block>();/// <summary>/// 尋路規(guī)則(移動)/// </summary>private static MoveRule CurrentMoveRule{get{if (_moveRule == null){_moveRule = new MoveRule();}return _moveRule;}}/// <summary>/// 尋找角色的可行走地塊/// </summary>/// <param name="level">關(guān)卡</param>/// <param name="role">角色</param>public static List<Block> FindWalkableBlocks(Level level, Role role){if (level == null || role == null || role.Speed == 0){_resultBlocks.Clear();return _resultBlocks;}CurrentMoveRule.CurrentLevel = level;CurrentMoveRule.CurrentRole = role;//WalkableNodefinding 為 A* 尋路方法,具體參閱 HTFrameworkAI//參數(shù)1:role.StayBlock.Pos 尋路起點//參數(shù)2:role.Speed 移動速度//參數(shù)3:傳入自定義尋路規(guī)則List<AStarNode> nodes = level.Map.WalkableNodefinding(role.StayBlock.Pos, role.Speed, CurrentMoveRule);//尋路結(jié)果為A*節(jié)點集合,通過節(jié)點索引獲取對應(yīng)的地塊即可_resultBlocks.Clear();for (int i = 0; i < nodes.Count; i++){_resultBlocks.Add(level.Blocks[nodes[i].XIndex, nodes[i].YIndex]);}return _resultBlocks;}
尋找到所有可行走地塊后,接下來只需要高亮這些地塊即可,同時讓玩家可以點擊選擇(高亮方式就取決于自己了,當(dāng)然這塊邏輯也有涉及,不過在最后的實現(xiàn)UI界面時講解
):
比如這里的角色絡(luò)英俊
,移動速度為7
,周圍高亮的都是可行走的地塊,其他在移動范圍內(nèi)的便是不可行走的地塊。
3.尋角色到達(dá)指定地塊的路徑
上一步已經(jīng)尋找到了所有可移動地塊
,如果玩家點擊了其中的一個,則表明期望角色移動到該地塊,所以需要尋角色到達(dá)該地塊的路徑:
/// <summary>/// 尋找角色到達(dá)指定地塊的路徑/// </summary>/// <param name="level">關(guān)卡</param>/// <param name="role">角色</param>/// <param name="block">目標(biāo)地塊</param>public static List<Block> FindPathBlocks(Level level, Role role, Block block){if (level == null || role == null || block == null){_resultBlocks.Clear();return _resultBlocks;}CurrentMoveRule.CurrentLevel = level;CurrentMoveRule.CurrentRole = role;//Pathfinding 為 A* 尋路方法,具體參閱 HTFrameworkAI//參數(shù)1:role.StayBlock.Pos 尋路起點//參數(shù)2:block.Pos 尋路終點//參數(shù)3:傳入自定義尋路規(guī)則List<AStarNode> nodes = level.Map.Pathfinding(role.StayBlock.Pos, block.Pos, CurrentMoveRule);_resultBlocks.Clear();for (int i = 0; i < nodes.Count; i++){_resultBlocks.Add(level.Blocks[nodes[i].XIndex, nodes[i].YIndex]);}return _resultBlocks;}
當(dāng)然,這里的角色移動動畫
涉及到戰(zhàn)斗系統(tǒng)
中的內(nèi)容了,在我們的進(jìn)程中它還不存在,我們先忽略。
五、角色攻擊尋路
1.自定義尋路規(guī)則
同樣的,角色攻擊尋路也必須單獨自定義一個尋路規(guī)則
:
/// <summary>/// 尋路規(guī)則(角色攻擊)/// </summary>public class AttackRule : AStarRule{/// <summary>/// 當(dāng)前的關(guān)卡/// </summary>public Level CurrentLevel;/// <summary>/// 當(dāng)前尋路的角色/// </summary>public Role CurrentRole;public override void Apply(AStarNode node){Block block = CurrentLevel.Blocks[node.XIndex, node.YIndex];switch (block.Type){case BlockType.Obstacle://遵循我們一開始制定的規(guī)則,只有【障礙】是不可跨越攻擊的,其他的都可//且攻擊尋路時,任何類型的地塊均不會產(chǎn)生額外的削減(OCost = 0)node.OCost = 0;node.IsCanWalk = false;break;default:node.OCost = 0;node.IsCanWalk = true;break;}}}
2.尋找角色的攻擊范圍內(nèi)的地塊
角色攻擊前,能夠根據(jù)所選要訣的攻擊距離
,周圍地塊屬性
等,尋找出所有在攻擊范圍內(nèi)的地塊:
private static AttackRule _attackRule;/// <summary>/// 尋路規(guī)則(攻擊)/// </summary>private static AttackRule CurrentAttackRule{get{if (_attackRule == null){_attackRule = new AttackRule();}return _attackRule;}}/// <summary>/// 尋找角色的攻擊范圍內(nèi)的地塊/// </summary>/// <param name="level">關(guān)卡</param>/// <param name="role">角色</param>/// <param name="ability">使用的要訣</param>public static List<Block> FindAttackableBlocks(Level level, Role role, Ability ability){if (level == null || role == null || ability == null){_resultBlocks.Clear();return _resultBlocks;}CurrentAttackRule.CurrentLevel = level;CurrentAttackRule.CurrentRole = role;//參數(shù)1:role.StayBlock.Pos 尋路起點//參數(shù)2:ability.AttackDistance 攻擊距離//參數(shù)3:傳入自定義尋路規(guī)則List<AStarNode> nodes = level.Map.WalkableNodefinding(role.StayBlock.Pos, ability.AttackDistance, CurrentAttackRule);_resultBlocks.Clear();for (int i = 0; i < nodes.Count; i++){_resultBlocks.Add(level.Blocks[nodes[i].XIndex, nodes[i].YIndex]);}return _resultBlocks;}
當(dāng)然,如此尋找出來的是所有在攻擊距離
內(nèi)的地塊,我們只需要判斷上面是否站有敵人,就能搜羅出周圍所有能夠被攻擊的敵人,以供玩家選擇了。
六、角色登場尋路
此處有一個難點,那就是我們設(shè)定為延后登場
的角色,如果他的登場地塊
在特殊情況下被占用了(一個地塊只能站一個角色),那么就需要基于其登場地塊
尋找四周的最近的空地塊,以便于完成登場任務(wù):
/// <summary>/// 以當(dāng)前地塊為起點,尋找周圍最近的沒有停留角色的地塊/// </summary>/// <param name="level">關(guān)卡</param>/// <param name="block">當(dāng)前地塊</param>public static Block FindNullBlock(Level level, Block block){if (level == null || block == null || block.StayRole == null)return block;//開啟列表:存放所有【未知地塊】,需檢測其是否【合格】(合格:沒有停留角色的【地面】類型地塊)List<Block> openList = new List<Block>();//關(guān)閉列表:存放所有【已知地塊】HashSet<Block> closeList = new HashSet<Block>();//相鄰列表HashSet<Block> neighborList = new HashSet<Block>();//從當(dāng)前地塊開始openList.Add(block);//如果存在【未知地塊】while (openList.Count > 0){//獲取該【未知地塊】,同時該地塊轉(zhuǎn)為【已知地塊】Block b = openList[0];openList.RemoveAt(0);closeList.Add(b);//發(fā)現(xiàn)合格地塊,直接返回if (b.Type == BlockType.Ground && b.StayRole == null){return b;}else{//否則,獲取其周圍九宮格范圍內(nèi)的地塊neighborList.Clear();GetNeighborBlock(level, b, neighborList);//檢測這些地塊foreach (var item in neighborList){//如果該地塊不是【已知地塊】,將其添加到【未知地塊】if (!closeList.Contains(item) && !openList.Contains(b)){openList.Add(item);}}}}//如果整個關(guān)卡都搜完了還是沒有空地塊,那......return null;}/// <summary>/// 獲取一個地塊的相鄰地塊(九宮格)/// </summary>/// <param name="level">關(guān)卡</param>/// <param name="block">地塊</param>/// <param name="blocks">緩存列表</param>private static void GetNeighborBlock(Level level, Block block, HashSet<Block> blocks){if (level == null || block == null || blocks == null)return;for (int i = -1; i <= 1; i++){for (int j = -1; j <= 1; j++){if (i == 0 && j == 0)continue;Vector2Int index = block.Pos + new Vector2Int(i, j);if (index.x >= 0 && index.x < level.MapSize.x && index.y >= 0 && index.y < level.MapSize.y){blocks.Add(level.Blocks[index.x, index.y]);}}}}
七、整合
如上我們的尋路系統(tǒng)
功能也完成得七七八八了,我決定將其整合到一個靜態(tài)類中:
/// <summary>/// RPG2D實用工具/// </summary>public static class RPG2DUtility{/// <summary>/// 尋路系統(tǒng)/// </summary>public static class FindSystem{//我們前面編寫的各種方法........}}
這樣的話,后續(xù)調(diào)用就十分簡單明了:
//求得所有能夠移動的地塊List<Block> blocks = RPG2DUtility.FindSystem.FindWalkableBlocks(_level, player);