How can I get 2 different reload animations if manual reload and auto reload both have the same StateIndex?

Lawrence Tran

New member
I am animating a pistol. I have 2 different animations for the reload. 1 with the slide closed, signaling to the player that there is ammunition left in the chamber. 1 with the slide open, signaling to the player that there is no more ammunition in the pistol. I want the manual reload to play the former animation and the auto reload to play the latter.

However, in the animator controller both manual reload and auto reload change the StateIndex to 3 and the SubstateIndex to 0; therefore, both manual reload and auto reload result in the former animation playing. I tried transitioning the latter animation from the fire animation with StateIndex equals 3, but this doesn't work. The former animation plays regardless.

How can I set it up so that the auto reload functionality in the ShootableWeapon component sets the StateIndex or SubstateIndex to another value, so I can play the latter animation when the pistol is empty? Or, is there a better way to get the same effect?
 
I'd like to see the response to this as well, I was going to post this as a feature request today actually!

To expand upon this for other readers: Shoot, Idle, and Reload all need to have a substate for if the weapon is empty, because on rifles/pistols the slide should lock back at the end of the fire animation, a second idle animation with it locked back should play, and then when reloaded a different reload animation where the bolt/slide is cycled or the slide is released. This is also the case with bolt action rifles - a reload with ammo remaining does not need to cycle the bolt only swap the mag, but a reload with no ammo requires opening the bolt, putting the mag in, then chamber the round (or swap mag, then cycle bolt). Shotguns require different animations for these cases as well.

I was going to do this manually with code but I'd like to see if there's a way to do it already.
 
You could create a new selector for the Reload Animator Audio State set that returns a different index based on the reload type. This page has info on the selector API:

 
I get this weird inconsistent off-by-1 error. This only happens maybe 20% of the time on the first manual reload. The auto reload animation (with the slide open) plays. Perhaps the timing when I check the input is off on the first manual reload? It seems that the animator controller quickly changes the SubstateIndex on the first reload very quickly between the two available choices and hence the wrong animation plays. Here is my code and screenshots. Is there a better way than checking the input? How can I check the remaining bullets in the item from this API?


Code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Opsive.UltimateCharacterController.Items.AnimatorAudioStates;

public class DetermineReload : AnimatorAudioStateSelector
{

    private int m_CurrentIndex = 0;

    public override void StartStopStateSelection(bool start)
    {
        base.StartStopStateSelection(start);

        // Check if manual reload
        if (Input.GetKeyDown("r")) {
            m_CurrentIndex = 0;
        } else {
            // Is auto reload
            m_CurrentIndex = 1;
        }
    }

    public override int GetStateIndex()
    {
        return m_CurrentIndex;
    }
}

*edit: Code formatting
 

Attachments

  • Screen Shot 2020-05-07 at 11.40.38 AM.png
    Screen Shot 2020-05-07 at 11.40.38 AM.png
    23.1 KB · Views: 10
  • Screen Shot 2020-05-07 at 11.40.43 AM.png
    Screen Shot 2020-05-07 at 11.40.43 AM.png
    22.9 KB · Views: 11
  • Screen Shot 2020-05-07 at 11.40.54 AM.png
    Screen Shot 2020-05-07 at 11.40.54 AM.png
    45.6 KB · Views: 11
Last edited:
I was able to figure out how to get the GetConsumableItemIdentifierAmount to figure out the remaining ammo count. Here is my code for the custom AnimatorAudioStateSelector.

Code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Opsive.UltimateCharacterController.Items.AnimatorAudioStates;
using Opsive.UltimateCharacterController.Items.Actions;

public class DetermineReload : AnimatorAudioStateSelector
{
    private int m_CurrentIndex = 0;

    public override void StartStopStateSelection(bool start)
    {
        base.StartStopStateSelection(start);

        int remainingAmmo = m_Item.gameObject.GetComponent<ShootableWeapon>().GetConsumableItemIdentifierAmount();

        // Check if ammo is left
        if (remainingAmmo != 0)
        {
            m_CurrentIndex = 0;
        }
        else
        {
            // Is empty clip
            m_CurrentIndex = 1;
        }
    }

    public override int GetStateIndex()
    {
        return m_CurrentIndex;
    }
}

My question now is if this is the best practice when creating my own custom AnimatorAudioStateSelector? I am confused if I should also be overriding NextState() or IsStateValid?
 
It looks like you are calling GetComponent when you should be caching the result. NextState will mostly be called for combos with melee weapons.
 
Here is my final code for anyone.

Code:
namespace Opsive.UltimateCharacterController.Items.AnimatorAudioStates
{
    using Opsive.UltimateCharacterController.Character;
    using Opsive.UltimateCharacterController.Items.Actions;
    using UnityEngine;

    public class DetermineReload : AnimatorAudioStateSelector
    {
        private int m_CurrentIndex = 0;
        private ShootableWeapon shootableWeapon;

        public override void Initialize(GameObject gameObject, UltimateCharacterLocomotion characterLocomotion, Item item, AnimatorAudioStateSet.AnimatorAudioState[] states)
        {
            base.Initialize(gameObject, characterLocomotion, item, states);
            shootableWeapon = m_Item.gameObject.GetComponent<ShootableWeapon>();
        }

        public override void StartStopStateSelection(bool start)
        {
            base.StartStopStateSelection(start);

            int remainingAmmo = shootableWeapon.GetConsumableItemIdentifierAmount();

            // Check if ammo is left
            if (remainingAmmo != 0)
            {
                m_CurrentIndex = 0;
            }
            else
            {
                // Is empty clip
                m_CurrentIndex = 1;
            }
        }

        public override int GetStateIndex()
        {
            return m_CurrentIndex;
        }
    }
}
 
Top