Created
July 6, 2022 11:30
-
-
Save Matthew-J-Spencer/7f2d3efa507656cde1b73071daf01d61 to your computer and use it in GitHub Desktop.
Control unity animations using code. Video: https://youtu.be/ZwLekxsSY3Y
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using UnityEngine; | |
using UnityEngine.Tilemaps; | |
using Random = UnityEngine.Random; | |
public class PlayerAnimator : MonoBehaviour { | |
[SerializeField] private float _minImpactForce = 20; | |
// Anim times can be gathered from the state itself, but | |
// for the simplicity of the video... | |
[SerializeField] private float _landAnimDuration = 0.1f; | |
[SerializeField] private float _attackAnimTime = 0.2f; | |
private IPlayerController _player; | |
private Animator _anim; | |
private SpriteRenderer _renderer; | |
private bool _grounded; | |
private float _lockedTill; | |
private bool _jumpTriggered; | |
private bool _attacked; | |
private bool _landed; | |
private void Awake() { | |
if (!TryGetComponent(out IPlayerController player)) { | |
Destroy(this); | |
return; | |
} | |
_player = player; | |
_anim = GetComponent<Animator>(); | |
_renderer = GetComponent<SpriteRenderer>(); | |
} | |
private void Start() { | |
_player.Jumped += () => { | |
_jumpTriggered = true; | |
}; | |
_player.Attacked += () => { | |
_attacked = true; | |
}; | |
_player.GroundedChanged += (grounded, impactForce) => { | |
_grounded = grounded; | |
_landed = impactForce >= _minImpactForce; | |
}; | |
} | |
private void Update() { | |
if (_player.Input.x != 0) _renderer.flipX = _player.Input.x < 0; | |
var state = GetState(); | |
_jumpTriggered = false; | |
_landed = false; | |
_attacked = false; | |
if (state == _currentState) return; | |
_anim.CrossFade(state, 0, 0); | |
_currentState = state; | |
} | |
private int GetState() { | |
if (Time.time < _lockedTill) return _currentState; | |
// Priorities | |
if (_attacked) return LockState(Attack, _attackAnimTime); | |
if (_player.Crouching) return Crouch; | |
if (_landed) return LockState(Land, _landAnimDuration); | |
if (_jumpTriggered) return Jump; | |
if (_grounded) return _player.Input.x == 0 ? Idle : Walk; | |
return _player.Speed.y > 0 ? Jump : Fall; | |
int LockState(int s, float t) { | |
_lockedTill = Time.time + t; | |
return s; | |
} | |
} | |
#region Cached Properties | |
private int _currentState; | |
private static readonly int Idle = Animator.StringToHash("Idle"); | |
private static readonly int Walk = Animator.StringToHash("Walk"); | |
private static readonly int Jump = Animator.StringToHash("Jump"); | |
private static readonly int Fall = Animator.StringToHash("Fall"); | |
private static readonly int Land = Animator.StringToHash("Land"); | |
private static readonly int Attack = Animator.StringToHash("Attack"); | |
private static readonly int Crouch = Animator.StringToHash("Crouch"); | |
#endregion | |
} | |
public interface IPlayerController { | |
public Vector2 Input { get; } | |
public Vector2 Speed { get; } | |
public bool Crouching { get; } | |
public event Action<bool, float> GroundedChanged; // Grounded - Impact force | |
public event Action Jumped; | |
public event Action Attacked; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@stewartteles: Probably a Vector2 isn't returned from your IPlayerController. IPlayerController should be a MonoBehaviour that implements IPlayerController and the Input get returns a value from the Unity Input System. See tarodev's IPlayerController implementation in the IPlayerController video.
@takahebi: If you have a sword swing attack, then you want it to play out completely and not just while the attack button is held. So you lock the character into the attack state until the anim completes. LockState is a bit weird because it's a nested function, but you could move it out of GetState and it would be the same.