为了让读者对本文知识点有一个比较清晰的了解,我制作了一张结构图,如下图,图中以移动为例子简单的描述了状态机的基本结构,本文不对角色控制系统做全面的讲解,只对状态机的在角色控制系统中是如何运用做出讲解。

1.我们先从Actor讲起。Actor作为角色脚本的基类,承载着角色的基本属性,包括角色id,移动速度,坐标等等,因为我们这里讲的是用状态机来控制角色,所以角色的属性还包括角色的当前状态,所有状态,状态类型等等,还有一些对状态操作的方法,包括初始化状态机,初始化当前状态,改变状态机,更新状态机等等,当然还有一些角色表现的方法,比如移动,改变方向,播放动画等等,这些表现方法是通过状态机来实现,从而实现改变状态来驱动表现,这就是状态机的用法。

// **********************************************************************
// Copyright (C) XM
// Author: 吴肖牧
// Date: 2018-04-13
// Desc:
// **********************************************************************
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public enum Direction
{Front = 0,Back,Left,Right
}public abstract class Actor : Base
{/// <summary>/// debug模式,程序测试/// </summary>public bool _debug;/// <summary>/// 玩家id/// </summary>public int _uid;/// <summary>/// 玩家名字/// </summary>public string _name;/// <summary>/// 移动速度/// </summary>public float _moveSpeed;/// <summary>/// 是否正在移动/// </summary>public bool _isMoving;/// <summary>/// 坐标/// </summary>public Vector3 _pos;/// <summary>/// 当前状态/// </summary>public ActorState _curState { set; get; }public ActorStateType _stateType;public Direction _direction = Direction.Front;/// <summary>/// 状态机集合/// </summary>public Dictionary<ActorStateType, ActorState> _actorStateDic = new Dictionary<ActorStateType, ActorState>();/// <summary>/// 动画控制器/// </summary>[HideInInspector]public Animator _animator;private Transform _transform;void Awake(){_transform = this.transform;_animator = GetComponent<Animator>();InitState();InitCurState();}/// <summary>/// 初始化状态机/// </summary>protected abstract void InitState();/// <summary>///  初始化当前状态/// </summary>protected abstract void InitCurState();/// <summary>/// 改变状态机/// </summary>/// <param name="stateType"></param>/// <param name="param"></param>public void TransState(ActorStateType stateType){if (_curState == null){return;}if (_curState.StateType == stateType){return;}else{ActorState _state;if (_actorStateDic.TryGetValue(stateType, out _state)){_curState.Exit();_curState = _state;_curState.Enter(this);_stateType = _curState.StateType;}}}/// <summary>/// 更新状态机/// </summary>public void UpdateState(){if (_curState != null){_curState.Update();}}/// <summary>/// 移动 数据(状态)驱动表现/// </summary>public virtual void Move(){//TODO 移动相关状态_animator.SetInteger("Dir", (int)_direction);if (_debug){//数据层位置_transform.position = _pos;}else{//表现层位置_transform.position = Vector3.Lerp(_transform.position, _pos, 100 * Time.deltaTime);}}/// <summary>/// 改变方向/// </summary>/// <param name="dir"></param>public void ChangeDir(Direction dir){_direction = dir;if (_direction == Direction.Left){_transform.localScale = new Vector3(-1, 1, 1);}else{_transform.localScale = new Vector3(1, 1, 1);}}/// <summary>/// 播放动画/// </summary>/// <param name="name"></param>/// <param name="dir"></param>public void PlayAnim(string name){_animator.SetBool("Idle", false);_animator.SetBool("Run", false);_animator.SetBool(name, true);_animator.SetInteger("Dir", (int)_direction);}
}

2.我们现在有了Actor这个角色基类,那么我们现在就可以用它来创造很多的不同角色了。我们先来创造一个自己的角色PayerActor,然后继承Actor,因为Actor的状态机初始化是用虚方法的,所以我们必须在子类中去实现它,来达到不同的角色有不同的状态。

// **********************************************************************
// Copyright (C) XM
// Author: 吴肖牧
// Date: 2018-04-13
// Desc:
// **********************************************************************
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class PlayerActor : Actor {/// <summary>/// 摇杆/// </summary>private ETCJoystick _joystick;/// <summary>/// 初始化状态机/// </summary>protected override void InitState(){_actorStateDic[ActorStateType.Idle] = new IdleState();_actorStateDic[ActorStateType.Move] = new MoveState();}/// <summary>/// 初始化当前状态/// </summary>protected override void InitCurState(){_curState = _actorStateDic[ActorStateType.Idle];_curState.Enter(this);}void Start(){_joystick = GameObject.FindObjectOfType<ETCJoystick>();if (_joystick != null){_joystick.onMoveStart.AddListener(StartMoveCallBack);_joystick.onMove.AddListener(MoveCallBack);_joystick.onMoveEnd.AddListener(EndMoveCallBack);}}/// <summary>/// 开始移动/// </summary>private void StartMoveCallBack(){TransState(ActorStateType.Move);}/// <summary>/// 正在移动/// </summary>/// <param name="arg0"></param>private void MoveCallBack(Vector2 vec2){float value = 0.02f * _moveSpeed / Mathf.Sqrt(vec2.normalized.x * vec2.normalized.x + vec2.normalized.y * vec2.normalized.y);//勾股定理得出比例,第一个值是摇杆的比例_pos = new Vector3(_pos.x + vec2.x * value, _pos.y + vec2.y * value, 0);int angle = (int)(Mathf.Atan2(vec2.normalized.y, vec2.normalized.x) * 180 / 3.14f);//Debug.Log(angle);if (angle > 45 && angle < 135){ChangeDir(Direction.Back);//Debug.Log("上");}else if (angle <= 45 && angle >= -45){ChangeDir(Direction.Right);//Debug.Log("右");}else if (Mathf.Abs(angle) >= 135){ChangeDir(Direction.Left);//Debug.Log("左");}else{ChangeDir(Direction.Front);//Debug.Log("下");}}/// <summary>/// 移动结束/// </summary>private void EndMoveCallBack(){TransState(ActorStateType.Idle);}void OnDestroy(){if (_joystick != null){_joystick.onMoveStart.RemoveListener(StartMoveCallBack);_joystick.onMove.RemoveListener(MoveCallBack);_joystick.onMoveEnd.RemoveListener(EndMoveCallBack);}}}

这里有个可以优化的地方就是动画控制器,由于我这里做的是一个2D的角色,并且他有4个朝向,所以我改成了用混合树来做,通过MoveCallBack方法传进来的二维坐标直接控制混合树的X和Y的参数,进而改变角色的朝向,所以MoveCallBack方法里面的实现,如果你们需要可以进行优化,就是不需要通过角度去算方向了,我就不去修改了。

3.接下来我们来讲讲状态机的基类ActorState,基类包括状态机类型,进入状态,更新状态,退出状态等等。

// **********************************************************************
// Copyright (C) XM
// Author: 吴肖牧
// Date: 2018-04-13
// Desc:
// **********************************************************************using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;/// <summary>
/// 角色状态
/// </summary>
public abstract class ActorState
{/// <summary>/// 状态机类型/// </summary>public abstract ActorStateType StateType { get; }/// <summary>/// 进入状态/// </summary>/// <param name="param"></param>public abstract void Enter(params object[] param);/// <summary>/// 更新状态/// </summary>public abstract void Update();/// <summary>/// 退出状态/// </summary>public abstract void Exit();}/// <summary>
/// 角色状态类型
/// </summary>
public enum ActorStateType
{Idle,Move,//...
}

4.既然我们有了状态机的基类,那我们就可以创造出很多的状态了,比如待机,移动,攻击,释放技能等等。同样的,基类ActorState的方法也是虚方法,必须通过子类来实现,所以我们每个不同的状态就可以各自实现自己的操作了。

// **********************************************************************
// Copyright (C) XM
// Author: 吴肖牧
// Date: 2018-04-13
// Desc:
// **********************************************************************using System.Collections;
using System.Collections.Generic;
using UnityEngine;/// <summary>
/// 待机状态
/// </summary>
public class IdleState : ActorState
{private Actor _actor;public override ActorStateType StateType{ get{return ActorStateType.Idle;}}public override void Enter(params object[] param){//Debug.Log("IdleState Enter");_actor = param[0] as Actor;if (_actor != null){_actor.PlayAnim("Idle");_actor._isMoving = false;//TODO 播放动画相关}}public override void Update(){}public override void Exit(){_actor = null;//Debug.Log("IdleState Exit");}
}/// <summary>
/// 移动状态
/// </summary>
public class MoveState : ActorState
{private Actor _actor;public override ActorStateType StateType{get{return ActorStateType.Move;}}public override void Enter(params object[] param){//Debug.Log("MoveState Enter");_actor = param[0] as Actor;if (_actor != null){_actor.PlayAnim("Run");_actor._isMoving = true;//TODO 播放动画相关}}public override void Update(){if (_actor != null){_actor.Move();}}public override void Exit(){//Debug.Log("MoveState Exit");_actor._isMoving = false;_actor = null;}
}

那我们是如何实现切换状态的呢?我们回到Actor,看看改变状态机的方法,每次切换的时候都会先把当前状态停掉,然后进入新的状态,再把自己的Actor传进状态机,然后根据状态的需要,实现Actor里的方法。

    /// <summary>/// 改变状态机/// </summary>/// <param name="stateType"></param>/// <param name="param"></param>public void TransState(ActorStateType stateType){if (_curState == null){return;}if (_curState.StateType == stateType){return;}else{ActorState _state;if (_actorStateDic.TryGetValue(stateType, out _state)){_curState.Exit();_curState = _state;_curState.Enter(this);_stateType = _curState.StateType;}}}

5.最后我们来讲讲简单的角色管理系统ActorManager,包括角色的创建,删除,获取等等。其中最重要的功能就是更新所有角色状态机UpdateActor(),所有角色的持续状态都是通过这个方法实现的。

// **********************************************************************
// Copyright (C) XM
// Author: 吴肖牧
// Date: 2018-04-14
// Desc: 角色管理器
// **********************************************************************using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class ActorManager : Singleton<ActorManager>
{/// <summary>/// 所有玩家的角色列表/// </summary>public Dictionary<int, Actor> _actorDic = new Dictionary<int, Actor>();// Update is called once per framevoid Update(){UpdateActor();}/// <summary>/// 更新角色状态/// </summary>private void UpdateActor(){var enumerator = _actorDic.GetEnumerator();while (enumerator.MoveNext()){enumerator.Current.Value.UpdateState();}enumerator.Dispose();}/// <summary>/// 创建角色/// </summary>/// <param name="uid">角色id</param>public void CreateActor(int uid){Actor actor = null;if (!_actorDic.TryGetValue(uid, out actor)){GameObject go = AppFacade.Instance.GetManager<ResourceManager>(ManagerName.Resource).CreateAsset("Prefabs/Actor/Wizard");Camera.main.GetComponentInChildren<Cinemachine.CinemachineVirtualCamera>().Follow = go.transform;actor = go.GetComponent<WizardActor>();actor._uid = uid;actor._name = uid.ToString();actor._moveSpeed = 5;_actorDic[uid] = actor;}else{Debug.Log("玩家" + uid + "已经存在");}}/// <summary>/// 删除角色/// </summary>/// <param name="uid">角色id</param>public void RemoveActor(int uid){Actor actor = null;if (_actorDic.TryGetValue(uid, out actor)){Destroy(actor.gameObject);_actorDic.Remove(uid);}else{Debug.Log("玩家" + uid + "不存在");}}/// <summary>/// 获取角色/// </summary>/// <param name="uid">角色id</param>/// <returns></returns>public Actor GetActor(int uid){Actor actor = null;_actorDic.TryGetValue(uid, out actor);return actor;}
}

后面有时间的话,我会基于这篇文章再写一篇简单帧同步的文章。

Unity3D用状态机制作角色控制系统相关推荐

  1. Unity3D粒子系统之制作火焰特效

    Unity3D粒子系统之制作火焰特效 本文将会介绍如何使用Unity内的粒子系统制作烟雾效果. 如果想了解Unity粒子系统中的基础属性,可以看这篇博客:Unity3D粒子系统之基础属性介绍 先上预览 ...

  2. C4D与Marvelous Designer制作角色服装设计视频教程

    大小解压后:22G 含课程文件 2560X1440 mp4 语言:英语+中英文字幕(机译) 学习如何使用Redshift和Marvelous Designer为创建的一系列服装添加纹理,查看真实的服装 ...

  3. Unity3d数字模型制作规范(更多资讯资料群:193521697邀请码:10026)

    Unity3d数字模型制作规范 本文提到的所有数字模型制作,全部是用3D MAX建立模型,即使是不同的驱动引擎,对模型的要求基本是相同的.当一个VR模型制作完成时,它所包含的基本内容包括场景尺寸.单位 ...

  4. Maya角色跑步动画制作 │ 角色跑步动画案例(附资料)

    本文介绍角色跑步动画上半身动画的制作技巧,学习角色跑步肩部与胯部运动的动画制作技巧,如何制作角色头部的动画效果.如何实现手臂的跟随动画制作技巧. 01 角色跑步动画案例 **Step 01 **肩部胸 ...

  5. unity3d 2d游戏制作的模式

      经过了4个月不懈的努力,我和图灵教育合作的这本3D游戏开发书预计下个月就要出版了.这里MOMO先打一下广告,图灵的出版社编辑成员都非常给力,尤其是编辑小花为这本书付出了很大的努力,还有杨海玲老师, ...

  6. 制作角色血条 [代码清单10-2]

    Unity3D游戏开发, 宣雨松 著. 1. 制作红色血条,血量是红色的,底色是是黑色的.它们被定义在公有变量Texture2D blood_red和Texture2D blood_black:这是要 ...

  7. Unity3d 数字模型制作规范

    数字模型制作规范 本文提到的所有数字模型制作,全部是用3D MAX建立模型,即使是不同的驱动引擎,对模型的要求基本是相同的.当一个VR模型制作完成时,它所包含的基本内容包括场景尺寸.单位,模型归类塌陷 ...

  8. Unity3d使用RenderTexture制作动态头像

    项目中需要实现一个3D动态头像,就是一个静态的头像实时与3D模型的表情动作同步,这个功能可以使用Unity3D的RenderTexture实现. 首先拖入3D模型,再添加与模型关联的摄像机,新建一个M ...

  9. 虚幻——动画蓝图、状态机制作人物走跑跳动作

    一.名词解释 序列--一段动作. 混合空间--混合不同的动作.将多个序列排序播放. 动画蓝图--根据状态播放不同的动画. 二.混合空间制作走跑动作 由于走和跑的动作比较连续,并且都是由速度控制,所以要 ...

最新文章

  1. 一个button导致的慘案
  2. LeetCode实战:螺旋矩阵
  3. R语言使用yardstick包的pr_curve函数评估二分类(binary)模型的性能、并使用autoplot函数可视化模型的PR曲线(precision recall)
  4. 批量修改文件名称(Python)
  5. 学生用的笔记本电脑什么样的好_大学生用的学生党笔记本什么牌子好,听听学姐的...
  6. 《IBM-PC汇编语言程序设计》(第2版)【沈美明 温冬婵】——第三章——自编解析与答案
  7. OpenCASCADE:形状愈合之概述
  8. iOS开发之普通网络异步请求与文件下载方法
  9. android adjust,android adjustresize adjustpan着名的问题
  10. ajax实战用法详解
  11. 5G 重新定义生老病死!
  12. CIO感悟:IT人转型之“势、道、术”
  13. Supervisor使用说明
  14. 大学计算机基础数据库知识点,大学计算机基础试题(数据库)
  15. 喝酒的规矩(是男人必看必顶,女人可不看直接顶)
  16. monaco-editor浏览器中的网页代码编辑器在项目中集成
  17. eclipse环境变量配置步骤
  18. 沉没的王国---揭秘滇东自杞国(2)
  19. Androd 基本布局(其一)
  20. 纯粹的Pure Storage,简单却又不简单

热门文章

  1. 5种网络隔离后的文件交换方式,哪种最安全便捷?
  2. 什么是子网掩码?怎么根据子网掩码得到网络号?
  3. Space X 火箭载人上天,马斯克改写时代:理科生的浪漫是用钱征服宇宙
  4. CoAP学习笔记(1)CoAP报文结构
  5. 用Vue3写个气泡对话框组件
  6. bfs+dfs分析----poj 3278 Catch That Cow
  7. 第一章: 微型计算机组成结构
  8. 距离空间,线性空间,赋范线性空间,Banach空间,内积空间,Hilbert空间的内在关系
  9. Error creating bean with name 'transactionManager' defined i
  10. Excel中汉字转拼音的完美方案