第四天

  • 创建一个C# script
  • 编辑
  • 教程代码 8个方位水平移动
  • 个人练习 实现任意方向的水平运动
  • API
  • 启用脚本
  • 总结
  • 下一节


创建一个C# script

unity走马灯代码 unity行走_Unity

编辑

unity走马灯代码 unity行走_unity走马灯代码_02


默认是virtual studio打开,官方社区版免费

教程代码 8个方位水平移动

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

public class PlayerMovement : MonoBehaviour
{

public float turnSpeed = 20f;//角速度为20
Vector3 m_Movement;//声明Vector3对象,它代表一个三维的向量,用记录玩家期望的朝向
Quaternion m_Rotation = Quaternion.identity;//声明一个四元数对象m_Rotation,并初始化为一个代表无旋转的值,用于记录旋转

Animator m_Animator;//声明一个Animator对象,为Animator组件所用
Rigidbody m_Rigidbody;//声明一个Rigidbody对象,为Rigidbody组件所用

// Start is called before the first frame update
void Start()
{
    m_Animator = GetComponent<Animator>();//关联Animator组件,<>括号内是类型参数用于查询Animator组件
    m_Rigidbody = GetComponent<Rigidbody>();//关联Animator组件
}
// 每帧调用一次
void FixedUpdate()
{
    /*获得水平轴和垂直轴的矢量值*/
    float horizontal = Input.GetAxis("Horizontal");//获得水平轴的值 AD
    float vertical = Input.GetAxis("Vertical");//获得垂直轴的值 WS
    /*合成目标向量*/
    m_Movement.Set(horizontal, 0f, 0f);//设置移动向量的值,该向量由三个坐标轴的值构成,竖直值轴的值应该为0,0f代表该数为浮点数类型,因为John不会向上或向下移动
    m_Movement.Normalize();//规范化该向量,使其在任何方向的大小一致

    /*是否移动的bool值*/
    bool hasHorizontalInput = !Mathf.Approximately(horizontal, 0f);//如果水平轴的值近似为0则该值为false
    bool hasVerticalInput = !Mathf.Approximately(vertical, 0f);//如果垂直轴的值近似为0则该值为false
    bool isWalking = hasHorizontalInput || hasVerticalInput;//是否行走,很明显这里是或的关系,true行走,false空闲
    m_Animator.SetBool("IsWalking",isWalking);//这里将isWalking的布尔值传入先前的IsWalking参数

    /*获得旋转所需的四元数值*/
    Vector3 desiredForward = Vector3.RotateTowards(transform.forward, m_Movement, turnSpeed * Time.deltaTime, 0f);//得到朝向
    //@1 transform.forward 是个访问Transform组件的捷径,获得当前的朝向
    //@2 目标朝向
    //@3 每帧的变化角度 d_angle = turnSpeed * time.deltaTime,time.deltaTime是每帧之间的间隔时间
    //@4 
    m_Rotation = Quaternion.LookRotation(desiredForward);//得到的旋转
}
// 将之前所得到的m_Movement和m_Rotation应用于root motion
void OnAnimatorMove()
{
    /*实现移动*/
    m_Rigidbody.MovePosition(m_Rigidbody.position + m_Movement * m_Animator.deltaPosition.magnitude);
    /*实现转向*/
    m_Rigidbody.MoveRotation(m_Rotation);
}
}    
//ps:类名要和文件名一致,否则无法运行,public修饰的变量将会在Inspector窗口显示

个人练习 实现任意方向的水平运动

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

/*改良版,将胶囊碰撞组件替换成了Character controller组件,解决了碰撞后乱动的问题,顺便试试中文编码*/

public class CharacterControl : MonoBehaviour
{

    private struct State //存储状态信息的结构体
    {
        public bool hasLeftInput;
        public bool hasRightInput;
        public bool hasForwardInput;
        public bool hasBackwardInput;
        public bool isCircling;
        public bool isGoStright;
        public bool isTurning;
        public bool isRetreat;
        public bool isWalking;
        public bool isIdel;
    };
    private bool retreatEnable; //是否允许快速转身
    private bool isMoving;
    private float angle;
    private float 移动速度;
    private Vector3 moveDirection;
    private Quaternion m_Rotation;
    private CharacterController m_Controller;
    private Animator m_Animator;
    private Rigidbody m_Rigidbody;
    /*set control key*/
    public KeyCode 左转键 = KeyCode.LeftArrow;
    public KeyCode 右转键 = KeyCode.RightArrow;
    public KeyCode 前进键 = KeyCode.UpArrow;
    public KeyCode 快速回头键 = KeyCode.DownArrow;
    public float 最大移动速度 = 0.025f; //移动速度
    public float 最大角速度 = 20f; //模型的旋转速度
    public float 静止角速度 = 4f; //每帧最多转4度
    public float 运动角速度 = 4f; //每帧最多转4度
    public float 加速度 = 0.001f; //每帧
    public string walkControl = string.Copy("Iswalk");
    // Start is called before the first frame update
    void Start()
    {
        m_Controller = GetComponent<CharacterController>();
        m_Animator = GetComponent<Animator>();
        m_Rigidbody = GetComponent<Rigidbody>();
        initData();
    }
    // Update is called once per frame
    void FixedUpdate()
    {
        State state = StateCheck();//检查按键获得状态
        m_Animator.SetBool("walkControl", state.isWalking); //播放移动动画
        moveDirection = MoveByThis(state);//获得移动方向
        m_Rotation = Quaternion.LookRotation(Vector3.RotateTowards(transform.forward, moveDirection, 最大角速度 * Time.deltaTime, 0f));//获得下一帧的旋转量
    }
    void OnAnimatorMove()
    {
        m_Controller.Move((isMoving? 1f:0f)*moveDirection);//移动
        m_Rigidbody.MoveRotation(m_Rotation);//旋转
    }
    State StateCheck()//检查按键获得状态
    {
        State state = new State();
        state.hasLeftInput = Input.GetKey(左转键);
        state.hasRightInput = Input.GetKey(右转键);
        state.hasForwardInput = Input.GetKey(前进键);
        state.hasBackwardInput = Input.GetKey(快速回头键);
        bool hasVerticalInput = state.hasForwardInput || state.hasBackwardInput && state.hasBackwardInput != state.hasForwardInput;//异或
        bool hasHorizontalInput = state.hasLeftInput || state.hasRightInput && state.hasRightInput != state.hasLeftInput;
        state.isCircling = !hasVerticalInput && hasHorizontalInput;//是否处于原地转圈状态
        state.isGoStright = state.hasForwardInput && !hasHorizontalInput;//是否处于直行状态
        state.isTurning = state.hasForwardInput && hasVerticalInput;//是否处于转向行走状态
        state.isRetreat = state.hasBackwardInput && !hasHorizontalInput;//是否回头
        state.isWalking = state.hasForwardInput || hasHorizontalInput;//是否播放行走动画
        state.isIdel = !hasVerticalInput && !hasHorizontalInput;
        isMoving = state.isGoStright || state.isTurning;
        return state;
    }
    Vector3 MoveByThis(State state)//获得移动方向
    {
        
        Vector3 Direction = Vector3.zero;
        if (Mathf.Approximately(angle % 360f, 0f))//watch dog 防止溢出
        {
            angle = 0f;
        }
        if( 移动速度 < 最大移动速度 && state.hasForwardInput)
        {
            移动速度 += 加速度;
        }//更新速度
        if (state.isCircling) 
        {
            if (state.hasRightInput)
            {
                angle += 静止角速度;
            }
            else if (state.hasLeftInput)
            {
                angle -= 静止角速度;
            }
            移动速度 = 最大移动速度;
            retreatEnable = true;//置为允许快速转身
            Direction.Set(移动速度 * Mathf.Sin(angle * Mathf.PI / 180), 0f, 移动速度 * Mathf.Cos(angle * Mathf.PI / 180));
            return Direction;
        }
        else if (state.isRetreat)//快速转身,只有在retreatEnable为true的情况下才能快速转身
        {
            if (retreatEnable)
            {
                angle += 180f;
                retreatEnable = false;//快速转身置为不允许,防止按住不放,反复转身
                移动速度 = 最大移动速度;
            }
            Direction.Set(移动速度 * Mathf.Sin(angle * Mathf.PI / 180), 0f, 移动速度 * Mathf.Cos(angle * Mathf.PI / 180));
            return Direction;
        }
        else if (state.isTurning)//移动中转向,两帧4度
        {
            if (state.hasRightInput)
            {
                angle += 运动角速度;
            }
            else if (state.hasLeftInput)
            {
                angle -= 运动角速度;
            }
            Direction.Set(移动速度 * Mathf.Sin(angle * Mathf.PI / 180), 0f, 移动速度 * Mathf.Cos(angle * Mathf.PI / 180));
            return Direction;
        }
        else//仅仅按住W或什么都没按的时候
        {
            if(state.isIdel)
            {
                移动速度 = 0f;
            }
             retreatEnable = true;
             return moveDirection;
        }
    }
    void initData()//初始化一些变量
    {
        angle = 0f;
        retreatEnable = true;
        移动速度 = 0f;
        m_Rotation = Quaternion.identity;
        moveDirection = Vector3.zero;
    }
}

API

Unity官方API信息

启用脚本

单击Hierarchy窗口中的JohnLemon,Inspector窗口open prefab,选中Asset/Scripts中的脚本拖到Inspector窗口的最下边
保存运行就可以使用WASD控制John一移动了。

总结

游戏角色行为是玩家输入行为的映射应该要一一对应,每一种同类行为动作之间关系应该是互斥的,比如向左走的同时就不可能向右走。

下一节

添加环境模型光效以及导航网格