Skip to main content

On Sale: GamesAssetsToolsTabletopComics
Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
TagsGame Engines

Making mechanics of Cult of the Lamb in Unity: Animations

This is the third tutorial of the making of the Cult of the Lamb mechanics in Unity. If you want to follow along the series you need to first read Ground Zero, Part 1 and Part 2.

First we need to change some things we have done to fix the problem with the Roll state. The problem right now is that if we press the roll button while Idle and move after some seconds, instead of moving normally, it will perform the Roll. This is not what we want. To fix this first we go to the PlayerInput class and add a new Event. We can call it RollCancelledEvent:

namespace Player.Input
{
    public class PlayerInput : MonoBehaviour, GameControls.IPlayerActions
    {
        ...
        public event UnityAction RollCancelledEvent = delegate { };

        public void OnRoll(InputAction.CallbackContext context)
        {
            if (context.performed)
            {
                RollEvent?.Invoke();
            } 
            if (context.canceled)
            {
                RollCancelledEvent?.Invoke();
            }
        }
    }
}

After that we need to go to the PlayerStateMachine class:

namespace Player
{
    public class PlayerStateMachine : StateMachine<PlayerStateMachine>
    {
        ...
        
        public bool RollPressed => _rollPressed;
        private bool _rollPressed;        
        
        private void OnEnable()
        {
            ...
            _playerInput.RollCancelledEvent += HandleCancelledRoll;
        }

        private void OnDisable()
        {
            ...
            _playerInput.RollCancelledEvent -= HandleCancelledRoll;
        }

        private void HandleCancelledRoll()
        {
            _rollPressed = false;
        }
        ...
    }
}

On the PlayerRollState we can remove line 24:

// instantly set this to false so there's no double rolling
parent.RollPressed = false;

No we are able to continue.

Adding Animations

For this tutorial I draw a quick sketch of my player and imported the .psb file to Unity. If you want, you can find the sprites and the animations in this .zip file. Of course you can use whatever sprites and animations you want. Make sure that you imported the 2D packages from the Package Manager.

After importing them to the Editor, drag the characters_spritesheet prefab in the scene, unpack the prefab and replace your player’s Sprite child with this Player gameObject. Also, add an Animator component and assign a Animator Controller.

Player Sprite with Animator Controller

On the Animator itself we don’t need to do much, just drag the the animations to it.

Animator view

We don’t need transitions since we are animating as a programmer.

Let’s write the scripts

We know that Unity is based on scripts. Each script should (hopefully) be in charge of one thing. We are going to take a slightly different approach here. First we create a new pure C# class that does not inherit from MonoBehaviour. Let’s call it PlayerAnimations. This class will be in charge of everything related to animations.

using UnityEngine;

namespace Player
{
    public class PlayerAnimations
    {
        private Animator _animator;

	// this needs to match the names on the Animator view
        private int PLAYER_IDLE = Animator.StringToHash("Idle");
        private int PLAYER_MOVE = Animator.StringToHash("Move");
        private int PLAYER_ROLL = Animator.StringToHash("Roll");

        private float _transitionDuration = .1f;

        public PlayerAnimations(Animator animator)
        {
            _animator = animator;
        }

        public void PlayIdle()
        {
            _animator.CrossFade(PLAYER_IDLE, _transitionDuration);
        }

        public void PlayMove()
        {
            _animator.CrossFade(PLAYER_MOVE, _transitionDuration);
        }

        public void PlayRoll()
        {
            _animator.CrossFade(PLAYER_ROLL, _transitionDuration);
        }
    }
}

Now we need to add a reference to this new class to the PlayerStateMachine script and initialize it:

namespace Player
{
    public class PlayerStateMachine : StateMachine<PlayerStateMachine>
    {
        ...
        [SerializeField] private Animator _animator;        
        public PlayerAnimations Animations { get; private set; }        

        protected override void Awake()
        {
            base.Awake();
            Animations = new PlayerAnimations(_animator);
        }        
    }
}

Doing this will allow us to call the animator from our states like this:

namespace Player.States
{    
    public class PlayerMove3DState : State<PlayerStateMachine>
    {       
	...
        public override void Enter(PlayerStateMachine parent)
        {
            base.Enter(parent);
            parent.Animations.PlayMove();
        }
        ...
    }
}
namespace Player.States
{
    public class PlayerRollState : State<PlayerStateMachine>
    {        
        public override void Enter(PlayerStateMachine parent)
        {
            base.Enter(parent);
            parent.Animations.PlayRoll();
            ...
        }
	...        
    }
}
namespace Player.States
{
    public class PlayerIdleState : State<PlayerStateMachine>
    {
        public override void Enter(PlayerStateMachine parent)
        {
            base.Enter(parent);
            ...
            parent.Animations.PlayIdle();
        }
        ...
    }
}

Remember to assign the animator reference on the PlayerStateMachine component in the Player’s inspector view.

Press play and now you will see the animations happen!

Final product

See you in the next one!

Support this post

Did you like this post? Tell us

Leave a comment

Log in with your itch.io account to leave a comment.