Unity实现动画切换的原理: Entry所指的橘黄色状态为默认状态。记住, Entry在Animator组件被激活后 无条件 跳转到默认状态,并且每个Layer有且仅有一个默认状态
在Animator中,我们可以给状态添加指定参数,当参数满足条件时便可实现动画的切换。 代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public Rigidbody2D rb;
public float speed;
public float jumpforce;
public Animator anim;
public Collider2D coll;
public LayerMask ground;
void Start()
{
}
private void Update()
{
if (Input.GetButtonDown("Jump"))
{
float t = 1 / Time.deltaTime;
rb.velocity = new Vector2(rb.velocity.x, jumpforce*Time.deltaTime*t);
anim.SetBool("jumping", true);
}
SwitchAnim();
}
void FixedUpdate()
{
Movement();
}
void Movement()
{
float horizontalMove= Input.GetAxis("Horizontal"); ;
float faceDirection = Input.GetAxisRaw("Horizontal");
if (horizontalMove != 0)
{
rb.velocity = new Vector2(horizontalMove * speed, rb.velocity.y);
anim.SetFloat("running",Mathf.Abs(horizontalMove));
Debug.Log(horizontalMove);
}
if (faceDirection != 0)
{
transform.localScale = new Vector3(faceDirection*0.693f, 0.6163f, 1);
}
}
void SwitchAnim()
{
anim.SetBool("idleing", false);
if (anim.GetBool("jumping"))
{
if (rb.velocity.y < 0)
{
anim.SetBool("jumping", false);
anim.SetBool("falling", true);
}
}else if (coll.IsTouchingLayers(ground))
{
anim.SetBool("falling", false);
anim.SetBool("idleing", true);
}
}
}
解释:
anim.SetBool(string name,bool value)
第一个参数为参数名,第二个为参数值
anim.GetBool(string name)
获取参数值
isTouchingLayer(LayeraMask LayerName)
检查附加到该刚体的任何碰撞体是否正在接触指定 LayerMask 上的任何碰撞体。(例如给地面自定义层,给人物添加LayerMask属性,和Box Colider 2D组件和属性,便可以将地面的层添加进角色属性LayerMask,从而使用isTouchingLayer判断是否接触了地面)
实现逻辑:
- walk和idle都可以切换为jump动画,参数条件为jumping:true
- jump切换为fall,参数条件为jumping:false;falling:true
- fall切换为idle,参数条件为idleing:true;falling:false
(其实上述参数并不需要那么多,只不过可以更好地理解) - 当我们按下空格应当进入跳跃动画,然后执行动画切换函数。
if (Input.GetButtonDown("Jump"))
{
float t = 1 / Time.deltaTime;
rb.velocity = new Vector2(rb.velocity.x, jumpforce*Time.deltaTime*t);
anim.SetBool("jumping", true);
SwitchAnim();
}
void SwitchAnim()
{
anim.SetBool("idleing", false);
if (anim.GetBool("jumping"))
{
if (rb.velocity.y < 0)
{
anim.SetBool("jumping", false);
anim.SetBool("falling", true);
}
}else if (coll.IsTouchingLayers(ground))
{
anim.SetBool("falling", false);
anim.SetBool("idleing", true);
}
}
- 跳起后,先检测是否为jumping参数是否为true(因为动画变换函数一定是在跳起之后)
- 上升和下降动画的分界点是垂直方向速度<0,即像下落。然后设置下降动画满足的参数条件
- 如果不是跳跃状态并且接触了地面则满足变为idle动画的条件
- 但是要注意吧把dle参数及时复原,所以变换函数的开头加入复原语句
解释: 为什么不可以吧SwitchAnim函数放入判断跳跃的if条件句中?
这样的话只有在跳跃键按下的一瞬间才执行,起跳后动画变换函数根本不执行
为什么不把判断是否接触地面的的判断语句继续嵌套在垂直速度小于0的判断语句中?
显然不行,速度小于零后,还有一段距离才到地面,这样if立马判断为假,所以不执行,而落地后速度又为零也不执行,并且jumping参数也不再满足(这样就一次空格只执行一次)
为什么要复原idleing?
良好的习惯,状态结束一般要复原
总结: 像是这样分阶段的过程最好分阶段写函数:
- 先是跳跃,然后动画变换
- 先跳跃下降,在落地
如果强行嵌套也可以这样: 代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public Rigidbody2D rb;
public float speed;
public float jumpforce;
public Animator anim;
public Collider2D coll;
public LayerMask ground;
void Start()
{
}
private void Update()
{
if (Input.GetButtonDown("Jump"))
{
float t = 1 / Time.deltaTime;
rb.velocity = new Vector2(rb.velocity.x, jumpforce*Time.deltaTime*t);
anim.SetBool("jumping", true);
}
SwitchAnim();
}
void FixedUpdate()
{
Movement();
}
void Movement()
{
float horizontalMove= Input.GetAxis("Horizontal"); ;
float faceDirection = Input.GetAxisRaw("Horizontal");
if (horizontalMove != 0)
{
rb.velocity = new Vector2(horizontalMove * speed, rb.velocity.y);
anim.SetFloat("running",Mathf.Abs(horizontalMove));
Debug.Log(horizontalMove);
}
if (faceDirection != 0)
{
transform.localScale = new Vector3(faceDirection*0.693f, 0.6163f, 1);
}
}
void SwitchAnim()
{
anim.SetBool("idleing", false);
if (rb.velocity.y <= 0)
{
anim.SetBool("jumping", false);
anim.SetBool("falling", true);
if (coll.IsTouchingLayers(ground))
{
anim.SetBool("falling", false);
anim.SetBool("idleing", true);
}
}
}
}
|