文章目录
状态模式是一种对象型模式,他将复杂的逻辑判断提取到不同状态对象中,允许状态对象在其内部状态发生改变时,改变其行为。状态的改变和各个状态的行为是状态模式的核心。
这里模式就需要谈到一个游戏中经常使用的状态模式的形式。它就是有限状态机,有限状态机是作为对象不同状态的管理而使用的(游戏AI也经常使用有限状态机)。其主要思想是程序在任意时刻仅可处在有限的几个状态中。在任意状态中,程序行为将随状态的改变,发生改变。而状态和状态之间的切换都是预先设计好的。
预先定义的状态切换规则称为转移。

说明
这里将实现一个有限状态机,状态机分为四个部分
我们使用了泛型来增强有限状态机的复用性,使用工厂来创建状态机(避免复杂的创建过程,停留在调用端来污染代码)
为了方便考虑,我只枚举了 Idle(闲置)和 Chase(追击)两种状态
状态枚举
public enum StateId
{
Idle,
Chase,
}
状态接口
public interface IState<T>
where T : Enum
{
//获得对应状态类的Id
T Id { get; }
//状态进入前,调用此函数
void OnEnterState();
//状态时,调用此函数
void OnUpdateState();
//状态退出前,调用此函数
void OnExitState();
//状态转换函数,通过此函数判断是否转移状态
bool TransitionState(out T id);
}
敌人抽象状态
public abstract class EnemyState : IState<StateId>
{
private readonly StateId _id;
private readonly ITransitionState<StateId> _transitionState;
protected EnemyState(StateId id, ITransitionState<StateId> transitionState)
{
_id = id;
_transitionState = transitionState;
}
public StateId Id => _id;
public virtual void OnEnterState() { }
public abstract void OnUpdateState();
public virtual void OnExitState() { }
public bool TransitionState(out StateId id) => _transitionState.Transition(out id);
}
之所以多出来这个抽象类,是为了将状态条件逻辑和状态行为逻辑分开,使得子类无需关注状态转移,只关注实现即可。而状态的转移实现转交给条件类完成(实现状态判断和状态行为实现的分离)。
具体状态类(Idle,Chase)
public class EnemyIdleState : EnemyState
{
public EnemyIdleState(ITransitionState<StateId> transitionState) :
base(StateId.Idle, transitionState)
{
}
public override void OnUpdateState()
{
}
}
public class EnemyChaseState : EnemyState
{
private float _chaseSpeed;
private float _chaseRange;
private GameObject _go;
private GameObject _chaseTarget;
//物理缓存
private Collider[] _colliders = new Collider[1];
public EnemyChaseState(float chaseSpeed, float chaseRange, GameObject go, ITransitionState<StateId> transitionState) :
base(StateId.Chase, transitionState)
{
_go = go;
_chaseSpeed = chaseSpeed;
_chaseRange = chaseRange;
}
public override void OnEnterState()
{
_chaseTarget = null;
int num = Physics.OverlapSphereNonAlloc(_go.transform.position, _chaseRange,
_colliders, 1 << LayerMask.NameToLayer("Player"));
if (num != 0) _chaseTarget = _colliders[0].gameObject;
}
public override void OnUpdateState()
{
//移动
var position = _go.transform.position;
position += _chaseSpeed * Time.deltaTime * (_chaseTarget.transform.position - position).normalized;
_go.transform.position = position;
//旋转
_go.transform.LookAt(_chaseTarget.transform);
}
public override void OnExitState()
{
_chaseTarget = null;
}
}
条件接口
public interface ITransitionState<T>
where T : Enum
{
bool Transition(out T id);
}
具体条件接口(IdleTransition,Chase Transition)
public class EnemyIdleStateTransition : ITransitionState<StateId>
{
//自身游戏对象
private GameObject _go;
//侦察范围
private float _scoutingRange;
//侦察范围间隔(每帧调用,不利于程序性能)
private readonly float _scoutingTime = 0.2f;
private float _currentTime;
public EnemyIdleStateTransition(GameObject go, float scoutingRange)
{
_scoutingRange = scoutingRange;
_go = go;
}
public bool Transition(out StateId id)
{
_currentTime += Time.deltaTime;
if (_currentTime >= _scoutingTime)
{
_currentTime = 0f;
if (Physics.CheckSphere(_go.transform.position, _scoutingRange, 1 << LayerMask.NameToLayer("Player")))
{
id = StateId.Chase;
return true;
}
}
id = StateId.Idle;
return false;
}
}
public class EnemyChaseStateTransition : ITransitionState<StateId>
{
//脱离追击的距离
private float _outChaseDistance;
//自身游戏对象
private GameObject _go;
//脱离范围间隔(每帧调用不利于程序性能)
private readonly float _outChaseTime = 0.2f;
private float _currentTime;
public EnemyChaseStateTransition(GameObject go, float outChaseDistance)
{
_outChaseDistance = outChaseDistance;
_go = go;
}
public bool Transition(out StateId id)
{
_currentTime += Time.deltaTime;
if (_currentTime >= _outChaseTime)
{
_currentTime = 0f;
if (!Physics.CheckSphere(_go.transform.position, _outChaseDistance, 1 << LayerMask.NameToLayer("Player")))
{
id = StateId.Idle;
return true;
}
}
id = StateId.Chase;
return false;
}
}
有限状态机系统类
public class FsmSystem<T>
where T : Enum
{
private Dictionary<T, IState<T>> _stateDic;
private T _currentStateId;
private IState<T> _currentState;
public T Id => _currentStateId;
public FsmSystem()
{
_stateDic = new Dictionary<T, IState<T>>();
}
public void Add(IState<T> state)
{
if (state == null) return;
if (_stateDic.ContainsKey(state.Id)) return;
_stateDic.Add(state.Id, state);
}
public void Remove(T id)
{
if (!_stateDic.ContainsKey(id)) return;
_stateDic.Remove(id);
}
public bool Enable(T id)
{
if (!_stateDic.ContainsKey(id)) return false;
_currentStateId = id;
_currentState = _stateDic[id];
_currentState.OnEnterState();
return true;
}
public void Update()
{
if (_currentState.TransitionState(out T id))
TransferState(id);
_currentState.OnUpdateState();
}
//转移状态函数
private void TransferState(T id)
{
if (!_stateDic.ContainsKey(id)) return;
_currentState.OnExitState();
_currentState = _stateDic[id];
_currentStateId = id;
_currentState.OnEnterState();
}
}
工厂类
public class FsmFactory
{
public static FsmSystem<StateId> CreateEnemyFsm(GameObject go, float chaseRange, float chaseSpeed, float outChaseRange)
{
var fsm = new FsmSystem<StateId>();
//创建条件,并添加条件所需对应参数
var idleStateTransition = new EnemyIdleStateTransition(go, chaseRange);
var chaseStateTransition = new EnemyChaseStateTransition(go, outChaseRange);
//创建状态,并添加状态所需参数,而且将条件与状态绑定
var idleState = new EnemyIdleState(idleStateTransition);
var chaseState = new EnemyChaseState(chaseSpeed, chaseRange, go, chaseStateTransition);
fsm.Add(idleState);
fsm.Add(chaseState);
fsm.Enable(StateId.Idle);
return fsm;
}
}
调用端
public class StateExample : MonoBehaviour
{
//追击速度
[SerializeField] private float _chaseSpeed = 3.0f;
//追击范围
[SerializeField] private float _chaseRange = 4.0f;
//脱离追击距离
[SerializeField] private float _outChaseRange = 5.0f;
//此时状态
[SerializeField] private StateId _stateId;
private FsmSystem<StateId> _system;
private void Awake()
{
_system = FsmFactory.CreateEnemyFsm(gameObject, _chaseRange, _chaseSpeed, _outChaseRange);
}
private void Update()
{
_system.Update();
_stateId = _system.Id;
}
}
将玩家对象设置为 Player 层
效果图


由于不会作动图只能将就这看吧,代码没有问题,效果也还不错。
优点
缺点
我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende
当我的预订模型通过rake任务在状态机上转换时,我试图找出如何跳过对ActiveRecord对象的特定实例的验证。我想在reservation.close时跳过所有验证!叫做。希望调用reservation.close!(:validate=>false)之类的东西。仅供引用,我们正在使用https://github.com/pluginaweek/state_machine用于状态机。这是我的预订模型的示例。classReservation["requested","negotiating","approved"])}state_machine:initial=>'requested
对于作为String#tr参数的单引号字符串文字中反斜杠的转义状态,我觉得有些神秘。你能解释一下下面三个例子之间的对比吗?我特别不明白第二个。为了避免复杂化,我在这里使用了'd',在双引号中转义时不会改变含义("\d"="d")。'\\'.tr('\\','x')#=>"x"'\\'.tr('\\d','x')#=>"\\"'\\'.tr('\\\d','x')#=>"x" 最佳答案 在tr中转义tr的第一个参数非常类似于正则表达式中的括号字符分组。您可以在表达式的开头使用^来否定匹配(替换任何不匹配的内容)并使用例如a-f来匹配一
我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur
给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最
?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------
本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01 客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02 数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit