2017-08-22 10:31:24 cglzy1982 阅读数 609

unity3d游戏,武侠类,需要实现人物多段式轻功
思路就是点击轻功的时候,给角色一个向上的速度并转换动作,摇杆控制方向。把向上或向下的速度,与角色水平方向区分处理。

  1. 角色起跳前判断位置是否属于可跳

    通过Physics.Raycast射线方式,判断角色脚下是否为walk层

   //向下发射线
   bool IsGrounded(float margin, string layerName = "Walk")
    {
        //本角色当前位置提高0.9f,然后向下发出1.2f的射线
        Vector3 SourcePosition = transform.position;
        Vector3 rayStartPoint = SourcePosition + new Vector3(0f, 0.9f, 0f);
        RaycastHit hit;
        LayerMask mask = 1 << LayerMask.NameToLayer(layerName);//射线打到walk层
        if (Physics.Raycast(rayStartPoint, Vector3.down, out hit, margin, mask))
        {
            //Helper.LogError("距离:::::::::::"+hit.distance);
            //Helper.DrawLine(rayStartPoint, hit.point, Color.red);
           //如果射线打到walk层就返回true
            return true;
        }
        else
        {
            return false;
        }
    }  
  1. 起跳设置
    首先人物身上挂CharacterController脚本
    CharacterController controller = GetComponent<CharacterController>();
 获取到CharacterController实例

核心起跳语句:

float gravity=10F; 
float temp =10;
bool checkdis=false;//是否开始轻功计算
void setjump()
{
    //点击起跳按钮后的初速度 只执行一次的
    moveDirection.y = jumpUpSpeed1;
    checkdis = true;
}
void FixedUpdate()
   {
    ......
    if(checkdis)
    {  
    moveDirection.y = temp;//每次必须把上次计算的方向y值结果赋值给方向y值
    //每一帧计算的,高度减值
    moveDirection.y -= gravity * Time.deltaTime;
    temp = moveDirection.y;
    controller.Move(moveDirection * Time.deltaTime);//注意是一个方向
    }
   }
  1. 四段跳 判断动作名,以及下降阶段变动作的判断
        bool result = false;
        //获取当前动作的状态
        AnimatorStateInfo animatorStateInfo = animator.GetCurrentAnimatorStateInfo(0);
        if (animatorStateInfo.IsName("jumpdown") || CurState == AnimatorState.JumpDown)
        {
            如果已经是降落状态,返回false
            return result;
        }
        if (animatorStateInfo.normalizedTime >= 0.9f)
        {
            //动作播放超过了0.9秒,并且是属于四段跳中的一段,则返回true
            if (animatorStateInfo.IsName("jump1") || animatorStateInfo.IsName("jump2") || animatorStateInfo.IsName("jump3") || animatorStateInfo.IsName("jump4"))
            {
                result = true;              
            }
        }

在fixupdata里面判断动作播放情况,并根据时间(0.9f可以读配置)来改变动作

  1. 如何判断轻功落地停止
    当起跳一段时间后,可以判断人物脚底是不是walk层,依此来判定是否落地
    IsGrounded(1.2f)
2019-09-07 21:25:55 weixin_44116706 阅读数 169

需要物体:一个用于移动的对象,一个主摄像机,一个地面对象,且摄像机的父结点为移动对象
旋转移动坐标示意图:
在这里插入图片描述

用于视角移动的代码(给摄像机的):

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

public class CameraTrun : MonoBehaviour
{
    // 水平视角移动的敏感度
    public float sensitivityHor = 3f;
    // 垂直视角移动的敏感度
    public float sensitivityVer = 3f;
    // 视角向上移动的角度范围,该值越小范围越大
    public float upVer = -40;
    // 视角向下移动的角度范围,该值越大范围越大
    public float downVer = 45;
    // 垂直旋转角度
    private float rotVer;

    // 旋转的方向问题
    // x 表示绕 x 轴旋转,即 前上后 的角度
    // y 表示绕 y 轴旋转,即 左前后 的角度
    // y 表示绕 y 轴旋转,即 左前后 的角度

    // Start is called before the first frame update
    void Start()
    {
        // 初始化当前的垂直角度
        rotVer = transform.eulerAngles.x;
    }

    // Update is called once per frame
    void Update()
    {
        // 获取鼠标上下的移动位置
        float mouseVer = Input.GetAxis("Mouse Y");
        // 获取鼠标左右的移动位置
        float mouseHor = Input.GetAxis("Mouse X");
        // 鼠标往上移动,视角其实是往下移,所以要想达到视角也往上移的话,就要减去它
        rotVer -= mouseVer * sensitivityVer;
        // 限定上下移动的视角范围,即垂直方向不能360度旋转
        rotVer = Mathf.Clamp(rotVer, upVer, downVer);
        // 水平移动
        float rotHor = transform.localEulerAngles.y + mouseHor * sensitivityHor;
        // 设置视角的移动值
        transform.localEulerAngles = new Vector3(rotVer, rotHor, 0);
    }
}

用于物体移动的控制器(给要移动的物体的):

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

// 表示一定需要这个控件
[RequireComponent(typeof(CharacterController))]
public class Move : MonoBehaviour {
    // 获取摄像机对象的位置信息,[SerializeField] 类似于 java 的构造方法的方法参数
    [SerializeField] private Transform target;
    // 跳起来的速度
    public float jumpSpeed = 15.0f;
    // 重力
    public float gravity = -9.8f;
    // 最终垂直速度
    public float endsVelocity = -10.0f;
    // 在地面时的垂直速度
    public float minFall = -1.5f;
    // 跑起来的速度
    public float runSpeed = 10;
    // 走路的速度
    public float walkSpeed = 4;
    // 垂直速度
    private float verSpeed;

    // 移动的速度
    private float moveSpeed;
    // 用于存储当前的角色控件
    private CharacterController character;

    // 在被加载时执行
    void Start() {
        // 初始化
        character = GetComponent<CharacterController>();
        verSpeed = minFall;
        moveSpeed = walkSpeed;
    }
    // 每更新一帧时执行
    void Update() {
        // 用于存储移动信息
        Vector3 movement = Vector3.zero;
        // 获取左右方向的移动信息
        float horspeed = Input.GetAxis("Horizontal");
        // 获取前后方向的移动信息
        float verspeed = Input.GetAxis("Vertical");
        // 当发生了移动才执行
        if (horspeed != 0 || verspeed != 0) {
            // 设置左右位置
            movement.x = horspeed * moveSpeed;
            // 设置前后的位置
            movement.z = verspeed * moveSpeed;
            // 设置斜着走的最大速度更水平垂直走的速度一样
            movement = Vector3.ClampMagnitude(movement, moveSpeed);
            // 将移动的信息转化为以摄像机为全局坐标的位置,即保证你向前走一定是摄像机的视角方向
            movement = target.TransformDirection(movement);
        }
        // 当按下左 shift 是跟换速度
        if (Input.GetKey(KeyCode.LeftShift)) {
            moveSpeed = runSpeed;
        } else {
            moveSpeed = walkSpeed;
        }
        // 角色控件自带的一个方法,用于检测是否在地面
        if (character.isGrounded) {
            // 按了空格键则给垂直方向施加一个速度
            if (Input.GetButtonDown("Jump")) {
                verSpeed = jumpSpeed;
            } else {
                verSpeed = minFall;
            }
        } else {
            // 若已经跳起来了则将垂直方向的速度递减降低,来达到一个 下上下 的一个效果
            // Time.deltaTime 表示为每秒的刷新频率的倒数,用来控制每台电脑的移动速度都是一样的
            verSpeed += gravity * 3 * Time.deltaTime;
            // 限制最大坠落速度
            if (verSpeed < endsVelocity) {
                verSpeed = endsVelocity;
            }
        }
        // 给移动一个垂直速度
        movement.y = verSpeed;
        // 控制速度
        movement *= Time.deltaTime;
        // 角色控件自带的一个方法,若用 transform.Translate() 的话会无视碰撞器
        character.Move(movement);
    }
}

不懂请留言!
问题询问:hellolxb@yeah.net

2018-05-01 11:40:53 Angle_Cal 阅读数 5270
  1. 打开输入管理界面,Edit->Project Setting->Input
    这里写图片描述
  2. 最上面有一个Size,输入通道数,比如这个游戏需要用到20个操作键,那就是20.游戏中需要用到的按键分为两种,一种是开关键,就是只能两个状态的键,例如开火,跳;另一种是线性键(“轴”键),就是键值是在一个范围内取值的,例如控制前进后退的摇杆,推的角度可以控制人物行走的速度,线性键都是成对出现的,例如水平轴,垂直轴,扳机轴(左右扳机键是一对轴),一对轴算一个通道,一个开关键算一个通道.
  3. 手柄标记:
    这里写图片描述
  4. 对于手柄上的开关键(就是只有0/1两个状态的键),获取状态的方法有两种:
    A.直接写代码
Input.GetKey(KeyCode.Joystick1Button0)
/*
说明:
  a.这个函数返回bool值,GetKey相当于"isKeyPress",
    GetKeyDown相当于"isKeyDown",GetKeyUp相当于"isKeyRelease"
  b.JoystickXButtonY,
    其中X可以取值为无或数字1~8,取数字时对应的是1~8号手柄,不取值的时候代表任意手柄;
    Y可以取值数字0~19,对应手柄上的20个开关键.
    所以理论上Unity3D最多同时支持8个手柄,最多支持手柄上带有20个开关键.
*/

B.使用输入管理器
使用Edit->Project Setting->Input:
先在Input面板中设定好Name和绑定的键位,然后写代码:

Input.GetAxis("A");
/*
 说明:
    a.这个函数返回的是float值,可以通过设定选择该值是否带有延迟,如果不设定,就默认是按下返回1,不按返回0.
    b.这个函数的效果相当于"isKeyPress"
*/

5 对于手柄上的”轴”键(就是线性开关,推动的力度不同效果不同,例如左右摇杆,扳机键),只能通过输入管理来处理:
使用Edit->Project Setting->Input:
先在Input面板中设定好Name和绑定的键位,然后写代码:

Input.GetAxis("Horizontal_L")
/*
    说明:
    a.这个函数返回的是float值,绑定到轴键上的时候,该函数的值会始终维持与轴的状态相同.
    b.这个函数的效果相当于"isKeyPress"
    c.轴键是两个方向一对的,例如水平方向是这样:最左边是-1,回中是0,最右边是+1.
    d.两个扳机键是一个轴,所以不用单独绑定两个扳机键,而是作为一个轴绑定.
*/

6 使用键盘
如果只使用键盘输入,可以在代码中直接检测按键,像这样:

Input.GetKeyDown(KeyCode.A)

当然也可以把键位绑定在输入管理里面然后用Input.GetAxis获取
7. 多种输入
有的时候我们需要多种输入方式,例如很多用手柄玩的游戏如果不插手柄,会有一套键盘鼠标的操作方法,这是怎么实现的呢?
方法1:
输入管理器中的通道是可以重名的,我们可以建两个同名的通道,在里面设置不同的键位,在代码里面检测这个通道是否有操作的时候,无论那个通道有操作,代码中都会有相同的反应.
方法2:
使用输入管理器中的Alt Negative Button和Alt Positive Button属性,这两个属性就是第二按键的意思,也就是说一个通道就能设置两种按键方式.
注意:方法1适用于线性按键,因为键盘鼠标都是瞬发开关键,用开关键模拟线性键需要设置延迟时间(例如W键按2秒以内是走,超过两秒是跑),而手柄摇杆本身就是线性键,不需要设置延迟,两套设置,必须占用两个通道;方法2适用于开关键,因为手柄上的开关键和键盘上的按键并没有什么区别,可以采用相同的延迟等设置.
8.Input Manager属性解释:

属性 作用
Name: [按键名]该键的名称,可以在脚本编程中直接引用他。比如:Input.GetButtonDown(“Jump”);
Descriptive Name: [描述名]在游戏的独立机构中的配置对话框中,当控制值为正时候所显示的名称。默认空白。
Negative Descriptive Name: [负描述]在游戏的独立机构中的配置对话框中,当控制值为负的时候所显示的名称。
Nagative Button: [负向按钮]玩家按下这个按钮来让被控制物体向负方向运动。
Positive Button: [正向按钮]玩家按下这个按钮让被控制物体向正方向运动。
Alt Negative Button: 玩家可以使用的备选负向按钮,比如方向键和WASD
Alt Positive Button: 玩家可以使用的备选正向按钮。
Gravity: [重力]如果玩家停止输入,该轴将恢复到空挡或0速度,其单位为单位每秒。
Dead: [盲区]可以用在模拟控制。在模拟控制器上,在这个范围内的任何值都会映射到空档不会提供任何输入。
Sensitivity: [灵敏度]可以用于数字控制,他是该轴向给定得值移动的速度。正负都可,单位为单位每秒。
Snap: [对齐]如果选中,可以确保在同时按下正向和负向按钮时候该轴的值为空挡。
Invert: [反转]迅速交换正向和负向控制键。
Type: [类型]该键对应的输入设备类型。可以为Key,MouseButton,MouseMove,JoystickAxis,或Window Movement。
Axis: [轴]这个控制方式有该输入设备的哪条轴指挥。这可受不同游戏手柄影响。
JoyNum: [手柄编号]可以指定某个特定的手柄或者任意手柄

9.可以填入button属性的值:

类型 填入button属性时的写法
标准键 “a”, “b”, “c” …
数字键 “1”, “2”, “3”, …
方向键 “up”, “down”, “left”, “right”
小键盘键 “[1]”, “[2]”, “[3]”, “[+]”, “[equals]”
修改键 “right shift”, “left shift”, “right ctrl”, “left ctrl”, “right alt”, “left alt”, “right cmd”, “left cmd”
鼠标按钮 “mouse 0”, “mouse 1”, “mouse 2”, …
手柄按钮(从任意手柄) “joystick button 0”, “joystick button 1”, “joystick button 2”, …
手柄按钮(从指定手柄) “joystick 1 button 0”,”joystick 1 button 1”,”joystick 2 button 0”,”joystick 2 button 1” …
特殊键 “backspace”, “tab”, “return”, “escape”, “space”, “delete”, “enter”, “insert”, “home”, “end”, “page up”, “page down”
功能键 “f1”, “f2”, “f3”, …

10.参考设置
这里写图片描述

2017-04-19 14:45:42 zzj051319 阅读数 2329
这次我们来做一个智能巡逻兵的游戏,先看看需求
首先我想着要做什么样的地图呢?单纯正方形没啥意思,突然想到之前看的一个户外真人闯关的综艺节目,和我们的游戏很像,它是蜂窝的形状的地图,由很多小的正六边形拼起来的大六边形。但是Unity3D没有正六边形的方块(但是自己挖的坑再大也要跳),我只能用正方体来组装成一个正六边形方块。
接着是上围墙,围墙就建几个长方体围住就行,长方体加上贴图就成了墙,只是正六面体需要旋转45,135度之类的
接着是巡逻兵制作,因为老师要求做成巡逻兵预制,即让巡逻兵自己拥有巡逻追捕功能。脚本如下:
public class GuardController : MonoBehaviour {
    private float pos_X,pos_Z;
    public float speed = 0.5f;
    private float dis = 0;
    private bool flag = true;
    public int state = 0;
    public GameObject role;//追捕的主角
    int n = 0;
    public SceneController sceneController;
	// Use this for initialization
	void Start () {
        sceneController = (SceneController)SSDirector.getInstance().currentScenceController;
        role = sceneController.role;//获得主角对象
        pos_X = this.transform.position.x;
        pos_Z = this.transform.position.z;
        //获得巡逻兵的位置,因为是同一平面所以无需改变y
	}
	
	// FixedUpdate is called once per frame //因为我将Guard设置为刚体
	void FixedUpdate () {
        //根据状态选择动作
        if(state == 0)
        {
            patrol();//巡逻
        }
        else if(state == 1)
        {
            chase(role);//追捕
        }
    }

    void patrol()//按照正六边形行走
    {
        if (flag)
        {
            switch (n)
            {
                case 0:
                    pos_X += 1;
                    pos_Z -= 1;
                    break;
                case 1:
                    pos_X += 2;
                    break;
                case 2:
                    pos_X += 1;
                    pos_Z += 1;
                    break;
                case 3:
                    pos_X -= 1;
                    pos_Z += 1;
                    break;
                case 4:
                    pos_X -= 2;
                    break;
                case 5:
                    pos_X -= 1;
                    pos_Z -= 1;
                    break;
            }
            flag = false;
        }
        dis = Vector3.Distance(transform.position, new Vector3(pos_X, 0, pos_Z));
        if (dis > 0.7)
        {
            transform.position = Vector3.MoveTowards(this.transform.position, new Vector3(pos_X, 0, pos_Z), speed * Time.deltaTime);
        }
        else
        {
            n++;
            n %= 6;
            flag = true;
        }
    }

    void chase(GameObject role)
    {
        transform.position = Vector3.MoveTowards(this.transform.position, role.transform.position, 0.5f * speed * Time.deltaTime);
    }
}
然后考虑下如何什么情况下触发巡逻兵状态,因为我想根据距离但是可能role还没到另一个巡逻兵巡逻的地图,巡逻兵就追过来了。所以最后我改用触发器。在各个入口设置触发器。
但是问题来了,要让触发器触发的恰好是我们这个地图上的巡逻兵而不是其他巡逻兵。我在每个地图上挂载脚本获得当前Guard。
public class GetGuard : MonoBehaviour {
    public GameObject guard;
    void OnCollisionEnter(Collision collider)
    {
        Debug.Log(collider.gameObject.tag);
        if(collider.gameObject.tag == "Guard")
        {
            Debug.Log(collider.gameObject.name);
            guard = collider.gameObject;
        }
    }
}
然后在触发器写脚本,当然这里用到订阅与发布模式,进来然后能够出去即成功逃掉一次,发布事件,让记分员去订阅它,加分。
public class Gate : MonoBehaviour {
    public delegate void AddScore();//委托
    public static event AddScore addScore;//事件
	// Use this for initialization

    void OnTriggerEnter(Collider collider)
    {
        if (collider.gameObject.tag == "Player")
        {
            if (this.transform.parent.GetComponent<GetGuard>().guard.GetComponent<GuardController>().state == 0)
                this.transform.parent.GetComponent<GetGuard>().guard.GetComponent<GuardController>().state = 1;
            else
            {
                this.transform.parent.GetComponent<GetGuard>().guard.GetComponent<GuardController>().state = 0;
                escape();
            }
        }
        if(collider.gameObject.tag == "Guard")
        {
            this.transform.parent.GetComponent<GetGuard>().guard.GetComponent<GuardController>().state = 0;
        }
    }

    void escape()
    {
        if(addScore != null)
        {
            addScore();//发布事件
        }
    }
}
然后在记分员里面订阅
public class ScoreRecorder : MonoBehaviour {
    public Text ScoreText;
    int Score = 0;
    void GetScore()
    {
        Score++;
    }
	// Use this for initialization
	void Start () {
        Gate.addScore += GetScore;//订阅事件
	}
	
	// Update is called once per frame
	void Update () {
        ScoreText.text = "Score:" + Score.ToString();//更新分数
    }
}
然后在role主角写脚本判断与Guard碰撞,即为被抓
public class RoleTrigger : MonoBehaviour {
    public delegate void GameOver();//委托
    public static event GameOver gameOver;//事件
    void OnCollisionEnter(Collision other)
    {
        if(other.gameObject.tag == "Guard")
        {
            if(gameOver != null)
            {
                gameOver();//发布事件
            }
        }
    }
}
然后写一下用户接口和场记就行(导演默认你们写好了,前面的博客有)
public interface IUserAction
{
    void ShowDetail();//显示规则
    void ReStart();//重新开始
}

public class UserInterface : MonoBehaviour {
    private IUserAction action;
    public GameObject role;
    public float speed = 1;
    public SceneController sceneController;
    // Use this for initialization
    void Start () {
        action = SSDirector.getInstance().currentScenceController as IUserAction;
        sceneController = (SceneController)SSDirector.getInstance().currentScenceController;
        role = sceneController.role;
    }
    void OnGUI()
    {
        GUIStyle fontstyle1 = new GUIStyle();
        fontstyle1.fontSize = 50;
        fontstyle1.normal.textColor = new Color(255, 255, 255);
        if (GUI.RepeatButton(new Rect(0, 0, 120, 40), "ESCAPE"))
        {
            action.ShowDetail();
        }
        if (GUI.Button(new Rect(0, 60, 120, 40), "RESTART"))
        {
            action.ReStart();
        }
    }
    // Update is called once per frame
    void Update () {
        if(sceneController.game != 0)//游戏开始状态才可以接受用户输入移动主角
        {
            float translationX = Input.GetAxis("Horizontal") * speed;
            float translationZ = Input.GetAxis("Vertical") * speed;
            translationX *= Time.deltaTime;
            translationZ *= Time.deltaTime;
            role.transform.Translate(translationX, 0, 0);
            role.transform.Translate(0, 0, translationZ);
        }
    }
}

实现导演要要载入资源用代码将多个正六边形拼接起来(自己作死)自己先手动计算偏移量,然后代码实现
public class SceneController : MonoBehaviour, ISceneController, IUserAction
{
    public GameObject role;
    public Text FinalText;
    public int game = 1;
    int size = 5;
    void Awake()
    //创建导演实例并载入资源  
    {
        SSDirector director = SSDirector.getInstance();
        director.setFPS(60);
        director.currentScenceController = this;
        director.currentScenceController.LoadResources();
    }
    public void LoadResources()  //载入资源  
    {
        Instantiate(Resources.Load("Prefabs/Light"));
        role = Instantiate(Resources.Load("Prefabs/role")) as GameObject;
        int pos_Z = 0, pos_X = 0,k;
        for(int i = 0; i < size; i++)
        {
            pos_X = i * 6;
            if (i <= size / 2)
            {
                k = i;
                pos_Z = -k * 2;
            } 
            else
            {
                k = (size - i - 1);
                pos_Z = - k * 2;
            }
            for (int j = 0; j <= k; j++)
            {
                Instantiate(Resources.Load("Prefabs/Maze"), new Vector3(pos_X, 0, pos_Z), Quaternion.identity);
                Instantiate(Resources.Load("Prefabs/Guard"), new Vector3(pos_X - 2, 0, pos_Z), Quaternion.identity);//每个地图初始化一个巡逻兵,因为我觉得这样写更简单所以我没写工厂了
                pos_Z += 4;
            }
        }
    }
    // Use this for initialization
    void Start () {
        RoleTrigger.gameOver += GameOver;//订阅事件
	}
	
	// Update is called once per frame
	void Update () {
		
	}
    void GameOver()
    {
        FinalText.text = "Game Over!!!";
        game = 0;
    }
    //实现IUserAction的接口
    public void ShowDetail() {
        GUIStyle fontstyle1 = new GUIStyle();
        fontstyle1.fontSize = 20;
        fontstyle1.normal.textColor = new Color(255, 255, 255);
        GUI.Label(new Rect(220, 50, 500, 500), "In the Maze you play a role, Try to escape away from\n Guard. Each success escape for one point.if you are\n caught by Guard, Game Over!!! Good Luck",fontstyle1);
    }
    //实现IUserAction的接口
    public void ReStart() {
        SceneManager.LoadScene("Task1");
        game = 1;
    }
}
然后我加了个摄像机随主角移动脚本
public class CameraMove : MonoBehaviour {
    public GameObject role;
    public SceneController sceneController;
    private Vector3 offset;
    // Use this for initialization
    void Start () {
        sceneController = (SceneController)SSDirector.getInstance().currentScenceController;
        role = sceneController.role;
        offset = role.transform.position - this.transform.position;
	}
	
	// Update is called once per frame
	void Update () {
        this.transform.position = role.transform.position - offset;
	}
}

看看地图效果


这次作业就这样了,等以后有时间再改进,我想加入自动门功能,还有难度梯度,和动画人物模型。不过一切等我计组CPU,实训打完再说哭哭哭

2017-02-03 14:39:59 cheng219101 阅读数 226

之前介绍了状态及状态机的概念,并且有个简单的跳转。

下面深入一点,写一个人物的普通状态、寻路状态导致的动作变化(站立状态到移动状态到骑马状态)

1、State类保持不变

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

public class State
{
    /// 
    /// 状态名
    /// 
    public string name = "";
    /// 
    /// 构造
    /// 
    public State(string _name)
    {
        name = _name;
    }
    public State()
    {

    }

    public delegate void TransitionEventHandler(State _from, State _to);
    public delegate void OnActionHandler(State _curState);

    /// 
    /// 进入时的事件
    /// 
    public TransitionEventHandler onEnter;//System.Action
    /// 
    /// 退出时的事件 
    /// 
    public TransitionEventHandler onExit;
    /// 
    /// 在状态机Update()时候调用的更新事件
    /// 
    public OnActionHandler onAction;

    public virtual void Enter(State _from, State _to)
    {
        if (onEnter != null)
            onEnter(_from, _to);
    }

    public virtual void Excute(State _curState)
    {
        if (onAction != null)
            onAction(_curState);
    }

    public virtual void Exit(State _from, State _to)
    {
        if (onExit != null)
            onExit(_from, _to);
    }
}

2、Machine小改动了下,添加了能跳转的状态列表

using UnityEngine;
using System.Collections;

public class Machine
{
    private State curState = null;
    private State lastState = null;

    /*Update*/  
    public void Update ()  
    {  
        if (curState != null)
            curState.Excute(curState);  
    }  
  
    /*状态改变*/  
    public void ChangeState (State _newState)  
    {
        if (_newState == null)
        {  
            Debug.LogError ("can't find this state");
            return;
        }
        if (curState != null && !curState.nextStates.Contains(_newState))
        {
            Debug.LogError("can't change to the state that curState don't contains");
            return;
        }
          
        //触发退出状态调用Exit方法  
        curState.Exit(curState, _newState);  
        //保存上一个状态   
        lastState = curState;  
        //设置新状态为当前状态  
        curState = _newState;  
        //m_pCurrentState.Target = m_pOwner;  
        //进入当前状态调用Enter方法  
        curState.Enter(lastState, curState);  
    }

    public Machine()
    {
        curState = null;
        lastState = null;
    }

    public Machine(State _curState)
    {
        curState = _curState;
        lastState = new State("init");
        curState.Enter(lastState, _curState);
    }

    public void SetInitState(State _curState)
    {
        curState = _curState;
        lastState = new State("init");
        curState.Enter(lastState, _curState);
    }

    public string GetCurStateName()
    {
        if (curState != null)
        {
            return curState.name;
        }
        return string.Empty;
    }

    /// 
    /// 重启状态机
    /// 
    public void Restart()
    {
        
    }
}

3、添加了一个继承MonoBehaviour的基类FSMBase,要用到状态变化就继承这个类

using UnityEngine;
using System.Collections;

public class FSMBase : MonoBehaviour {

    protected Machine stateMachine;
	void Start () {
        stateMachine = new Machine();
        InitStateMachine();
	}

    void Update()
    {
        if (stateMachine != null)
            stateMachine.Update();
    }

    public virtual void Reset()
    {
        if (stateMachine != null)
            stateMachine.Restart();
    }

    protected virtual void InitStateMachine() { }
}

4、上篇中的TestMachine改为AnimMachine,并且添加了骑马状态的情况

using UnityEngine;
using System.Collections;

public class AnimMachine : FSMBase {

    protected State moveState = null;
    protected State idleState = null;
    protected State attackState = null;
    protected State rideState = null;

    protected override void InitStateMachine()
    {
        base.InitStateMachine();
        idleState = new State("idle");
        idleState.onEnter = EnterIdle;
        idleState.onExit = ExitIdle;
        idleState.onAction = ExcuteIdle;

        moveState = new State("move");
        moveState.onEnter = EnterMove;
        moveState.onExit = ExitMove;
        moveState.onAction = ExcuteMove;

        attackState = new State("attack");
        attackState.onEnter = EnterAttack;
        attackState.onExit = ExitAttack;
        attackState.onAction = ExcuteAttack;

        rideState = new State("ride");
        rideState.onEnter = EnterRide;
        rideState.onExit = ExitRide;
        rideState.onAction = ExcuteRide;

        idleState.nextStates.Add(moveState);
        idleState.nextStates.Add(attackState);
        idleState.nextStates.Add(rideState);

        moveState.nextStates.Add(idleState);
        moveState.nextStates.Add(attackState);
        moveState.nextStates.Add(rideState);

        attackState.nextStates.Add(idleState);
        attackState.nextStates.Add(moveState);

        rideState.nextStates.Add(idleState);

        stateMachine.SetInitState(idleState);
    }

    void EnterIdle(State _from, State _to)
    {
        Debug.Log("EnterIdle _from:" + _from.name);
    }
    void ExitIdle(State _from, State _to)
    {
        Debug.Log("ExitIdle _to:" + _to.name);
    }
    void ExcuteIdle(State _curState)
    {

    }

    void EnterMove(State _from, State _to)
    {
        Debug.Log("EnterMove _from:" + _from.name);
    }
    void ExitMove(State _from, State _to)
    {
        Debug.Log("ExitMove _to:" + _to.name);
    }
    void ExcuteMove(State _curState)
    {

    }

    void EnterAttack(State _from, State _to)
    {
        Debug.Log("EnterAttack _from:" + _from.name);
    }
    void ExitAttack(State _from, State _to)
    {
        Debug.Log("ExitAttack _to:" + _to.name);
    }
    void ExcuteAttack(State _curState)
    {

    }

    void EnterRide(State _from, State _to)
    {
        Debug.Log("EnterRide _from:" + _from.name);
    }
    void ExitRide(State _from, State _to)
    {
        Debug.Log("ExitRide _to:" + _to.name);
    }
    void ExcuteRide(State _curState)
    {

    }

    public void Idle()
    {
        if (stateMachine != null)
            stateMachine.ChangeState(idleState);
    }

    public void Move()
    {
        if (stateMachine != null)
            stateMachine.ChangeState(moveState);
    }

    public void Ride()
    {
        if (stateMachine != null)
            stateMachine.ChangeState(rideState);
    }
}

5、主玩家类,MainPlayer

using UnityEngine;
using System.Collections;

public class MainPlayer : FSMBase {

    protected State normalState = null;
    protected State taskPathState = null;//任务寻路

    private AnimMachine animMachaine = null;

    void Awake()
    {
        if (animMachaine == null)
        {
            animMachaine = gameObject.GetComponent();
        }
        if (animMachaine == null)
        {
            animMachaine = gameObject.AddComponent();
        }
    }

    protected override void InitStateMachine()
    {
        base.InitStateMachine();
        normalState = new State("normal");
        normalState.onEnter = EnterNormalState;
        normalState.onExit = ExitNormalState;
        normalState.onAction = ExcuteNormalState;

        taskPathState = new State("taskPathState");
        taskPathState.onEnter = EnterTaskPathState;
        taskPathState.onExit = ExitTaskPathState;
        taskPathState.onAction = ExcuteTaskPathState;

        normalState.nextStates.Add(taskPathState);

        taskPathState.nextStates.Add(normalState);

        stateMachine.SetInitState(normalState);
    }

    void EnterNormalState(State _from, State _to)
    {
        Debug.Log("EnterNormalState _from:" + _from.name);
        animMachaine.Idle();
    }
    void ExitNormalState(State _from, State _to)
    {
        Debug.Log("ExitNormalState _to:" + _to.name);
    }
    void ExcuteNormalState(State _curState)
    {

    }

    protected float enterTaskPathTime = 0f;
    protected bool isRiding = false;
    void EnterTaskPathState(State _from, State _to)
    {
        Debug.Log("EnterTaskPathState _from:" + _from.name);
        animMachaine.Move();
        enterTaskPathTime = Time.time;
    }
    void ExitTaskPathState(State _from, State _to)
    {
        Debug.Log("ExitTaskPathState _to:" + _to.name);
        isRiding = false;
    }
    void ExcuteTaskPathState(State _curState)
    {
        if (Time.time - enterTaskPathTime > 5f && !isRiding)
        {
            animMachaine.Ride();
            isRiding = true;
        }
    }

    void OnGUI()
    {
        if (GUILayout.Button("normal"))
        {
            if (stateMachine != null)
                stateMachine.ChangeState(normalState);
        }
        if (GUILayout.Button("taskPath"))
        {
            if (stateMachine != null)
                stateMachine.ChangeState(taskPathState);
        }

        GUILayout.Label(stateMachine != null ? stateMachine.GetCurStateName() : "");
    }
}

这个问题很简单,

博文 来自: u014313341

人物左右的移动

博文 来自: JAROD_JIANGHE
没有更多推荐了,返回首页