AI决策算法之GOAP(三)
发表于2018-02-26
上篇给大家分别介绍了 IGoal,Agent,Action,Planer,FSM的实现,这篇我们就用上篇文章写的GOAP框架来完成一个实例:
实例内容:
(1)AI有10HP, 需要去站岗,站岗完成扣5HP
(2)当HP<=5必须去补充HP,找到HP球补充HP,每个HP球补充5HP
根据GOAP框架逻辑推断出需要:AI数据提供者,站岗Action,补充HPAction,HP点脚本,站岗点脚本, AI属性脚本
主要脚本:
AI数据提供者
public class Worker : MonoBehaviour,IGoap{ private PropertyComponent property; //属性脚本 public float moveSpeed = 3; //移动速度 void Start() { property = GetComponent<PropertyComponent>(); } public System.Collections.Generic.HashSet<System.Collections.Generic.KeyValuePair<string, object>> GetState() { HashSet<KeyValuePair<string, object>> state = new HashSet<KeyValuePair<string, object>>(); //当前状态HP是否足够 state.Add(new KeyValuePair<string, object>("EnoughHP", property.HP > 5)); return state; } public System.Collections.Generic.HashSet<System.Collections.Generic.KeyValuePair<string, object>> CreateGoalState() { HashSet<KeyValuePair<string, object>> goal = new HashSet<KeyValuePair<string, object>>(); //站岗完成目标 goal.Add(new KeyValuePair<string, object>("SentryComplete", true)); return goal; } public void PlanFailed(System.Collections.Generic.HashSet<System.Collections.Generic.KeyValuePair<string, object>> failedGoal) { } public void PlanFound(System.Collections.Generic.HashSet<System.Collections.Generic.KeyValuePair<string, object>> goal, System.Collections.Generic.Queue<Action> actions) { } public void ActionsFinished() { Debug.LogError("FinishedSentry"); } public void PlanAborted(Action aborterAction) { } public bool MoveAgent(Action tagetAction) { //移动 float step = moveSpeed * Time.deltaTime; gameObject.transform.position = Vector3.MoveTowards(gameObject.transform.position, tagetAction.target.transform.position, step); if (gameObject.transform.position.Equals(tagetAction.target.transform.position)) { tagetAction.IsInRange = true; return true; } else return false; } }
站岗Action
public class SentryAction : Action { private SentryComponent targetSentry; //站岗目标脚本 private float SentryTimer = 0; //站岗计时 public float SentryTime = 3; bool isDone = false; //是否完成 public SentryAction() { AddPrecondition("EnoughHP", true); //前置条件:必须HP足够 AddEffect("SentryComplete", true); //完成效果:站岗完成 } public override void Reset() { targetSentry = null; SentryTimer = 0; isDone = false; } public override bool IsDone() { return isDone; } public override bool CheckProcedualPrecondition(GameObject agent) { //得到最近的站岗目标 SentryComponent[] sentryComponents = GameObject.FindObjectsOfType<SentryComponent>(); SentryComponent temp = null; foreach(var v in sentryComponents) { if (temp == null) { temp = v; continue; } if (Vector3.Distance(agent.transform.position, v.transform.position) < Vector3.Distance(agent.transform.position, temp.transform.position)) temp = v; } targetSentry = temp; target = temp.gameObject; return temp != null; } public override bool Perform(GameObject agent) { //站岗执行 SentryTimer += Time.deltaTime; if(SentryTimer > SentryTime) { //站岗完成消耗HP agent.GetComponent<PropertyComponent>().HP -= 5; isDone = true; } return true; } public override bool RequiresInRange() { return true; } }
补充HPAction
public class GetHPAction : Action { private HPPointComponent targetHPPoint; //HP点目标脚本 bool isDone = false; void Start() { AddEffect("EnoughHP", true); //完成效果:HP补充到足够 } public override void Reset() { targetHPPoint = null; isDone = false; } public override bool IsDone() { return isDone; } public override bool CheckProcedualPrecondition(GameObject agent) { HPPointComponent[] tempComponents = GameObject.FindObjectsOfType<HPPointComponent>(); HPPointComponent temp = null; foreach (var v in tempComponents) { if (temp == null) { temp = v; continue; } if (Vector3.Distance(agent.transform.position, v.transform.position) < Vector3.Distance(agent.transform.position, temp.transform.position)) temp = v; } targetHPPoint = temp; target = temp.gameObject; return temp != null; } public override bool Perform(GameObject agent) { DestroyImmediate(targetHPPoint.gameObject); isDone = true; agent.GetComponent<PropertyComponent>().HP += 5; return true; } public override bool RequiresInRange() { return true; } }