unity3d 游戏人工智能开发之状态机(C#模板与示例)

Finite State Machine

状态机

This is a Deterministic Finite State Machine framework based on chapter 3.1 of Game Programming Gems 1 by Eric Dybsend. Therea are two classes and two enums. Include them in your project and follow the explanations to get the FSM working properly. There‘s also
a complete example script at the end of this page.

这是一个确定性的有限状态机框架基于Game Programming Gems 1的第3.1章节。有两个类和两个枚举。把它们放入你的项目中并遵循注释FSM才能正常工作。后面还有一个完整的示例脚本。

Transition enum: This enum contains the labels to the transitions that can be fired by the system. Don‘t change the first label, NullTransition, as the FSMSystem class uses it.

转换枚举:该枚举包含标签转换。不要改变第一个标签,NullTransition ,作为FSMSystem类使用它。

StateID enum: This is the ID of the states the game may have. You could use references to the real States‘ classes but using enums makes the system less susceptible to have code having access to objects it is not
supposed to. All the states‘ ids should be placed here. Don‘t change the first label, NullStateID, as the FSMSystem class uses it.

状态ID枚举:这是游戏中也许有的状态的ID。你可以用来标记真状态的类,但使用枚举不应该使系统受影响太少而不能有代码访问物体。所有状态的id都应该放在这里。不要改变第一个标签,NullTransition ,作为FSMSystem类使用它。

FSMState class: This class has a Dictionary with pairs (Transition-StateID) indicating which new state S2 the FSM should go to when a transition T is fired and the current state is S1. It has methods to add and
delete pairs (Transition-StateID), a method to check which state to go to if a transition is passed to it. Two methods are used in the example given to check which transition should be fired (Reason()) and which action(s) (Act()) the GameObject that has the
FSMState attached should do. You don‘t have to use this schema, but some kind of transition-action code must be used in your game.

状态机状态类:这类有一个字典(Dictionary)包括一对(Transition-StateID)标志,当一个变换T不需要了,当前状态是S1,FSM需要到达的新的状态S2,并且。它有添加和删除的一对(Transition-StateID)的方法,来检查如果一个变换作用于物体,将要变到哪个状态。示例中使用的两个方法去检查哪个变化应该被淘汰(Reason())和有FSMState的物体应该做什么动作(Act())。你不需要使用这个模式,但是必须在你的游戏中使用某种类型的转换操作(transition-action)的代码。

FSMSystem: This is the Finite State Machine class that each NPC or GameObject in your game must have in order to use the framework. It stores the NPC‘s States in a List, has methods to add and delete a state and
a method to change the current state based on a transition passed to it (PerformTransition()). You can call this method anywhere within your code, as in a collision test, or within Update() or FixedUpdate().

FSMSystem: 这是每个游戏中的NPC或物体有限状态机类替代使用框架。在一个列表中储存NPC的状态,有增加或删去状态的方法,有基于受到一个变换(PerformTransition())而改变当前状态的方法。你可以在代码中的任何地方调用这个方法,如在碰撞测试(collision test)中,或在Update()或FixedUpdate()中。

C#模板

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/**
A Finite State Machine System based on Chapter 3.1 of Game Programming Gems 1 by Eric Dybsand

Written by Roberto Cezar Bianchini, July 2010

How to use:
	1. Place the labels for the transitions and the states of the Finite State System
	    in the corresponding enums.

	2. Write new class(es) inheriting from FSMState and fill each one with pairs (transition-state).
	    These pairs represent the state S2 the FSMSystem should be if while being on state S1, a
	    transition T is fired and state S1 has a transition from it to S2. Remember this is a Deterministic FSM.
	    You can't have one transition leading to two different states.

	   Method Reason is used to determine which transition should be fired.
	   You can write the code to fire transitions in another place, and leave this method empty if you
	   feel it's more appropriate to your project.

	   Method Act has the code to perform the actions the NPC is supposed do if it's on this state.
	   You can write the code for the actions in another place, and leave this method empty if you
	   feel it's more appropriate to your project.

	3. Create an instance of FSMSystem class and add the states to it.

	4. Call Reason and Act (or whichever methods you have for firing transitions and making the NPCs
	     behave in your game) from your Update or FixedUpdate methods.

	Asynchronous transitions from Unity Engine, like OnTriggerEnter, SendMessage, can also be used,
	just call the Method PerformTransition from your FSMSystem instance with the correct Transition
	when the event occurs.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

/// <summary>
/// Place the labels for the Transitions in this enum.
/// Don't change the first label, NullTransition as FSMSystem class uses it.
/// </summary>
public enum Transition
{
    NullTransition = 0, // Use this transition to represent a non-existing transition in your system
}

/// <summary>
/// Place the labels for the States in this enum.
/// Don't change the first label, NullTransition as FSMSystem class uses it.
/// </summary>
public enum StateID
{
    NullStateID = 0, // Use this ID to represent a non-existing State in your system
}

/// <summary>
/// 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.
/// Method Reason is used to determine which transition should be fired .
/// Method Act has the code to perform the actions the NPC is supposed do if it's on this state.
/// </summary>
public abstract class FSMState
{
    protected Dictionary<Transition, StateID> map = new Dictionary<Transition, StateID>();
    protected StateID stateID;
    public StateID ID { get { return stateID; } }

    public void AddTransition(Transition trans, StateID id)
    {
        // Check if anyone of the args is invalid
        if (trans == Transition.NullTransition)
        {
            Debug.LogError("FSMState ERROR: NullTransition is not allowed for a real transition");
            return;
        }

        if (id == StateID.NullStateID)
        {
            Debug.LogError("FSMState ERROR: NullStateID is not allowed for a real ID");
            return;
        }

        // Since this is a Deterministic FSM,
        //   check if the current transition was already inside the map
        if (map.ContainsKey(trans))
        {
            Debug.LogError("FSMState ERROR: State " + stateID.ToString() + " already has transition " + trans.ToString() +
                           "Impossible to assign to another state");
            return;
        }

        map.Add(trans, id);
    }

    /// <summary>
    /// This method deletes a pair transition-state from this state's map.
    /// If the transition was not inside the state's map, an ERROR message is printed.
    /// </summary>
    public void DeleteTransition(Transition trans)
    {
        // Check for NullTransition
        if (trans == Transition.NullTransition)
        {
            Debug.LogError("FSMState ERROR: NullTransition is not allowed");
            return;
        }

        // Check if the pair is inside the map before deleting
        if (map.ContainsKey(trans))
        {
            map.Remove(trans);
            return;
        }
        Debug.LogError("FSMState ERROR: Transition " + trans.ToString() + " passed to " + stateID.ToString() +
                       " was not on the state's transition list");
    }

    /// <summary>
    /// This method returns the new state the FSM should be if
    ///    this state receives a transition and
    /// </summary>
    public StateID GetOutputState(Transition trans)
    {
        // Check if the map has this transition
        if (map.ContainsKey(trans))
        {
            return map[trans];
        }
        return StateID.NullStateID;
    }

    /// <summary>
    /// This method is used to set up the State condition before entering it.
    /// It is called automatically by the FSMSystem class before assigning it
    /// to the current state.
    /// </summary>
    public virtual void DoBeforeEntering() { }

    /// <summary>
    /// This method is used to make anything necessary, as reseting variables
    /// before the FSMSystem changes to another one. It is called automatically
    /// by the FSMSystem before changing to a new state.
    /// </summary>
    public virtual void DoBeforeLeaving() { } 

    /// <summary>
    /// This method decides if the state should transition to another on its list
    /// NPC is a reference to the object that is controlled by this class
    /// </summary>
    public abstract void Reason(GameObject player, GameObject npc);

    /// <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 object that is controlled by this class
    /// </summary>
    public abstract void Act(GameObject player, GameObject npc);

} // class FSMState

/// <summary>
/// FSMSystem class represents the Finite State Machine class.
///  It has a List with the States the NPC has and methods to add,
///  delete a state, and to change the current state the Machine is on.
/// </summary>
public class FSMSystem
{
    private List<FSMState> states;

    // The only way one can change the state of the FSM is by performing a transition
    // Don't change the CurrentState directly
    private StateID currentStateID;
    public StateID CurrentStateID { get { return currentStateID; } }
    private FSMState currentState;
    public FSMState CurrentState { get { return currentState; } }

    public FSMSystem()
    {
        states = new List<FSMState>();
    }

    /// <summary>
    /// This method places new states inside the FSM,
    /// or prints an ERROR message if the state was already inside the List.
    /// First state added is also the initial state.
    /// </summary>
    public void AddState(FSMState s)
    {
        // Check for Null reference before deleting
        if (s == 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 (states.Count == 0)
        {
            states.Add(s);
            currentState = s;
            currentStateID = s.ID;
            return;
        }

        // Add the state to the List if it's not inside it
        foreach (FSMState state in states)
        {
            if (state.ID == s.ID)
            {
                Debug.LogError("FSM ERROR: Impossible to add state " + s.ID.ToString() +
                               " because state has already been added");
                return;
            }
        }
        states.Add(s);
    }

    /// <summary>
    /// This method delete a state from the FSM List if it exists,
    ///   or prints an ERROR message if the state was not on the List.
    /// </summary>
    public void DeleteState(StateID id)
    {
        // Check for NullState before deleting
        if (id == StateID.NullStateID)
        {
            Debug.LogError("FSM ERROR: NullStateID is not allowed for a real state");
            return;
        }

        // Search the List and delete the state if it's inside it
        foreach (FSMState state in states)
        {
            if (state.ID == id)
            {
                states.Remove(state);
                return;
            }
        }
        Debug.LogError("FSM ERROR: Impossible to delete state " + id.ToString() +
                       ". It was not on the list of states");
    }

    /// <summary>
    /// This method tries to change the state the FSM is in based on
    /// the current state and the transition passed. If current state
    ///  doesn't have a target state for the transition passed,
    /// an ERROR message is printed.
    /// </summary>
    public void PerformTransition(Transition trans)
    {
        // Check for NullTransition before changing the current state
        if (trans == Transition.NullTransition)
        {
            Debug.LogError("FSM ERROR: NullTransition is not allowed for a real transition");
            return;
        }

        // Check if the currentState has the transition passed as argument
        StateID id = currentState.GetOutputState(trans);
        if (id == StateID.NullStateID)
        {
            Debug.LogError("FSM ERROR: State " + currentStateID.ToString() +  " does not have a target state " +
                           " for transition " + trans.ToString());
            return;
        }

        // Update the currentStateID and currentState
        currentStateID = id;
        foreach (FSMState state in states)
        {
            if (state.ID == currentStateID)
            {
                // Do the post processing of the state before setting the new one
                currentState.DoBeforeLeaving();

                currentState = state;

                // Reset the state to its desired condition before it can reason or act
                currentState.DoBeforeEntering();
                break;
            }
        }

    } // PerformTransition()

} //class FSMSystem

c#试例

using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;

[RequireComponent(typeof(Rigidbody))]
public class NPCControl : MonoBehaviour
{
    public GameObject player;
    public Transform[] path;
    private FSMSystem fsm;

    public void SetTransition(Transition t) { fsm.PerformTransition(t); }

    public void Start()
    {
        MakeFSM();
    }

    public void FixedUpdate()
    {
        fsm.CurrentState.Reason(player, gameObject);
        fsm.CurrentState.Act(player, gameObject);
    }

	// The NPC has two states: FollowPath and ChasePlayer
	// If it's on the first state and SawPlayer transition is fired, it changes to ChasePlayer
	// If it's on ChasePlayerState and LostPlayer transition is fired, it returns to FollowPath
    private void MakeFSM()
    {
        FollowPathState follow = new FollowPathState(path);
        follow.AddTransition(Transition.SawPlayer, StateID.ChasingPlayer);

        ChasePlayerState chase = new ChasePlayerState();
        chase.AddTransition(Transition.LostPlayer, StateID.FollowingPath);

        fsm = new FSMSystem();
        fsm.AddState(follow);
        fsm.AddState(chase);
    }
}

public class FollowPathState : FSMState
{
    private int currentWayPoint;
    private Transform[] waypoints;

    public FollowPathState(Transform[] wp)
    {
        waypoints = wp;
        currentWayPoint = 0;
        stateID = StateID.FollowingPath;
    }

    public override void Reason(GameObject player, GameObject npc)
    {
        // If the Player passes less than 15 meters away in front of the NPC
        RaycastHit hit;
        if (Physics.Raycast(npc.transform.position, npc.transform.forward, out hit, 15F))
        {
            if (hit.transform.gameObject.tag == "Player")
                npc.GetComponent<NPCControl>().SetTransition(Transition.SawPlayer);
        }
    }

    public override void Act(GameObject player, GameObject npc)
    {
        // Follow the path of waypoints
		// Find the direction of the current way point
        Vector3 vel = npc.rigidbody.velocity;
        Vector3 moveDir = waypoints[currentWayPoint].position - npc.transform.position;

        if (moveDir.magnitude < 1)
        {
            currentWayPoint++;
            if (currentWayPoint >= waypoints.Length)
            {
                currentWayPoint = 0;
            }
        }
        else
        {
            vel = moveDir.normalized * 10;

            // Rotate towards the waypoint
            npc.transform.rotation = Quaternion.Slerp(npc.transform.rotation,
                                                      Quaternion.LookRotation(moveDir),
                                                      5 * Time.deltaTime);
            npc.transform.eulerAngles = new Vector3(0, npc.transform.eulerAngles.y, 0);

        }

        // Apply the Velocity
        npc.rigidbody.velocity = vel;
    }

} // FollowPathState

public class ChasePlayerState : FSMState
{
    public ChasePlayerState()
    {
        stateID = StateID.ChasingPlayer;
    }

    public override void Reason(GameObject player, GameObject npc)
    {
        // If the player has gone 30 meters away from the NPC, fire LostPlayer transition
        if (Vector3.Distance(npc.transform.position, player.transform.position) >= 30)
            npc.GetComponent<NPCControl>().SetTransition(Transition.LostPlayer);
    }

    public override void Act(GameObject player, GameObject npc)
    {
        // Follow the path of waypoints
		// Find the direction of the player
        Vector3 vel = npc.rigidbody.velocity;
        Vector3 moveDir = player.transform.position - npc.transform.position;

        // Rotate towards the waypoint
        npc.transform.rotation = Quaternion.Slerp(npc.transform.rotation,
                                                  Quaternion.LookRotation(moveDir),
                                                  5 * Time.deltaTime);
        npc.transform.eulerAngles = new Vector3(0, npc.transform.eulerAngles.y, 0);

        vel = moveDir.normalized * 10;

        // Apply the new Velocity
        npc.rigidbody.velocity = vel;
    }

} // ChasePlayerState

英文原文地址:http://wiki.unity3d.com/index.php/Finite_State_Machine

     -------译自:wolf96

时间: 2024-10-05 20:58:59

unity3d 游戏人工智能开发之状态机(C#模板与示例)的相关文章

细数那些年我们一起玩过的Unity3D游戏(unity开发的游戏有哪些)

经典重现<新仙剑OL> <新仙剑OL>采用跨平台Unity3D引擎,耗资数千万,历时三年多,由台湾大宇正版授权,"仙剑之父"姚壮宪监制的全球首款Unity3D航母级双端(网页和客户端)中国风MMORPG网络游戏巨作.主打温情牌并且延续了仙剑系列的国风雅韵,人物塑造细腻唯美,场景构建精致逼真. <蒸汽之城>(City of Steam) 由国内游戏公司参与开发的Unity3D页游<蒸汽之城>(City of Steam)在北美地区呼声颇高,

游戏人工智能开发之人群的动态行为交互仿真

博主把实验效果做成了视频,可以先看一下: youtube(清晰):https://youtu.be/S5wLx-zMrIE 优酷:http://v.youku.com/v_show/id_XMTMxOTM3NTE1Ng==.html TX:TX:http://v.qq.com/page/q/m/x/q0163gorwmx.html 动态人群交互模拟基于一般适应综合征理论General Adaptation Syndrome Theory 先了解一下什么是  一般适应综合征(general ada

游戏人工智能开发之进阶版随机技术

又get3种新的rand方式,简单又实用 分别为高斯分布随机,过滤随机,和perlin随机,perlin老朋友了,主要说说前两种. 高斯分布随机(Gaussian Randomness) 高斯分布也叫正态分布(Normal distribution)或钟形曲线(bell curves),正态分布再熟悉不过了.它长这个样子: 为什么要根据高斯分布来产生随机呢,这里要提到一个名词"中心极限定理(central limit theorem)".中心极限定理:在自然界与生产中,一些现象受到许多

[Unity3D]Unity3D游戏开发之跑酷游戏项目解说

大家好,我是秦元培.我參加了CSDN2014博客之星的评选,欢迎大家为我投票,同一时候希望在新的一年里大家能继续支持我的博客. 大家晚上好.我是秦元培,欢迎大家关注我的博客,我的博客地址是blog.csdn.net/qinyuanpei.终于到了更新博客的时间.从昨天下午開始,博主開始设计一个跑酷的游戏,到晚上睡觉前这个游戏已经基本完毕. 博主今天早上七点钟就起来了,到早上十点钟终于把整个游戏写完了. 所以,今天的博客的主题就是<Unity3D游戏开发之跑酷游戏项目解说>. 从博主自身来考虑这

Unity3D游戏开发之跑酷游戏项目详解

更多精彩请到http://www.gopedu.com/ 今天的博客的主题就是<Unity3D游戏开发之跑酷游戏项目讲解>. 从我们自身来考虑这件事情,当你选择做自己热爱的事情的时候,你的内心一定是充满激情和勇气的,你愿意看到自己的努力,你愿意看到自己的付出,我们成长是为了促进自我对认知的不断完善,所以我们应该以一种虔诚.谦恭的态度来对待我们的生命,我们或许无法选择出身,但我们可以选择向自己喜欢的生活去努力.或许我和这些人真的不是一个世界的人吧,很多事情在今天都给出了结局,对于这种人我已经没有

Unity3D游戏开发从零单排(五) - 导入CS模型到Unity3D

游戏动画基础 Animation组件 Animation组件是对于老的动画系统来说的. 老的动画形同对应的动画就是clip,每个运动都是一段单独的动画,使用Play()或CrossFade(),直接播放动画 或淡入淡出播放动画. animation.Play("name"); animation.CrossFade("name"); 下面的是它的几个属性 Animation:默认的动画片段: Aniamtions:包含的动画片段: Play Automaticall

Unity3D游戏开发初探—2.初步了解3D模型基础

一.什么是3D模型? 1.1 3D模型概述 简而言之,3D模型就是三维的.立体的模型,D是英文Dimensions的缩写. 3D模型也可以说是用3Ds MAX建造的立体模型,包括各种建筑.人物.植被.机械等等,比如一个大楼的3D模型图.3D模型也包括玩具和电脑模型领域. 互联网的形态一直以来都是2D模式的,但是随着3D技术的不断进步,在未来的时间里,将会有越来越多的互联网应用以3D的方式呈现给用户,包括网络视讯.电子阅读.网络游戏.虚拟社区.电子商务.远程教育等等.甚至对于旅游业,3D互联网也能

[Unity3D]Unity3D游戏开发之异步记载场景并实现进度条读取效果

大家好,我是秦元培.欢迎大家关注我的博客,我的博客地址是:blog.csdn.net/qinyuanpei.终于在各种无语的论文作业中解脱了,所以立即抓紧时间来这里更新博客.博主本来计划在Unity3D游戏开发之从<魂斗罗>游戏说起(上)--目标追踪这篇文章后再写一篇<Unity3D游戏开发之从<魂斗罗>游戏说起(下)>,只是眼下博主的项目进度有些缓慢,所以想等项目稳定下来以后再和大家分享. 作为大家等待博主更新博客的回报,我们今天来说一说Unity3D中的游戏场景异步

Unity3D游戏开发从零单排(六) - 人物运动及攻击连击

提要 今天要实现的是一个简单人物控制器.包括用w,a,s,d来控制人物上下左右跑动,鼠标左击发出连招,都是基于老的lagacy的动画.虽然unity3d自带有charactorcontroller,但是并不是很好用,所以人物控制相关的全部自己来实现.先上效果图: 场景搭建 首先下载这个package,里面包含了人物的动作还有地面的模型.将人物和地面都拖进场景中.这里的模型默认的动画模式是lagacy,不用修改.模型有点偏小,改变模型的scale值为10.最好不要改源文件的scale的scale