需求:鱼儿鱼儿水中游
鱼儿在海底世界嬉戏
1、触摸鱼儿时候,鱼儿受到惊吓,随机快速移动。
2、触摸空白区域,在触摸点添加鱼食,当鱼食在鱼儿可视范围内,鱼儿快速移动,追赶鱼食,把它吃掉。
3、海底下方,随机产生气泡,气泡向上飘浮,当到达生存时间或者被触摸后,鱼儿若在气泡爆炸范围,鱼儿受到惊吓,随机快速移动。
4、后续添加一个鱼儿进场过程。
这里鱼儿动画比较简单,只有一份 idle 动作即可,鱼儿受惊快速移动,可以加快动画播放速度即可。
根据需求,可以给鱼儿编写一份有限无穷状态机。鱼儿在某个时间点只处于一种状态,只是在该状态下,做自己相应的事情罢了。
好了步入正题,开始限无穷状态机的编写。
FSM.cs
using UnityEngine;
using System.Collections;
public class FSM : MonoBehaviour
{
//Fish GameObject
protected GameObject goFish;
//Player Transform
public Transform oceanTransform;
//Next destination position of the NPC Tank
protected Vector3 destPos;
protected float elapsedTime;
protected virtual void Initialize() {}
protected virtual void FSMUpdate() {}
protected virtual void FSMFixedUpdate() {}
//Use this for initialization
void Start()
{
Initialize();
}
// Update is called once per frame
void Update ()
{
FSMUpdate();
}
void FixedUpdate()
{
FSMFixedUpdate();
}
}
FSMState.cs
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
/// <summary>
/// This class is adapted and modified from the FSM implementation class available on UnifyCommunity website
/// The license for the code is Creative Commons Attribution Share Alike.
/// It's originally the port of C++ FSM implementation mentioned in Chapter01 of Game Programming Gems 1
/// You're free to use, modify and distribute the code in any projects including commercial ones.
/// Please read the link to know more about CCA license @http://creativecommons.org/licenses/by-sa/3.0/
/// This class represents the States in the Finite State System.
/// Each state has a Dictionary with pairs (transition-state) showing
/// which state the FSM should be if a transition is fired while this state
/// is the current state.
/// Reason method is used to determine which transition should be fired .
/// Act method has the code to perform the actions the NPC is supposed to do if it磗 on this state.
/// </summary>
public abstract class FSMState
{
protected Dictionary<Transition, FSMStateID> map = new Dictionary<Transition, FSMStateID>();
protected FSMStateID stateID;
public FSMStateID ID { get { return stateID; } }
// 以下几个参数,是鱼儿相应的参数设置,在继承各个状态机时使用 觉得写这里 不是很好,不过懒得改 就这样吧
protected Vector3 destPos; // 目标位置
protected float curRotSpeed; // 惊吓后速度参数
protected float curSpeed; // 自身游动速度参数
protected float chaseDistance = 10.0f; // 鱼儿追赶鱼食范围
public void AddTransition(Transition transition, FSMStateID id)
{
//Since this is a Deterministc FSM,
//Check if the current transition was already inside the map
if (map.ContainsKey(transition))
{
Debug.LogWarning("FSMState ERROR: transition is already inside the map");
return;
}
map.Add(transition, id);
Debug.Log("Added : " + transition + " with ID : " + id);
}
/// <summary>
/// This method deletes a pair transition-state from this state磗 map.
/// </summary>
public void DeleteTransition(Transition trans)
{
// Check if the pair is inside the map before deleting
if (map.ContainsKey(trans))
{
map.Remove(trans);
return;
}
Debug.LogError("FSMState ERROR: Transition passed was not on this State磗 List");
}
/// <summary>
/// This method returns the new state the FSM should be if
/// this state receives a transition
/// </summary>
public FSMStateID GetOutputState(Transition trans)
{
return map[trans];
}
/// <summary>
/// Decides if the state should transition to another on its list
/// NPC is a reference to the npc tha is controlled by this class
/// </summary>
public abstract void Reason(GameObject fish);
/// <summary>
/// This method controls the behavior of the NPC in the game World.
/// Every action, movement or communication the NPC does should be placed here
/// NPC is a reference to the npc tha is controlled by this class
/// </summary>
public abstract void Act(GameObject fish);
/// <summary>
/// Find the next semi-random patrol point
/// </summary>
public void FindNextPoint()
{
//Debug.Log("Finding next point");
}
/// <summary>
/// Check whether the next random position is the same as current tank position
/// </summary>
/// <param name="pos">position to check</param>
/*
protected bool IsInCurrentRange(Transform trans, Vector3 pos)
{
float xPos = Mathf.Abs(pos.x - trans.position.x);
float zPos = Mathf.Abs(pos.z - trans.position.z);
if (xPos <= 50 && zPos <= 50)
return true;
return false;
}*/
}
定义俩个枚举,用来处理状态机
public enum Transition
{
SawFood = 0, // 看见发现食物
ReachFood, // 追逐抵达食物
LostFood, // 失去食物(有可能被别的鱼吃了、脱离视野、掉入海底)
BombBubble, // 气泡爆炸
TouchFish, // 鱼儿被触摸
Patrolle, // 巡逻(普通)
ComeInto, // 进场
ComeEnd, // 进场结束
NoTime, // 没有时间了
}
public enum FSMStateID
{
Patrolling = 0, // 巡逻 状态
Showtiming, // 表演 状态
Chasing, // 追赶 状态
Frightening, // 受惊 状态
Eatting, // 吃食 状态
Coming, // 进场 状态
Disappearing, // 消失 状态
}
AdvancedFSM.cs
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
/// <summary>
/// This class is adapted and modified from the FSM implementation class available on UnifyCommunity website
/// The license for the code is Creative Commons Attribution Share Alike.
/// It's originally the port of C++ FSM implementation mentioned in Chapter01 of Game Programming Gems 1
/// You're free to use, modify and distribute the code in any projects including commercial ones.
/// Please read the link to know more about CCA license @http://creativecommons.org/licenses/by-sa/3.0/
/// </summary>
public class AdvancedFSM : FSM
{
private List<FSMState> fsmStates;
//The fsmStates are not changing directly but updated by using transitions
private FSMStateID currentStateID;
public FSMStateID CurrentStateID { get { return currentStateID; } }
private FSMState currentState;
public FSMState CurrentState { get { return currentState; } }
public AdvancedFSM()
{
fsmStates = new List<FSMState>();
}
/// <summary>
/// Add New State into the list
/// </summary>
public void AddFSMState(FSMState fsmState)
{
// Check for Null reference before deleting
if (fsmState == null)
{
Debug.LogError("FSM ERROR: Null reference is not allowed");
}
// First State inserted is also the Initial state
// the state the machine is in when the simulation begins
if (fsmStates.Count == 0)
{
fsmStates.Add(fsmState);
currentState = fsmState;
currentStateID = fsmState.ID;
return;
}
// Add the state to the List if it磗 not inside it
foreach (FSMState state in fsmStates)
{
if (state.ID == fsmState.ID)
{
Debug.LogError("FSM ERROR: Trying to add a state that was already inside the list");
return;
}
}
//If no state in the current then add the state to the list
fsmStates.Add(fsmState);
}
//This method delete a state from the FSM List if it exists,
public void DeleteState(FSMStateID fsmState)
{
// Search the List and delete the state if it磗 inside it
foreach (FSMState state in fsmStates)
{
if (state.ID == fsmState)
{
fsmStates.Remove(state);
return;
}
}
Debug.LogError("FSM ERROR: The state passed was not on the list. Impossible to delete it");
}
/// <summary>
/// This method tries to change the state the FSM is in based on
/// the current state and the transition passed.
/// </summary>
public void PerformTransition(Transition trans)
{
// Check if the currentState has the transition passed as argument
FSMStateID id = currentState.GetOutputState(trans);
// Update the currentStateID and currentState
currentStateID = id;
foreach (FSMState state in fsmStates)
{
if (state.ID == currentStateID)
{
currentState = state;
break;
}
}
}
}
鱼儿实体挂载上 AIController.cs
using UnityEngine;
using System.Collections;
public class AIController : AdvancedFSM
{
private int survivalTime; // 生存时间
public bool isNPC = true;
protected override void Initialize()
{
goFish = this.gameObject;
survivalTime = 60 * 3; // 生存时间 60秒 * 3
//Start Doing the Finite State Machine
ConstructFSM ();
}
//Update each frame
protected override void FSMUpdate()
{
//Check for health
elapsedTime += Time.deltaTime;
// 时间 到了 消失
if (elapsedTime > survivalTime && isNPC == false)
SetTransition(Transition.NoTime);
}
protected override void FSMFixedUpdate()
{
CurrentState.Reason(goFish);
CurrentState.Act(goFish);
}
public void SetTransition(Transition t)
{
PerformTransition(t);
}
public void TouchFishAC()
{
int TouchID = Random.Range(1, 6);
string strTouch = "Sound/yu0" + TouchID.ToString();
AudioClip ACTouch = Resources.Load(strTouch) as AudioClip;
AudioSource.PlayClipAtPoint(ACTouch, transform.localPosition);
}
private void ConstructFSM()
{
GameObject objOcean = GameObject.FindGameObjectWithTag("Ocean");
oceanTransform = objOcean.transform;
PatrolState patrol = new PatrolState (); // 游走
patrol.AddTransition (Transition.SawFood, FSMStateID.Chasing); // 发现鱼食 进入追食
patrol.AddTransition (Transition.BombBubble, FSMStateID.Frightening); // 气泡爆炸 进入受惊
patrol.AddTransition (Transition.TouchFish, FSMStateID.Frightening); // 触碰鱼 进入受惊
patrol.AddTransition (Transition.NoTime, FSMStateID.Disappearing); // 到时间 消失
ChaseState chase = new ChaseState(); // 追食
chase.AddTransition (Transition.ReachFood, FSMStateID.Eatting); // 追到鱼食 进入吃食
chase.AddTransition (Transition.LostFood, FSMStateID.Patrolling); // 鱼食消失 进入游走
chase.AddTransition (Transition.BombBubble, FSMStateID.Frightening); // 气泡爆炸 进入受惊
chase.AddTransition (Transition.TouchFish, FSMStateID.Frightening); // 触碰鱼 进入受惊
chase.AddTransition (Transition.NoTime, FSMStateID.Disappearing); // 到时间 消失
//EatState eat = new EatState (); // 吃食
FrightenState frighten = new FrightenState (); // 受惊
frighten.AddTransition (Transition.Patrolle, FSMStateID.Patrolling); // 受惊后 游走
frighten.AddTransition (Transition.NoTime, FSMStateID.Disappearing); // 到时间 消失
DisappearState disappear = new DisappearState (); // 离场
disappear.AddTransition(Transition.NoTime, FSMStateID.Disappearing); // 到时间 消失
ComeState comeinto = new ComeState ();
comeinto.AddTransition (Transition.ComeInto, FSMStateID.Coming);
comeinto.AddTransition (Transition.ComeEnd, FSMStateID.Patrolling);
//AddFSMState (comeinto);
AddFSMState (patrol);
AddFSMState (chase);
//AddFSMState (eat);
AddFSMState (frighten);
AddFSMState (disappear);
//AddFSMState (comeinto);
}
}
好了,我们现在实现
PatrolState、ChaseState、FrightenState、DisappearState、ComeState 在相应的状态下做相应的事情就ok了。
PatrolState.cs
using UnityEngine;
using System.Collections;
public class PatrolState : FSMState
{
float intervalTime = 4.0f; // 间隔时间
float patrolTime = 0.0f;
int patrolPointID = 2;
Vector3 selfPoint;
GameObject goPatrol;
public PatrolState()
{
intervalTime = Random.Range(3.5f, 8.0f);
stateID = FSMStateID.Patrolling;
curRotSpeed = 3.0f;
curSpeed = 0.1f;
goPatrol = GameObject.FindGameObjectWithTag("PatrolPoint");
destPos = goPatrol.transform.Find (patrolPointID.ToString ()).transform.position;
}
public void FindNextPoint(GameObject fish)
{
intervalTime = Random.Range(5.5f, 8.0f);
selfPoint = fish.transform.position;
destPos = goPatrol.transform.Find (patrolPointID.ToString ()).transform.position;
}
public override void Reason(GameObject fish)
{
//fish.GetComponent<AIController> ().SetTransition (Transition.SawFood);
AIController aiController = fish.GetComponent<AIController> ();
if (aiController != null)
{
OceanManage oceanManage = aiController.oceanTransform.GetComponent<OceanManage> ();
GameObject goFish = oceanManage.FindFishFood (fish);
if (goFish != null) {
float distance = Vector3.Distance(fish.transform.position, goFish.transform.position);
if (distance < chaseDistance)
aiController.SetTransition (Transition.SawFood);
}
}
}
public override void Act(GameObject fish)
{
if ((patrolTime += Time.deltaTime) > intervalTime) {
patrolTime = 0.0f;
patrolPointID = Random.Range(1, goPatrol.gameObject.transform.childCount + 1);
FindNextPoint(fish);
}
Quaternion targetRotation = Quaternion.LookRotation(destPos - fish.transform.position);
Quaternion rotateQuaterntion = Quaternion.identity;
rotateQuaterntion.eulerAngles = new Vector3(0f, 90.0f, 0f);
fish.transform.rotation = Quaternion.Slerp(fish.transform.rotation, targetRotation * rotateQuaterntion, Time.deltaTime * curRotSpeed);
//fish.transform.LookAt(destPos);//鱼随机方向
//fish.transform.Rotate(new Vector3(0, 90, 0));
fish.transform.position = Vector3.Lerp(fish.transform.position, destPos, Time.deltaTime * curSpeed);
Animator animComponent = fish.GetComponent<Animator>();
animComponent.CrossFade("idle", 0);
animComponent.speed = 1f;
}
}
ChaseState.cs
using UnityEngine;
using System.Collections;
public class ChaseState : FSMState
{
public ChaseState()
{
stateID = FSMStateID.Chasing;
curRotSpeed = 3.0f;
curSpeed = 2.75f;
}
public override void Reason(GameObject fish)
{
AIController aiController = fish.GetComponent<AIController> ();
OceanManage oceanManage = aiController.oceanTransform.GetComponent<OceanManage> ();
GameObject goFish = oceanManage.FindFishFood (fish);
if (goFish == null) {
aiController.SetTransition (Transition.LostFood);
}
}
public override void Act(GameObject fish)
{
AIController aiController = fish.GetComponent<AIController> ();
OceanManage oceanManage = aiController.oceanTransform.GetComponent<OceanManage> ();
GameObject goFish = oceanManage.FindFishFood (fish);
if (goFish != null) {
float distance = Vector3.Distance(fish.transform.position, goFish.transform.position);
if (distance < chaseDistance)
{
fish.transform.LookAt(goFish.transform.position);//鱼随机方向
fish.transform.Rotate(new Vector3(0, 90, 0));
fish.transform.position = Vector3.Lerp(fish.transform.position, goFish.transform.position, Time.deltaTime * curSpeed);
Animator animComponent = fish.GetComponent<Animator>();
animComponent.CrossFade("idle", 0);
animComponent.speed = 1f;
}
else
{
aiController.SetTransition (Transition.LostFood);
}
//animComponent.animation.s
}
}
}
FrightenState.cs
using UnityEngine;
using System.Collections;
public class FrightenState :FSMState
{
float intervalTime = 1.0f; // 间隔时间
float patrolTime = 0.0f;
GameObject goPatrol;
int patrolPointID = 2;
public FrightenState()
{
intervalTime = 1.5f;
stateID = FSMStateID.Frightening;
curRotSpeed = 3.0f;
curSpeed = 0.7f;
goPatrol = GameObject.FindGameObjectWithTag("PatrolPoint");
}
public override void Reason(GameObject fish)
{
if (patrolTime == 0)
{
patrolPointID = Random.Range(1, goPatrol.gameObject.transform.childCount + 1);
destPos = goPatrol.transform.Find(patrolPointID.ToString()).transform.position;
}
}
public override void Act(GameObject fish)
{
if ((patrolTime += Time.deltaTime) > intervalTime)
{
patrolTime = 0.0f;
AIController aiController = fish.GetComponent<AIController>();
aiController.SetTransition(Transition.Patrolle);
}
Quaternion targetRotation = Quaternion.LookRotation(destPos - fish.transform.position);
Quaternion rotateQuaterntion = Quaternion.identity;
rotateQuaterntion.eulerAngles = new Vector3(0f, 90.0f, 0f);
fish.transform.rotation = Quaternion.Slerp(fish.transform.rotation, targetRotation * rotateQuaterntion, Time.deltaTime * curRotSpeed);
//fish.transform.LookAt(destPos);//鱼随机方向
//fish.transform.Rotate(new Vector3(0, 90, 0));
fish.transform.position = Vector3.Lerp(fish.transform.position, destPos, Time.deltaTime * curSpeed );
Animator animComponent = fish.GetComponent<Animator>();
animComponent.CrossFade("idle", 0);
animComponent.speed = 2f;
}
}
DisappearState.cs
using UnityEngine;
using System.Collections;
public class DisappearState : FSMState
{
public DisappearState()
{
stateID = FSMStateID.Disappearing;
}
public override void Reason(GameObject fish)
{
}
public override void Act(GameObject fish)
{
GameObject oceanGO = GameObject.Find("Ocean");
if (oceanGO) {
OceanManage ocean = oceanGO.GetComponent<OceanManage> ();
ocean.MakeBubble(fish);
}
GameObject.Destroy (fish);
}
}
ComeState.cs
using UnityEngine;
using System.Collections;
public class ComeState : FSMState
{
int comeIntoPointID = 1;
GameObject goComeInto;
Hashtable pathHashtable;
float intervalTime = 1.0f; // 间隔时间
float comeintoTime = 0.0f;
// Use this for initialization
public ComeState()
{
stateID = FSMStateID.Coming;
goComeInto = GameObject.FindGameObjectWithTag("ComeIntoPoint");
}
public void SetComeIntoPath(GameObject fish)
{
comeIntoPointID = Random.Range(1, goComeInto.gameObject.transform.childCount + 1);
iTweenPath path = goComeInto.transform.Find (comeIntoPointID.ToString ()).GetComponent<iTweenPath>();
destPos = goComeInto.transform.Find (comeIntoPointID.ToString ()).transform.position;
fish.transform.position = path.nodes [0];
pathHashtable = new Hashtable();
//设置路径的点
pathHashtable.Add("path",path);
//设置类型为线性,线性效果会好一些。
pathHashtable.Add("easeType", iTween.EaseType.linear);
//设置寻路的速度
pathHashtable.Add("time",intervalTime);
//是否先从原始位置走到路径中第一个点的位置
pathHashtable.Add("movetopath",true);
//是否让模型始终面朝当面目标的方向,拐弯的地方会自动旋转模型
//如果你发现你的模型在寻路的时候始终都是一个方向那么一定要打开这个
pathHashtable.Add("orienttopath",true);
//让模型开始寻路
iTween.MoveTo(fish,pathHashtable);
}
public override void Reason(GameObject fish)
{
if (comeintoTime == 0.0f)
SetComeIntoPath (fish);
if ((comeintoTime += Time.deltaTime) > intervalTime) {
comeintoTime = 0.0f;
AIController aiController = fish.GetComponent<AIController> ();
aiController.SetTransition (Transition.ComeEnd);
}
}
public override void Act(GameObject fish)
{
Quaternion targetRotation = Quaternion.LookRotation(destPos - fish.transform.position);
Quaternion rotateQuaterntion = Quaternion.identity;
rotateQuaterntion.eulerAngles = new Vector3(0f, 90.0f, 0f);
fish.transform.rotation = Quaternion.Slerp(fish.transform.rotation, targetRotation * rotateQuaterntion, Time.deltaTime * curRotSpeed);
Animator animComponent = fish.GetComponent<Animator>();
animComponent.CrossFade("idle", 0);
animComponent.speed = 2f;
}
}
这个思想 就是这样,但具体实现根据具体功能需求而定和编写。
posted on 2018-09-20 14:11
vic.MINg 阅读(787)
评论(0) 编辑 收藏 引用 所属分类:
Unity 3D