Unity 2D Animation 动画案例
Unity 2D Animation 案例
Animator Controller
在project面板中 右键 > create > Animator Controller 创建动画控制器并将动画控制器绑定到Player上。
Animation制作
- Windows > Animation > Animation 打开动画编辑器,选中Player后点击Create创建.anim
- 将Sprites添加到帧动画中。可以依次添加crouch,idle,jump,run动画
注:原素材的Pixels Per Unit的值为100,可以修改为16
动画控制脚本
run状态
-
建立player_idle与player_run的联系
在Parameters面板添加float变量
单击连线在inspector面板的
①去除动画退出时间
②Conditions处添加条件变量
player_idle → player_run
player_run → player_idle
-
变量控制(脚本)
using UnityEngine; public class PlayerMoveController : MonoBehaviour { private CharacterController2D cc; private Animator animator; [SerializeField] private float runSpeed = 40; private float move = 0f; private bool jump = false; private bool crouch = false; private void Awake() { cc = GetComponent<CharacterController2D>(); animator = GetComponent<Animator>(); } // Update is called once per frame void Update() { move = Input.GetAxisRaw("Horizontal"); //取move的绝对值(即只存在1与0两个值),再结合Conditions判断即可切换idle与run之间的状态 animator.SetFloat("move", Mathf.Abs(move)); if (Input.GetButtonDown("Jump")) { jump = true; } if (Input.GetButtonDown("Crouch")) { crouch = true; } else if (Input.GetButtonUp("Crouch")) { crouch = false; } } private void FixedUpdate() { cc.Move(move * runSpeed * Time.fixedDeltaTime, crouch, jump); jump = false; } }
其他状态
其他状态与run状态相似,这里仅给出思路,供读者参考。
-
jump (判断条件)
Any State → jump (isJumping = true)
jump → idle (isJumping = false; move < 1)
jump → run(isJumping = false; move > 0)
-
crouch (判断条件)
crouch→ idle (isCrouching = true)
crouch→ run(isCrouching = true)
idle → crouch(isCrouching = false)
run→ crouch(isCrouching = false)
完整代码如下:
CharacterController2D.cs
using UnityEngine; using UnityEngine.Events; public class CharacterController2D : MonoBehaviour { [SerializeField] private float m_JumpForce = 400f; // Amount of force added when the player jumps. [Range(0, 1)] [SerializeField] private float m_CrouchSpeed = .36f; // Amount of maxSpeed applied to crouching movement. 1 = 100% [Range(0, .3f)] [SerializeField] private float m_MovementSmoothing = .05f; // How much to smooth out the movement [SerializeField] private bool m_AirControl = false; // Whether or not a player can steer while jumping; [SerializeField] private LayerMask m_WhatIsGround; // A mask determining what is ground to the character [SerializeField] private Transform m_GroundCheck; // A position marking where to check if the player is grounded. [SerializeField] private Transform m_CeilingCheck; // A position marking where to check for ceilings [SerializeField] private Collider2D m_CrouchDisableCollider; // A collider that will be disabled when crouching const float k_GroundedRadius = .2f; // Radius of the overlap circle to determine if grounded private bool m_Grounded; // Whether or not the player is grounded. const float k_CeilingRadius = .2f; // Radius of the overlap circle to determine if the player can stand up private Rigidbody2D m_Rigidbody2D; private bool m_FacingRight = true; // For determining which way the player is currently facing. private Vector3 velocity = Vector3.zero; public bool m_wasCrouching = false; private PlayerMoveController mPlayerMoveController; private void Awake() { m_Rigidbody2D = GetComponent<Rigidbody2D>(); mPlayerMoveController = GetComponent<PlayerMoveController>(); } private void FixedUpdate() { bool wasGrounded = m_Grounded; m_Grounded = false; //ground检测 Collider2D[] colliders = Physics2D.OverlapCircleAll(m_GroundCheck.position, k_GroundedRadius, m_WhatIsGround); for (int i = 0; i < colliders.Length; i++) { if (colliders[i].gameObject != gameObject) { m_Grounded = true; if (!wasGrounded) //OnLandEvent.Invoke(); mPlayerMoveController.OnLanding(); } } } public void Move(float move, bool crouch, bool jump) { //对Crouch行为的检测,如果Crouch键未触发却处于必须Crouch的环境中,那么需要保持Crouch=true; if (!crouch) { if (Physics2D.OverlapCircle(m_CeilingCheck.position, k_CeilingRadius, m_WhatIsGround)) { crouch = true; Debug.Log(m_wasCrouching); } } if (m_Grounded || m_AirControl) { if (crouch) { if (!m_wasCrouching) { m_wasCrouching = true; mPlayerMoveController.OnCrouching(true); } // 减速 move *= m_CrouchSpeed; if (m_CrouchDisableCollider != null) m_CrouchDisableCollider.enabled = false; } else { if (m_CrouchDisableCollider != null) m_CrouchDisableCollider.enabled = true; if (m_wasCrouching) { m_wasCrouching = false; mPlayerMoveController.OnCrouching(false); } } // Move the character by finding the target velocity Vector3 targetVelocity = new Vector2(move * 10f, m_Rigidbody2D.velocity.y); m_Rigidbody2D.velocity = Vector3.SmoothDamp(m_Rigidbody2D.velocity, targetVelocity, ref velocity, m_MovementSmoothing); // 转向 if (move > 0 && !m_FacingRight) { Flip(); } else if (move < 0 && m_FacingRight) { Flip(); } } //跳跃 if (m_Grounded && jump) { m_Grounded = false; m_Rigidbody2D.AddForce(new Vector2(0f, m_JumpForce)); } } private void Flip() { m_FacingRight = !m_FacingRight; Vector3 theScale = transform.localScale; theScale.x *= -1; transform.localScale = theScale; } private void OnDrawGizmos() { Gizmos.DrawWireSphere(m_GroundCheck.position, k_GroundedRadius); } }
PlayerMoveController.cs
using UnityEngine; public class PlayerMoveController : MonoBehaviour { private CharacterController2D cc; private Animator animator; [SerializeField] private float runSpeed = 40; private float move = 0f; private bool jump = false; private bool crouch = false; private Rigidbody2D rb2; private void Awake() { cc = GetComponent<CharacterController2D>(); animator = GetComponent<Animator>(); rb2 = GetComponent<Rigidbody2D>(); } // Update is called once per frame void Update() { move = Input.GetAxisRaw("Horizontal"); animator.SetFloat("move", Mathf.Abs(move)); if (Input.GetButtonDown("Jump")) { jump = true; animator.SetBool("isJump", true); } if (Input.GetButtonDown("Crouch")) { crouch = true; } else if (Input.GetButtonUp("Crouch")) { crouch = false; } } private void FixedUpdate() { cc.Move(move * runSpeed * Time.fixedDeltaTime, crouch, jump); jump = false; } public void OnLanding() { animator.SetBool("isJump", false); } public void OnCrouching(bool wasCrouching) { Debug.Log(wasCrouching); animator.SetBool("isCrouching", wasCrouching); } }
演示
补充
部分问题
有可能你的人物会遇到卡在碰撞盒边缘的问题
可能是因为Tile Collider的多个碰撞盒拼接,如果锁住人物z轴,由于unity的浮点计算问题,导致collider之间并不是绝对水平的。
解决办法:为Tile map添加Composite Collider 2D同时将Rigidbody.BodyType设为Static,
TileCollider2D.UsedByComposite设为true
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 上周热点回顾(2.17-2.23)