2019-06-25 15:50:37 wangjiangrong 阅读数 725
  • Unity3D实战入门之第三人称射击游戏(TPS)

    这是一套第三人称射击游戏开发的入门基础课程。 本课程以一款小型的第三人称射击游戏为案例,手把手教你如何搭建一款3D射击游戏的雏形。麻雀虽小,五脏俱全,这款小游戏难度不大,但第三人称射击游戏该有的模块、功能它都有涉及。通过案例游戏的开发,学习者可以对第三人称射击游戏入门,对后续复杂大中型3D游戏的开发有实际的借鉴意义。 这套课程,对初次接触3D射击类游戏的学习者而言,是一块极好的跳板。

    1876 人正在学习 去看看 伍晓波

今天主要是简单的模拟下吃鸡游戏的第三人称摄像机的实现。

观察

我们玩过吃鸡手游的都知道,吃鸡的人物跟随的摄像机有两种状态

1. 滑动屏幕的时候,摄像机左右上下移动,人物也会跟随着左右移动,上下抬头低头(播放相应的动画,但是不上下移动)。摄像机始终保持着观察人物背面。左右可以360度旋转,上下会有个最大值和最小值的限制,无法达到0度往上看或90度往下看。

2. 按住拖动右上角小眼睛按钮,摄像机左右上下移动,人物不会跟随着左右转动,头会左右上下摆动。几乎可以360度观察人物。上下左右的限制与情况1相同。

在不考虑头部转动的情况下,可以总结出两种情况摄像机的转动围绕人物的规则都是一样的,即以一个固定半径形成的球面上移动,除去了球面的最上和最下的部分,视角始终看向球心。而当滑动屏幕的情况下,人物也随之左右自转,保持摄像机观察人物的正背面。

分析

根据表现的现象,我们可以分析出我们需要实现的逻辑。我们可以想象人物的正后面有一条直线line,方向为(0,0,-1),摄像机离人物距离为distance,初始值摄像机的位置为(0,0,-distance)

当摄像机进行360度移动的时候,我们可以把移动操作拆分为两个步骤,首先是摄像机垂直移动,即以人物为中心,半径为distance的圆上移动。设垂直角度为α(0<min<α<max<90),则摄像机的高度y轴为distance*sin α,离人物的z轴距离为distance*cos α。此时摄像机的位置为(0,distance*sin α,-distance*cos α)

然后在之前的基础上,我们对摄像头进行水平移动,即以人物头顶高度distance*sin α的点为中心,半径为distance*cos α的圆上移动。设水平角度为β,则摄像机x轴为distance*cos α * sin β,z轴距离则更新为distance*cos α * cos β。

所以我们可以用两个角度α,β,来控制摄像机的转动,上下移动更新α值,左右移动更新β值,摄像机的位置为(distance*cos α * sin β, distance*sin α, -distance*cos α * cos β),摄像机始终看向(LookAt)人物

接着我们要区分两种情况的人物状态,当滑动屏幕的时候,人物会随之一起反方向转动,从而保证摄像机始终观察着人物背面。所以当摄像机水平转了β度时,人物的转向应该为(0, -β, 0)。而按住小眼睛的时候,人物不需要随之移动,但是当松开小眼睛的时候,摄像头需要回归原始位置,所以在按下的时候,我们需要纪录按下时摄像机的偏移角度,以供松开时复原。

实现

Demo中我们一共有四个cs文件,一个控制小眼睛按钮,一个控制屏幕滑动,一个控制人物移动,最后一个就是控制摄像机了。

还有需要搭建一个简单的测试场景,里面有一个方块模拟角色,一个plane做地面,然后添加一个UGUI的Button做小眼睛按钮。

首先是人物移动的控制器PlayerController,就是做了简单的位移

using UnityEngine;

namespace Test
{
    public class PlayerController : MonoBehaviour
    {
        //速度转换
        const float SPEED_CONVERTER = 0.3f;

        void Update()
        {
            float inputV = Input.GetAxis("Vertical");
            float inputH = Input.GetAxis("Horizontal");

            this.transform.position += this.transform.forward * inputV * SPEED_CONVERTER;
            this.transform.position += this.transform.right * inputH * SPEED_CONVERTER;
        }
    }
}

接着是我们的摄像机控制器ThridPlayerCameraController,对摄像机的转动进行操作

using UnityEngine;

namespace CameraTool
{
    [RequireComponent(typeof(Camera))]
    public class ThridPlayerCameraController : MonoBehaviour
    {
        [SerializeField] Transform m_target;
        //相机与人物距离
        [SerializeField] float m_distance = 5;
        //初始化的偏移角度,以人物的(0,0,-1)为基准
        [SerializeField] float m_offsetAngleX = 0;
        [SerializeField] float m_offsetAngleY = 45;

        //相机与人物的坐标的偏移量
        Vector3 m_offsetVector;
        //纪录偏移角度用于复原
        float m_recordAngleX;
        float m_recordAngleY;
        //相机是否在旋转,旋转中需要一直重新计算 m_offsetVector
        bool m_isRotateing = false;

        //弧度,用于Mathf.Sin,Mathf.Cos的计算
        const float ANGLE_CONVERTER = Mathf.PI / 180;

        //相机上下的最大最小角度
        const float MAX_ANGLE_Y = 80;
        const float MIN_ANGLE_Y = 10;

        Transform m_trans;
        public Transform mineTransform
        {
            get
            {
                if (m_trans == null)
                {
                    m_trans = this.transform;
                }
                return m_trans;
            }
        }

        GameObject m_go;
        public GameObject mineGameObject
        {
            get
            {
                if (m_go == null)
                {
                    m_go = this.gameObject;
                }
                return m_go;
            }
        }

        void Start()
        {
            CalculateOffset();
        }

        void LateUpdate()
        {
            //相机坐标 = 人物坐标 + 偏移坐标
            mineTransform.position = m_target.position + m_offsetVector;
            mineTransform.LookAt(m_target);
        }

        void Update()
        {
            if (m_isRotateing)
            {
                CalculateOffset();
            }
        }

        //计算偏移,可以想象成在一个球面转,m_distance为半径,m_offsetAngleY决定了相机的高度y
        //高度确定后,就是在一个圆面上转,根据m_offsetAngleX计算出x与z
        void CalculateOffset()
        {
            m_offsetVector.y = m_distance * Mathf.Sin(m_offsetAngleY * ANGLE_CONVERTER);
            float newRadius = m_distance * Mathf.Cos(m_offsetAngleY * ANGLE_CONVERTER);
            m_offsetVector.x = newRadius * Mathf.Sin(m_offsetAngleX * ANGLE_CONVERTER);
            m_offsetVector.z = -newRadius * Mathf.Cos(m_offsetAngleX * ANGLE_CONVERTER);
        }

        //开始旋转,纪录当前偏移角度,用于复原
        public void StartRotate()
        {
            m_isRotateing = true;

            m_recordAngleX = m_offsetAngleX;
            m_recordAngleY = m_offsetAngleY;
        }

        //旋转,修改偏移角度的值,屏幕左右滑动即修改m_offsetAngleX,上下滑动修改m_offsetAngleY
        public void Rotate(float x, float y)
        {
            if (x != 0)
            {
                m_offsetAngleX += x;
            }
            if (y != 0)
            {
                m_offsetAngleY += y;
                m_offsetAngleY = m_offsetAngleY > MAX_ANGLE_Y ? MAX_ANGLE_Y : m_offsetAngleY;
                m_offsetAngleY = m_offsetAngleY < MIN_ANGLE_Y ? MIN_ANGLE_Y : m_offsetAngleY;
            }
        }

        //旋转结束,如需要复原镜头则,偏移角度还原并计算偏移坐标
        public void EndRotate(bool isNeedReset = false)
        {
            m_isRotateing = false;

            if (isNeedReset)
            {
                m_offsetAngleY = m_recordAngleY;
                m_offsetAngleX = m_recordAngleX;
                CalculateOffset();
            }
        }
    }
}

接着就是两个UI控制器UIController 和 SmallEyeController,实现IDragHandler, IBeginDragHandler, IEndDragHandler接口

using UnityEngine;
using UnityEngine.EventSystems;

namespace Test
{
    public class UIController : MonoBehaviour, IDragHandler, IBeginDragHandler, IEndDragHandler
    {
        [SerializeField] Transform m_target;
        [SerializeField] CameraTool.ThridPlayerCameraController m_thridCamera;

        //滑动的偏移转换为角度偏移的系数
        const float DRAG_TO_ANGLE = 0.5f;
        Vector2 m_previousPressPosition;
        float m_angleX, m_angleY;

        public void OnBeginDrag(PointerEventData eventData)
        {
            m_previousPressPosition = eventData.position;
            m_thridCamera.StartRotate();
        }

        public void OnDrag(PointerEventData eventData)
        {
            //滑动屏幕,人物和摄像机一起旋转(人物只转Y轴,即左右旋转)
            m_angleX = (eventData.position.x - m_previousPressPosition.x) * DRAG_TO_ANGLE;
            m_angleY = (eventData.position.y - m_previousPressPosition.y) * DRAG_TO_ANGLE;
            m_thridCamera.Rotate(-m_angleX, -m_angleY);
            m_target.Rotate(new Vector3(0, m_angleX, 0));
            m_previousPressPosition = eventData.position;
        }

        public void OnEndDrag(PointerEventData eventData)
        {
            m_thridCamera.EndRotate();
        }
    }
}
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;

namespace Test{
	public class SmallEyeController : MonoBehaviour, IDragHandler, IBeginDragHandler, IEndDragHandler
    {
        [SerializeField] CameraTool.ThridPlayerCameraController m_thridCamera;

        const float DRAG_TO_ANGLE = 0.5f;
        Vector2 m_previousPressPosition;
        float m_angleX, m_angleY;
        Image m_image;

        void Start()
        {
            m_image = GetComponent<Image>();
        }

        public void OnBeginDrag(PointerEventData eventData)
        {
            m_previousPressPosition = eventData.position;
            m_thridCamera.StartRotate();
            m_image.color = Color.gray;
        }

        public void OnDrag(PointerEventData eventData)
        {
            //滑动小眼睛按钮,只旋转摄像机
            m_angleX = (eventData.position.x - m_previousPressPosition.x) * DRAG_TO_ANGLE;
            m_angleY = (eventData.position.y - m_previousPressPosition.y) * DRAG_TO_ANGLE;
            m_thridCamera.Rotate(-m_angleX, -m_angleY);
            m_previousPressPosition = eventData.position;
        }

        public void OnEndDrag(PointerEventData eventData)
        {
            m_image.color = Color.white;
            m_thridCamera.EndRotate(true);
        }
	}
}

效果图

2018-04-28 23:02:15 ghl1390490928 阅读数 12281
  • Unity3D实战入门之第三人称射击游戏(TPS)

    这是一套第三人称射击游戏开发的入门基础课程。 本课程以一款小型的第三人称射击游戏为案例,手把手教你如何搭建一款3D射击游戏的雏形。麻雀虽小,五脏俱全,这款小游戏难度不大,但第三人称射击游戏该有的模块、功能它都有涉及。通过案例游戏的开发,学习者可以对第三人称射击游戏入门,对后续复杂大中型3D游戏的开发有实际的借鉴意义。 这套课程,对初次接触3D射击类游戏的学习者而言,是一块极好的跳板。

    1876 人正在学习 去看看 伍晓波

       在设计第一人称射击游戏以及RPG游戏时,往往需要在主角身上或者近邻位置设置一个摄像机,使其能够跟随主角的移动,提升游戏体验,这里介绍三种实现摄像机跟随的方法。

       (一)固定摄像机方法,常用于RPG游戏

第一种方法,在Unity的坐标系中,我将摄像机固定在主角头部上边靠后位置,这样,主角在移动过程中,摄像机也随着移动,最后在游戏场景中大概是这个样子:

        也就是说,摄像机相对于主角,总是在Vector3.forward方向上靠后一些,在Vector3.up方向上偏上一些。同时为了使摄像机的移动更加平滑,避免掉帧现象,引入差值函数Vector3.Lerp(),使摄像机的移动更加圆润。

         相关代码如下:

using UnityEngine;
using System.Collections;

/// <summary>
/// Third person camera.
/// </summary>
public class TheThirdPersonCamera : MonoBehaviour
{
	public float distanceAway=1.7f;			
	public float distanceUp=1.3f;			
	public float smooth=2f;				// how smooth the camera movement is
		
	private Vector3 m_TargetPosition;		// the position the camera is trying to be in)
	
	Transform follow;        //the position of Player
	
	void Start(){
		follow = GameObject.FindWithTag ("Player").transform;	
	}
	
	void LateUpdate ()
	{
		// setting the target position to be the correct offset from the 
		m_TargetPosition = follow.position + Vector3.up * distanceUp - follow.forward * distanceAway;
		
		// making a smooth transition between it's current position and the position it wants to be in
		transform.position = Vector3.Lerp(transform.position, m_TargetPosition, Time.deltaTime * smooth);
		
		// make sure the camera is looking the right way!
		transform.LookAt(follow);
	}
}

     (二)摄像机替代主角方法,常用于第一人称射击

       如图所示,直接用摄像机替代主角视角,摄像机看到的便是玩家在游戏场景中看到的。首先在Hierachy视图中建立空物体作为Player,使其旋转方向与摄像机保持一致。

        相关代码如下:

// 摄像机Transform
    Transform m_camTransform;

    // 摄像机旋转角度
    Vector3 m_camRot;

    // 摄像机高度(即表示主角的身高)
    float m_camHeight = 1.4f;
 void Start () {

        m_transform = this.transform;
        
         
        // 获取摄像机
        m_camTransform = Camera.main.transform;

        // 设置摄像机初始位置
        m_camTransform.position = m_transform.TransformPoint(0, m_camHeight, 0);    //摄像机初始位置从本地坐标转化成世界坐标,且在X-Z平面的初始位置

        // 设置摄像机的旋转方向与主角一致
        m_camTransform.rotation = m_transform.rotation;  //rotation为物体在世界坐标中的旋转角度,用Quaternion赋值
        m_camRot = m_camTransform.eulerAngles;    //在本游戏实例中用欧拉角表示旋转
        }
Quaternion赋值
        m_camRot = m_camTransform.eulerAngles;    //在本游戏实例中用欧拉角表示旋转
        }

 

void Control()
    {

        //获取鼠标移动距离
        float rh = Input.GetAxis("Mouse X");
        float rv = Input.GetAxis("Mouse Y");

        // 旋转摄像机
        m_camRot.x -= rv;
        m_camRot.y += rh;
        m_camTransform.eulerAngles = m_camRot;   //通过改变XYZ轴的旋转改变欧拉角

        // 使主角的面向方向与摄像机一致
        Vector3 camrot = m_camTransform.eulerAngles;
        camrot.x = 0; camrot.z = 0;
        m_transform.eulerAngles = camrot;
    }

      (三)类似于第一种方法

此方法在学习制作一款坦克大战时用到,原理其实和第一种方法类似,只不过用到了正余弦函数。

    相关代码如下:

 public float distance = 8;
    //横向角度
    public float rot = 0;    //用弧度表示
    //纵向角度
    private float roll = 30f * Mathf.PI * 2 / 360;    //弧度
    //目标物体
    private GameObject target;
 void Start()
    {
        //找到坦克
        target = GameObject.Find("Tank");
        
    }

 
void LateUpdate() 
    {
        //一些判断
        if (target == null)
            return;
        if (Camera.main == null)
            return;
        //目标的坐标
        Vector3 targetPos = target.transform.position;
        //用三角函数计算相机位置
        Vector3 cameraPos;
        float d = distance *Mathf.Cos (roll);
        float height = distance * Mathf.Sin(roll);
        cameraPos.x = targetPos.x +d * Mathf.Cos(rot);
        cameraPos.z = targetPos.z + d * Mathf.Sin(rot);
        cameraPos.y = targetPos.y + height;
        Camera.main.transform.position = cameraPos;
        //对准目标
        Camera.main.transform.LookAt(target.transform);
     }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2019-04-17 20:47:54 Root915 阅读数 152
  • Unity3D实战入门之第三人称射击游戏(TPS)

    这是一套第三人称射击游戏开发的入门基础课程。 本课程以一款小型的第三人称射击游戏为案例,手把手教你如何搭建一款3D射击游戏的雏形。麻雀虽小,五脏俱全,这款小游戏难度不大,但第三人称射击游戏该有的模块、功能它都有涉及。通过案例游戏的开发,学习者可以对第三人称射击游戏入门,对后续复杂大中型3D游戏的开发有实际的借鉴意义。 这套课程,对初次接触3D射击类游戏的学习者而言,是一块极好的跳板。

    1876 人正在学习 去看看 伍晓波

1.实现的最终效果

鼠标可以控制摄像机,使用WASD控制人物奔跑,可以跳跃,跑步时有灰尘特效,可以击打,奔跑的时候也可以进行打击(这里要学习状态机)

2.大概学习到的知识点

FBX Importing(如何导入资源)

Skeleton Assets(如何在不同的人物资源上使用同一个Skeleton)

Persona

Input Setup(Project setting)

Blend Spaces(idle->walk->run)

Animation Blueprints(Anim Graph/Event Graph)

State Machines

Character Blueprints(vs. Pawn BP)

3.新建项目

New Project ->Blueprint,这里我们选择Blank(A clean empty project with no code),选择With Starter Content,选择好自己的项目存储路径,项目的名字,Create Project

4.导入资源

打开网页http://wiki.unrealengine.com/File:ThirdPerson_FBX.zip,下载ThirdPerson_FBX.zip文件,解压。

为了方便管理我们在项目中,鼠标右键新建一个文件夹,命名为Character,首先导入SK_Mannequin.FBX,可以直接鼠标左键拖拽到项目中,或使用引擎中的Import按键进行导入。Unity也是拖拽的方式,比较方便。

这时虚幻会弹出一个FBX Import Options窗口(Unity不会弹出),虚幻会检测到你在导入模型文件,大多数的选项呢我们保持默认即可,这里呢,我们介绍一下Skeleton

如果你比较熟悉3D Max,Maya,或者其他3D动画工具,你应该是熟悉Skeleton Meshes的,使用Skeleton Meshes的模型呢可以share 动画,当然是有条件的,如果破坏了Hierarchy的顺序是不能使用的,如图

a)  b)  c)

a)的Animation b)是可以使用的,但是c)不可以,因为c)破坏了a)原有的骨骼层级

回到我们的弹出窗口,FBX Import Options,在Mesh中到,点击倒三角,展开,找到Normal Import Method,选择Import Normals,我这里默认的是Compute Normals(虚幻会创建硬边), Import

打开SK_Mannequin,虚幻中,模型编辑会弹出三个新的窗口,Skeleton,Mesh,Physics

在Character中我们新建一个新的文件夹,命名为Animations,把剩下的资源全部导入,弹出的窗口中,Skeleton选择刚刚我们创建出来的SK_Mannequin_Skeleton, Import All

文件左下角会有星号,代表文件没有保存,点击SaveAll,保存。

5.Persona简单介绍

Filters 可以快速筛选不同类型的文件

打开ThirdPersonIdle动画,Skeleton中可以看到所有的Bones,可在bone右键Add Socket,可以添加各种武器等人物想随身携带的东西。你可以在窗口中调节Socket的位置,WASD移动视角,F按键调整摄像机的位置

Animation,可以Reverse,Notifies中可以add Notify->PlayParticleEffect,其实跑步时的灰尘特效,就是在这里设置的

6.设置输入Input

Edit->Project Settings-Engine->Input,在这里设置输入,也可以在蓝图中,这个项目我们就在这设置了

点击Axis Mapping后的加号,展开,重命名为MoveForward,在它后面的加号,可以添加在不同平台上不同按键方式,设置完我们要使用的输入,参考下图

这些输入呢,都被保存在了DefaultInput.ini文件中,它会自动保存不用担心,你可以在项目文件夹\Config中找到它。

文章有点长了,先结束了,拜拜

这里说明一下,如果发现自己没跟上,或者丢了什么东西,请看上一篇文章,有时候我会更新,就不通知大家了。

2019-04-23 18:25:12 Root915 阅读数 102
  • Unity3D实战入门之第三人称射击游戏(TPS)

    这是一套第三人称射击游戏开发的入门基础课程。 本课程以一款小型的第三人称射击游戏为案例,手把手教你如何搭建一款3D射击游戏的雏形。麻雀虽小,五脏俱全,这款小游戏难度不大,但第三人称射击游戏该有的模块、功能它都有涉及。通过案例游戏的开发,学习者可以对第三人称射击游戏入门,对后续复杂大中型3D游戏的开发有实际的借鉴意义。 这套课程,对初次接触3D射击类游戏的学习者而言,是一块极好的跳板。

    1876 人正在学习 去看看 伍晓波

1.新建BlueprintAnimation

在Character中右键,Animation->Animation Blueprint,Parent Class选择AnimInstance,Target Skeleton选择SK_Mannequin_Skeleton,OK,重命名为CharacterAnimBP。在Anim Graph中可以建立动画状态机State Machine。

2.了解State Machine

我们需要的是:

3.编辑AnimationBlueprint

1)打开CharacterAnimBP,在Anim Graph中操作。

鼠标右键,Add New State Machine,命名为Locomotion,连接到Final Animation Pose,双击machine,进入,add state,命名为Idle_Walk_Run,双击进入,将Idle_Walk_Run拖入state中,连接到Final Animation Pose。通过调整参数的大小(speed)来播放不同的动画,这里我们在pin处右键,promote to variable,命名speed。记得点击Compile,才能看到你想要的动画效果哦,步骤如下图:

在Locomotion中,Add State,命名为JumpStart,双击进入,将ThirdPersonJump_Start拖入state中,连接到Final Animation Pose。返回到Locomotion中,双击双向箭头,transition rule,将Can Enter Transition,右键promote to variable,命名isInAir?。

Jump Start到Jump Loop的条件:

Jump Loop到Jump End的条件:

Jump End到Idle_Walk_Run的条件:

总的来说呢,在state中添加相应的动画,在transition rule中添加动画转换的条件,下面用图展示最终结果:

2)打开CharacterAnimBP,在Event Graph中操作。

4.新建Character蓝图

在Character中,鼠标右键,Blueprint Class,选择Character为Parent Class。命名为CharacterBP,打开将mesh赋值为我们的SK_Mannequin,调整好Mesh的位置和角度,在Details中的Animation中赋值我们的CharacterAnimBP。

修改CharacterMovement中的参数,建议:

Add Component,添加SpringArm,重命名为CameaRoot。在细节面板中,Camera Settings,勾选Use Pawn Control Rotation。添加Camera,作为CameaRoot的子物体,位置归零。

选择Class Defaults,在细节面板中,找到pawn,取消勾选Use Controller Rotation Yaw。

选择CharacterMovement,在细节面板中,Character Movement(Rotation Settings),勾选Orient Rotation to Movement。

2017-03-01 15:42:46 cbbbc 阅读数 1938
  • Unity3D实战入门之第三人称射击游戏(TPS)

    这是一套第三人称射击游戏开发的入门基础课程。 本课程以一款小型的第三人称射击游戏为案例,手把手教你如何搭建一款3D射击游戏的雏形。麻雀虽小,五脏俱全,这款小游戏难度不大,但第三人称射击游戏该有的模块、功能它都有涉及。通过案例游戏的开发,学习者可以对第三人称射击游戏入门,对后续复杂大中型3D游戏的开发有实际的借鉴意义。 这套课程,对初次接触3D射击类游戏的学习者而言,是一块极好的跳板。

    1876 人正在学习 去看看 伍晓波

解决什么问题?

动画分层可以用来解决什么样的问题呢?试想一下如果你要开发一款第三人称的射击游戏,那么肯定是希望身体的动画分为上下两部分,上方根据瞄准的位置和是否射击进行动画播放,下方根据移动播放动画。最好的例子就是Unity4.x自带的示例AngryBots了。

Avatar Mask

下面我们就使用Avatar Mask来实现人物在奔跑中招手的效果。

我们先添加一个人物到场景,同时为其添加一个Animator Controller并设定好跳转条件,如下:


接下来我们添加下面的脚本来控制动画的播放,对了为了不让角色因为播放动画而移动,记得把“Apply Root Motion”取消,方便我们查看动画播放的效果。

添加的脚本如下:

using UnityEngine;
using System.Collections;

public class MaskTest : MonoBehaviour
{
    private Animator _animator;

    void Start()
    {
        _animator = this.GetComponent<Animator>();
    }
    
    void Update()
    {
        if(Input.GetKeyDown(KeyCode.R))
        {
            _animator.SetBool("run", true);
        }
        if(Input.GetKeyUp(KeyCode.R))
        {
            _animator.SetBool("run", false);
        }

        AnimatorStateInfo state = _animator.GetCurrentAnimatorStateInfo(0);
        if(state.shortNameHash == Animator.StringToHash("Run") && Input.GetKeyDown(KeyCode.J))
        {
            _animator.SetTrigger("jump");
        }
    }
}

接下来我们希望按下空格时人物播放招手动画,但是跑动跳跃的动画不能停止播放。

我们需要添加一个新的Layer来管理招手动画的播放:


同时我们要配置好招手动画,这里的Idle可以不添加任何动画,仅仅表示空就可以了,同时还添加了一个Trigger“wave”表示进入招手动画的条件。

接下来我们设置一下该层,将其权重设置为1:


好了,我们修改一下代码试试看吧:

using UnityEngine;
using System.Collections;

public class MaskTest : MonoBehaviour
{
    private Animator _animator;

    void Start()
    {
        _animator = this.GetComponent<Animator>();
    }
    
    void Update()
    {
        if(Input.GetKeyDown(KeyCode.R))
        {
            _animator.SetBool("run", true);
        }
        if(Input.GetKeyUp(KeyCode.R))
        {
            _animator.SetBool("run", false);
        }

        AnimatorStateInfo state = _animator.GetCurrentAnimatorStateInfo(0);
        if(state.shortNameHash == Animator.StringToHash("Run") && Input.GetKeyDown(KeyCode.J))
        {
            _animator.SetTrigger("jump");
        }

        if(Input.GetKeyDown(KeyCode.Space))
        {
            _animator.SetTrigger("wave");
        }
    }
}


运行一下,发现当播放招手动画时其它动画都会停止,该怎么办呢?

答案是我们需要创建一个Avatar Mask来表示我们只希望播放动画的部分,即手部动画播放,其它部分不播放,在Project窗口右击创建一个Avatar Mask文件:


将右手部分以外的区别关闭即可:


这里说一个Transform这个选项,该选项是控制每个骨骼是否参与动画使用的,如果我们的人物有翅膀和尾巴之类的东西就要使用它了。

最后一步我们将这个Avatar Mask拖入Animator Controller的Wave层中即可:


再次运行下游戏,我们需要的效果是不是已经出来了呢。

设置解说

下面我们来详解一下Layer面板的设置:



Weight

动画层的权重,默认的Base Layer必须为1。如果设置为0则当前层的动画不会播放,1则会播放,0-1之间会采用类似融合的情况来播放动画,比如之前的招手的例子,如果设置为0.5则招手动画播放时手部只会抬到脖子附近。

Mask

动画遮罩,上方已经详解就不赘述。

Blending

动画混合方式:

  • Override:覆盖,表示当前层的动画会覆盖掉其它层的动画,比如招手播放时右手就不能播放其它的动画了;
  • Additive:添加,表示当前层的动画的量添加到其它层的动画,比如招手播放时,手部奔跑或站立的甩动也会保留;

Sync

开启了该功能后会多出一些选项,如下:


我们可以将该功能看做复制的功能。

Source Layer:指定当前层是哪个层的副本,设定后当前层的状态会和指定层完全一致或完全同步,但是我们可以修改某一个状态的动画。

该功能提供的效果就是两个状态一致的层可以做出一些不同的调整。

Timing:当前层和Souurce层同一个状态使用的动画时间长度不一致时,不勾选复制的层按Source层的时间播放(效果就是复制层动画可能会变快或变慢,Source层动画播放速度不变),勾选则Source层按复制层的时间播放(效果就是Source层动画可能会变快或变慢,复制层动画播放速度不变)。

IK Pass

表示启动IK动画,上一篇文章已经详解就不赘述。

天道酬勤,功不唐捐!

原文地址:http://www.cnblogs.com/hammerc/p/4832637.html

没有更多推荐了,返回首页