Unity3D AnimationControl

教程来自于Youtube-Aaron HibberdUnity 5 Tutorial - Animation Control

视频简介

Use the Animator Controller to play and blend between animations of the beautiful Unity-Chan model from Unity Technologies Japan

Music Composed By Stephen Lu

Donations are greatly appreciated: Here

Download Complete Project: Here

Learn More: Here

PART TWO: Here

内容是Unity3D的动画控制 稍微记录一下流程与坎坷

其资源文件位于Unity内的Asset Store里的UnityChan,免费搜索下载即可使用

下载后可直接导入项目,但由于是Untiy 4时代制作的模型所以并没有很好的向下兼容,警告可以忽略

首先将场景置于[0, 0, 0], 从GameObject -> 3D Object -> Plane导入一个平面,将Scale设置为 [100, 0, 100]意为把平面放大到一个100*100的正方形地面

同时UnityChan自带了舞台的制材,可以从Asset -> UnityChan -> Stage -> Materials 里导入想要的素材,拖放到平面上即可

导入UnityChan模型,位于Asset -> UnityChan -> Models -> unitychan, 拖入场景中并置于[0, 0, 0]

此时将摄像头置于 [0, 1, 2]可正面对着模型,同时在Hierarchy中将摄像头拖入unitychan成为其次级,可跟随其移动而移动

在项目面板创建Animation文件夹,并创建一个Animator Controller命名为player,将此动画控制拖入unitychanInspector面板中的Animator下的Controller,去掉Apply Root Motion的勾,意味着在动画的同时,模型的位置和角度不会同时跟着变化

Window -> Animator中调出Animator窗口

Unity Chan资源包中自带了许多动画,可从Asset -> UnityChan -> Animations中取用

1.添加空闲动作

选取unitychan_WAIT00unitychan_WAIT01unitychan_WAIT02unitychan_WAIT03unitychan_WAIT04 拖动到Animator窗口中,作为unitychan空闲时的动作

点击unitychan_WAIT00右键,将其设为Default Animation默认动作,并设置一条从Entryunitychan_WAIT00的连线,则从开始之后,unitychan一直执行unitychan_WAIT00动作直到游戏结束

unitychan_WAIT01等其他四个动作都添加连线到unitychan_WAIT00, 意味所有动作结束后都回到unitychan_WAIT00状态

Project 面板创建一个 Scripts 文件夹,此时创建一个 C# 脚本文件 player,用于读取输入并控制动画效果

添加代码如下

using UnityEngine;  
using System.Collections;

public class player : MonoBehaviour {

    public Animator anim;

    // Use this for initialization
    void Start () 
    {
        anim = GetComponent<Animator>();
    }

    // Update is called once per frame
    void Update () 
    {
        if(Input.GetKeyDown("1"))
        {
            anim.Play("WAIT01",-1,0f);
        }
        if(Input.GetKeyDown("2"))
        {
            anim.Play("WAIT02",-1,0f);
        }
        if(Input.GetKeyDown("3"))
        {
            anim.Play("WAIT03",-1,0f);
        }
        if(Input.GetKeyDown("4"))
        {
            anim.Play("WAIT04",-1,0f);
        }
    }
}

首先创建一个公有的 Animator 变量 anim, 在Start()函数里初始化

我们希望在键盘输入1, 2, 3, 4 的时候分别执行 unitychan_WAIT01unitychan_WAIT02unitychan_WAIT03unitychan_WAIT04 动作,因此在 Update() 函数中使用 Input.GetKeyDown() 函数来检测键盘输入,一旦按下 1, 2, 3, 4 时动画便会被执行

其中 anim.Play() 函数的三个参数分别是 "要执行的动画", "在 Animator 窗口中位于的层数", "动画开始的相对位置"

2.添加被攻击动作

现在加入两个动画 unitychan_DAMAGED00unitychan_DAMAGED01, 拖入 Animator 窗口, 创建连线到 WAIT00 在鼠标左键点击的时候产生被攻击的效果

Update() 函数中添加代码如下

        if(Input.GetMouseButtonDown(0))
        {
            int n = Random.Range(0,2);

            if(n == 0 || n ==1)
            {
                anim.Play ("DAMAGED00",-1,0f);
            }
            else
            {
                anim.Play ("DAMAGED01",-1,0f);
            }
        }

这里设置了一个随机值 n ,当 n==0n==1 的时候播放 DAMAGED00 动画, 否则播放 DAMAGED01

这里 Input.GetMouseButtonDown() 中的参数 0 代表鼠标左键, 1代表鼠标右键, 2 代表鼠标中键

3.添加移动动作

Animator 面板的 Parameters 标签页内添加两个 float 类型变量 inputHinputV

其中 inputH 代表水平移动量, 表示左右; inputV 代表垂直移动量, 表示前后

添加代码如下

using UnityEngine;  
using System.Collections;

public class player : MonoBehaviour {

    public Animator anim;

    private float inputH;
    private float inputV;

    // Use this for initialization
    void Start () 
    {
        anim = GetComponent<Animator>();
    }

    // Update is called once per frame
    void Update () 
    {
        if(Input.GetKeyDown("1"))
        {
            anim.Play("WAIT01",-1,0f);
        }
        if(Input.GetKeyDown("2"))
        {
            anim.Play("WAIT02",-1,0f);
        }
        if(Input.GetKeyDown("3"))
        {
            anim.Play("WAIT03",-1,0f);
        }
        if(Input.GetKeyDown("4"))
        {
            anim.Play("WAIT04",-1,0f);
        }

        if(Input.GetMouseButtonDown(0))
        {
            int n = Random.Range(0,2);

            if(n == 0)
            {
                anim.Play ("DAMAGED00",-1,0f);
            }
            else
            {
                anim.Play ("DAMAGED01",-1,0f);
            }
        }

        inputH = Input.GetAxis ("Horizontal");
        inputV = Input.GetAxis ("Vertical");

        anim.SetFloat("inputH",inputH);
        anim.SetFloat("inputV",inputV);

添加两个私有 float 变量 inputHinputV ,并且赋值为 Input.GetAxis ("Horizontal")Input.GetAxis ("Vertical") 意为读取水平与垂直方向上的输入

同时将值传递到 anim 动画控制器中

此时运行则可以发现随着输入, Animator 窗口的 Parameters 标签页的 inputHinputV 已经可以随着输入改变数值了

AnimatorBaseLayer 内右击,选择 Create State -> From New Blend Tree 创建混合树,重命名为 walk, 双击进入混合树

在右边的 Inspector 面板内修改设置为下图

walk blend tree

回到 BaseLayer ,创建从 WAIT00walk 的连线,不勾选 Has Exit Time ,意为无需等待就可以从上一动作切换到下一动作,不必等待上个动作执行完毕 设置条件 inputV Greater 0.1 , 即当垂直输入大于 0.1 时,从 WAIT00 切换到 walk, 为前进

同上再创建连线,设置条件 inputV less -0.1 , 即当垂直输入小于 -0.1 时,从 WAIT00 切换到 walk, 为后退

创建从 walkWAIT00 的连线,设置条件 inputV Less 0.1inputV Greater -0.1,即当垂直输入在 -0.1至0.1之间时,从 walk 切换到 WAIT00

为了使 unitychan 能够在现实世界坐标移动,此时给 unitychan 添加刚体元素

添加代码如下

using UnityEngine;  
using System.Collections;

public class player : MonoBehaviour {

    public Rigidbody rbody;

    private float inputH;
    private float inputV;

    // Use this for initialization
    void Start () 
    {
        rbody = GetComponent<Rigidbody>();
    }

    // Update is called once per frame
    void Update () 
    {
        inputH = Input.GetAxis ("Horizontal");
        inputV = Input.GetAxis ("Vertical");

        anim.SetFloat("inputH",inputH);
        anim.SetFloat("inputV",inputV);

        float moveX = inputH * 20f * Time.deltaTime;
        float moveZ = inputV * 50f * Time.deltaTime;

        if(moveZ <= 0f)
        {
            moveX = 0f;
        }

        rbody.velocity = new Vector3(moveX,0f,moveZ);
    }
}

添加公有 rigidbody 元素 rbody , 在 Start() 函数里面初始化 rbody 对象, 在 Update() 函数内设置两个 float 变量来记录 X轴Z轴 上的移动量, 并且设置刚体的速度 rbody.velocity[moveX, 0f, moveZ]

当垂直移动量小于等于0时, 设置为不能水平移动

同时在 unitychanRigidbody 内的 Constraints 里将旋转勾选成不变

4.其他工作

添加跑、跳跃、滑动等动作

如上则不再赘述

最终 animator 如图 animator

注意点: 公有变量 AnimRbody 必须在unity中附上 unitychan 对象,否则在世界坐标中无法走动

成品代码 player.cs

using UnityEngine;  
using System.Collections;

public class player : MonoBehaviour {

    public Animator anim;
    public Rigidbody rbody;

    private float inputH;
    private float inputV;
    private bool run;

    // Use this for initialization
    void Start () 
    {
        anim = GetComponent<Animator>();
        rbody = GetComponent<Rigidbody>();
        run = false;
    }

    // Update is called once per frame
    void Update () 
    {
        if(Input.GetKeyDown("1"))
        {
            anim.Play("WAIT01",-1,0f);
        }
        if(Input.GetKeyDown("2"))
        {
            anim.Play("WAIT02",-1,0f);
        }
        if(Input.GetKeyDown("3"))
        {
            anim.Play("WAIT03",-1,0f);
        }
        if(Input.GetKeyDown("4"))
        {
            anim.Play("WAIT04",-1,0f);
        }

        if(Input.GetMouseButtonDown(0))
        {
            int n = Random.Range(0,2);

            if(n == 0)
            {
                anim.Play ("DAMAGED00",-1,0f);
            }
            else
            {
                anim.Play ("DAMAGED01",-1,0f);
            }
        }

        if(Input.GetKey(KeyCode.LeftShift))
        {
            run = true;
        }
        else
        {
            run = false;
        }

        if(Input.GetKey(KeyCode.Space))
        {
            anim.SetBool("jump",true);
        }
        else
        {
            anim.SetBool("jump", false);
        }

        inputH = Input.GetAxis ("Horizontal");
        inputV = Input.GetAxis ("Vertical");

        anim.SetFloat("inputH",inputH);
        anim.SetFloat("inputV",inputV);
        anim.SetBool ("run",run);

        float moveX = inputH*20f*Time.deltaTime;
        float moveZ = inputV*50f*Time.deltaTime;

        if(moveZ <= 0f)
        {
            moveX = 0f;
        }
        else if(run)
        {
            moveX*=3f;
            moveZ*=3f;
        }

        rbody.velocity = new Vector3(moveX,0f,moveZ);

    }
}

最终完成界面scene01