泰坦之旅的ai
titan quest的AI用的是切換式的狀態(tài)機(jī),而尋路用的是path engine第3方庫(kù),游戲中有一個(gè)任務(wù)編輯器主要是生成每一個(gè)任務(wù),每個(gè)任務(wù)中可以生成多個(gè)觸發(fā)器(trigger),每個(gè)觸發(fā)器可以生成一系列條件(condition),并可生成條件成立時(shí)要觸發(fā)的動(dòng)作(action). 這個(gè)有點(diǎn)類似war3的事件編輯器。
以下是切換式狀態(tài)機(jī)跟蹤的一些記錄,很亂,沒(méi)寫(xiě)總結(jié),只是用于備忘。。。
AI移動(dòng)跟蹤
WinMain消息循環(huán)中的Game::Run()中先
1. gGameEngine->GetFrustumForPlayer(updateFrustum, player->GetCoords().origin); 得到frustum
2. gEngine->Update(&updateFrustum);進(jìn)行更新, 其中
調(diào)用 world->Update(worldFrustumList);
然后遍歷每個(gè)frustum取得對(duì)應(yīng)region進(jìn)行更新
a. 看看當(dāng)前region是否與frustum相交,如果是則load,否則擴(kuò)大一點(diǎn)再相交,這時(shí)如果相交則將添加到后臺(tái)加載。
b. 查看portal相關(guān)region進(jìn)行更新
3. 查看connectedRegions(在地圖裝載得到玩家出生位置后進(jìn)行玩家所在region擴(kuò)大后跟其它region判斷相交所得)進(jìn)行更新
3. Region的更新中進(jìn)行l(wèi)evel更新:
level->Update(frustumList, numFrustums, elapsedTime);
在其中先取得在frustum中的Entity, 然后再遍歷更新所有Entity,
之后再更新Entity所在4叉樹(shù)空間.
4. 角色的更新:
Entity的更新中, 先UpdateSelf再UpdateAttachedEntities
(Entity中有PhysicsRigidBody成員physicsObject)
5. UpdateSelf會(huì)跑到Character::PreAnimationUpdate中執(zhí)行 baseController->Update(localTimeDelta);從而跳到ContrallerAI中執(zhí)行GetExecutingState()->OnUpdate(deltaTime); 從而到達(dá)ControllerNpcStateIdle的更新中進(jìn)行狀態(tài)切換到SetState("Wander", ControllerAIStateData(0, 0, 0));
之后再跑到ControllerNpcStateWander::OnBegin()
處理:
int closestPoint = GetClosest(GetController().GetWanderPoints());
GetController().SetCurrentWanderPoint(closestPoint);
if (!MoveToCurrentWanderPoint())
{
SetState("Idle", ControllerAIStateData());
return;
}
這個(gè)在MoveToCurrentWanderPoint()函數(shù)里從隊(duì)列中取出當(dāng)前目標(biāo)點(diǎn)并ControllerAI::WalkTo
其中會(huì)GetController().WalkTo(location, target);即ControllerAI::WalkTo(。。), 這會(huì)執(zhí)行:
HandleAction(new WalkAction(GetParentId(), GetAI()->GetPathPosition(), location, target));
這個(gè)會(huì)執(zhí)行:
SetCurrentAction(action);
GetCurrentAction()->Execute();從而運(yùn)行了WalkAction::Execute(), 這其中又調(diào)用了Character::WalkTo
這又會(huì):
movementMgr->SetNewPathTarget進(jìn)行處理
最后在CharacterMovementManager::Update()中進(jìn)行角色位置更新:
CreateLocalPath(deltaTime, speed);
if (!MoveDownPath(deltaTime, speed))
{
return false;
}
UpdateCharacterPosition(deltaTime, speed);
void UIDialogWindow::OnOpen()會(huì)調(diào)用 myNpc->AddSocialTarget( target );
在void ControllerNpcStateIdle::OnUpdate(Time deltaTime)中判斷如果有SocialTarget則進(jìn)入Chat狀態(tài)處理
【狀態(tài)處理例子】
Monster的初始狀態(tài)是Idle,在Monster的更新函數(shù)里:
一)。進(jìn)行搜敵,并切換成pursue狀態(tài)
,并調(diào)追捕狀態(tài)的OnBegin()函數(shù)處理,如果Monster不能行走則切回Idle狀態(tài),否則如果搜不到敵
人則切換到Return狀態(tài),否則根據(jù)當(dāng)前技能id找出要移到的位置點(diǎn).
------------------------------------------------------
【注意】找到要移到的位置點(diǎn)細(xì)節(jié):
I. (Character::GetMoveToPoint)找出目標(biāo)點(diǎn):
1. 目標(biāo)是自己則直接return自己位置
2. 沒(méi)有目標(biāo)則保存goalPoint=目標(biāo)點(diǎn),distance=技能施放范圍,待后處理.
3. 目標(biāo)是FixedItem則return FixedItem->GetMoveToPoint(..)里進(jìn)行處理
4. 目標(biāo)是StrategicMovementBase,則return sm->GetMoveToPoint(..)里進(jìn)行處理
5. 目標(biāo)是Entity,則goalPoint = entity->GetCoords().origin;并且如果entity阻擋則讓goalPoint回移一點(diǎn)以免浮點(diǎn)出錯(cuò)?否則distance=GetExtents() + entity-
>GetExtents();待后處理
6. 目標(biāo)是Character,則,
a. 如果是朋友
1)如果當(dāng)前是移動(dòng)狀態(tài),則要求目標(biāo)給出DefenseSlot(防御位置點(diǎn))作為goalPoint直接返回.
2)否則如果能直線通路到目標(biāo)點(diǎn)的話就直接返回離目標(biāo)比較近的一點(diǎn)(去掉半徑),不能直通則返回0點(diǎn)
b. 如果是敵人
1)如果沒(méi)有技能,則goalPoint=目標(biāo)點(diǎn),distance=GetExtents() + target->GetExtents();待后處理
2)如果技能不需要AttackSlot或者this是Player, 則
goalPoint=目標(biāo)點(diǎn),distance = GetExtents() + target->GetExtents() + skill->GetRange();待后處理
3)否則直接返回目標(biāo)算出的AttackSlot攻擊點(diǎn)位置.
上述2和5以及6中的b.的1)和2)需要待后處理的最后通過(guò)
WorldVec3 finalPoint = movementMgr->GetPointAwayFromGoal(goalPoint, distance);
得到最終位置, 這個(gè)位置還要特殊判斷一下如果不在Region中或者路徑不能到達(dá)的話,則直接用TranslateToFloor到goalPoint.
【說(shuō)明】:
1. 什么是AttackSlot/DefenseSlot:
每個(gè)角色可以有n個(gè)x距離的AttackSlot/DefenseSlot,它會(huì)在周圍x半徑的圓上平分出n個(gè)位置點(diǎn),當(dāng)有其它人要攻擊它或者要來(lái)幫助(防御)它時(shí),它就會(huì)在旁邊找一個(gè)較近的還
沒(méi)其它人用過(guò)的slot分給這個(gè)其它人。
2. movementMgr->GetPointAwayFromGoal()函數(shù)細(xì)節(jié):
先是FindPath(目標(biāo))得出path,再用path->Advance(pathLength - distance)得到回退一點(diǎn)的位置。
II. 找到目標(biāo)點(diǎn)后,還要調(diào)用movementGoalManager->GetClosestMovePoint(目標(biāo)點(diǎn)) 進(jìn)行處理:
這個(gè)函數(shù)主要是給范圍武器用的,如果不是使用范圍武器的Monster則不會(huì)調(diào)整目標(biāo)點(diǎn)。
如果是的話則遍歷全局對(duì)象movementGoalManager中的m_MoveGoalMap目標(biāo)點(diǎn)映射表,求出其它在同一region中的Monster
所在目標(biāo)點(diǎn)跟當(dāng)前Monster所在目標(biāo)點(diǎn)的距離,如果距離比較近則調(diào)用GetPointAwayFromGoal(目標(biāo)點(diǎn), 3.0);調(diào)整當(dāng)前Monster的目標(biāo)點(diǎn)回退一點(diǎn),并將些處理后的位置及些
Monsterid映射到m_MoveGoalMap目標(biāo)點(diǎn)映射表中。這樣遍歷過(guò)所有其它Monster的目標(biāo)點(diǎn)進(jìn)行一一檢測(cè)處理后就會(huì)盡量避免與其它Monster擠到一起。
------------------------------------------------------
找到要移到的位置點(diǎn)之后,
1. 用(CloseEnoughToUseSkill(GetCurrentEnemy(), GetCurrentSkillID()))判斷是否在技能攻擊范圍內(nèi),
如果在則用IsPathClear(GetCurrentEnemy())來(lái)判斷是不是到直通目標(biāo),是則切換成Attack狀態(tài)后返回,否則切換成NavigateObstacle狀態(tài)后返回。
2. 否則敵人不在攻擊范圍內(nèi)就看當(dāng)前是否已站在目標(biāo)點(diǎn),是則切回Idle狀態(tài)后返回
3. 不在目標(biāo)點(diǎn)則看是不是能夠移到目標(biāo)點(diǎn),不能則切回Idle狀態(tài)后返回。
4. 能移到目標(biāo)點(diǎn)則MoveTo到目標(biāo)點(diǎn).
這個(gè)MoveTo會(huì)調(diào)用
HandleAction(new MoveToAction(GetParentId(), GetAI()->GetPathPosition(), location, target, GetAI()->GetSkillReferenceNumber(skill), 1.0, animType));
這個(gè)會(huì)執(zhí)行到MoveToAction, 其中會(huì)轉(zhuǎn)調(diào):
monseter->SetCurrentAttackTarget(targetId, location, skillNumber);
monseter->SkillWarmUp( skillNumber, false );
monseter->MoveTo(location, GetBlendTime(), animType);
monseter->PlayLoopingRunningSound();
而monseter->MoveTo又會(huì)調(diào)用 movementMgr->SetNewPathTarget(movementMgr->GetPathPosition(), surfacePoint, alreadyThere))
然后再用SetActionState(Character_ActionState_Move);設(shè)置Action的狀態(tài)為Move,并通過(guò)PlayAnimation播放run動(dòng)作(即調(diào)用GetAnimationBase( type ).PlayAnimation(
actor, selection, speedModifier, loop, iteration ),這個(gè)可以參考我另一個(gè)動(dòng)畫(huà)跟蹤文檔看細(xì)節(jié))
二)。搜敵后會(huì)接著調(diào)用ControllerAI::Update()更新函數(shù):
1. 先進(jìn)行當(dāng)前狀態(tài)更新()
由于前面Monster切換到了pursue追捕狀態(tài),所以執(zhí)行到
ControllerMonsterStatePursue::OnUpdate(),其中:
a. 追捕所用時(shí)間過(guò)了,則切換回return狀態(tài)
b. 重新選擇技能時(shí)間到了則重新找出一個(gè)bestSkill.(這也避免了萬(wàn)一當(dāng)前技能是melee,而玩家總是繞著Monster轉(zhuǎn),怪就會(huì)不停地追不上而沒(méi)法肉搏攻擊)
c. 用CloseEnoughToUseSkill判斷是否夠技能施放距離,夠的話用IsPathClear判斷攻擊方向是否可通,是則轉(zhuǎn)Attack狀態(tài),不通則轉(zhuǎn)NavigateObstacle狀態(tài).
2. 再遍歷執(zhí)行m_PreloadQuestActionList中action.
上述都在【更祥細(xì)一點(diǎn)】中1。Character::UpdateSelf()中進(jìn)行
接著會(huì)到【更祥細(xì)一點(diǎn)】中2。 Update subsystems:中的FollowPath()進(jìn)行真正的移動(dòng)處理.
posted on 2012-08-13 10:09
flipcode 閱讀(367)
評(píng)論(0) 編輯 收藏 引用