How To Switch Animations For Different Weapons Unity
Difficulty: Intermediate - Recommended after Interrupt Management and Brain Transplants
Location: Assets/Plugins/Animancer/Examples/06 State Machines/06 Weapons
Namespace:
Animancer.Examples.StateMachines.Weapons
This example demonstrates how you can implement a graphic symbol that can move around and attack with various dissimilar weapons. It is based on a character and animations downloaded from Mixamo which unfortunately cannot exist legally redistributed in their raw source course, meaning that they cannot be directly included in Animancer. So instead of providing a fix-made scene, this instance explains how to download the required assets and set them up from scratch in Unity using the Brain Transplants example equally a base.
Required Assets
Character
The first thing you demand to decide is which character model yous want to apply:
- You can download whatsoever of the Mixamo Characters.
- We are using the grapheme called Paladin J Nordstrom, but you can choose any you like.
- Once you take downloaded the character you will demand to configure its Import Settings as explained hither, including exposing the Right Hand bone so that we can attach weapons to it.
- The DefaultHumanoid model included in Animancer at Assets/Plugins/Animancer/Examples/Art/Default Humanoid would work.
- Or you could even use a
Sprite
based character if you have all the animations listed beneath.
Animations
One time you have a graphic symbol, you will need to get the post-obit animations for them:
Animations | Import Settings |
---|---|
Idle, Walk, and Run. If you are using a The Import Settings of these animations should have Loop Pose if yous want to use Root Motion for the grapheme's master movements. | |
Several Attack animations for each weapon y'all want to implement. Nosotros are using 3 weapons with the post-obit Mixamo Animations:
Root Transform Rotation and Root Transform Position (Y) to Bake Into Pose based on the Original values. | |
Optional Sheathe and Unsheathe animations for each weapon. We are using the post-obit Mixamo Animations:
| Aforementioned as above. |
Weapons
Yous will also need a model for each weapon except Unarmed. The Getting Animations page lists several places where you tin get models or programs to create your own, but for this example we are simply using primitive shapes (cubes, cylinders, and capsules). Most of the construction should exist obvious at a glance except for the spiked parts which are created by skewing cubes:
Hierarchy | Transform |
---|---|
By rotating the kid cube 45 degrees on one axis then scaling the parent object on a dissimilar axis, the cube gets stretched diagonally then that it turns into a spike:
Set on Input
The AttackInput
script would normally be role of the CharacterBrain
, but we have implemented it separately so that we tin can reuse the classes from the Brain Transplants case and have it work with both the KeyboardBrain
and MouseBrain
:
using Animancer; using Animancer.Examples.StateMachines.Brains; using Animancer.FSM; using Animancer.Units; using UnityEngine; public sealed class AttackInput : MonoBehaviour { [SerializeField] private CharacterState _Attack; [SerializeField, Seconds] private float _AttackInputTimeOut = 0.5f; individual StateMachine<CharacterState>.InputBuffer _InputBuffer; private void Awake() { _InputBuffer = new StateMachine<CharacterState>.InputBuffer(_Attack.Character.StateMachine); } private void Update() { if (Input.GetButtonDown("Fire2"))// Right Click by default. { _InputBuffer.Buffer(_Attack, _AttackInputTimeOut); } _InputBuffer.Update(); } }
Information technology starts with a reference to the Attack State which is assigned in the Inspector (once we create it below):
[SerializeField] private CharacterState _Attack;
We are also using a concept known as input buffering which only ways that if a button press fails to trigger the desired action, it will still be able to do and then for a brusque time later. Animancer's Finite State Machine system has an InputBuffer
course which is very easy to use. Then we brand a "time out" field to determine how long the buffer will last and initialize it with the StateMachine
it will be buffering (in seconds):
[SerializeField, Seconds] individual float _AttackInputTimeOut = 0.5f; private StateMachine<CharacterState>.InputBuffer _InputBuffer; private void Awake() { _InputBuffer = new StateMachine<CharacterState>.InputBuffer(_Attack.Character.StateMachine); }
Then when we discover the Attack push button beingness pressed and would ordinarily call ...StateMachine.TrySetState(_Attack)
we instead call Buffer
on the InputBuffer
where we also specify the time out elapsing:
individual void Update() { if (Input.GetButtonDown("Fire2"))// Right Click by default. { _InputBuffer.Buffer(_Attack, _AttackInputTimeOut); }
Then at the finish of the Update
method nosotros call Update
on the buffer to brand it try to enter the buffered action and bank check if information technology has timed out:
_InputBuffer.Update(); }
You could give each attack a different time out period, but using the same 0.five second window for every attack tends to give more than consistent gameplay where the actor tin can go a feel for when they will need to printing the button.
Weapon
The Weapon
script is simply a component which holds the animations relating to a weapon so that it tin can be attached to the weapon'due south model in the scene:
using Animancer; using UnityEngine; public sealed course Weapon : MonoBehaviour { [SerializeField] private ClipTransition[] _AttackAnimations; public ClipTransition[] AttackAnimations => _AttackAnimations; [SerializeField] private ClipTransition _EquipAnimation; public ClipTransition EquipAnimation => _EquipAnimation; [SerializeField] private ClipTransition _UnequipAnimation; public ClipTransition UnequipAnimation => _UnequipAnimation; }
In a existent game, this class might have other details like impairment, damage type, weapon category, etc. It could also inherit from a base Item class for things like weight, price, and description.
Equip State
The EquipState
script manages the currently equipped Weapon
. It is a is a CharacterState
and then that other actions cannot be performed while it is playing the EquipAnimation
and UnequipAnimation
of the Weapon
when it is changed:
using Animancer; using Animancer.Examples.StateMachines.Brains; using Animancer.FSM; using UnityEngine; public sealed class EquipState : CharacterState { [SerializeField] individual Transform _WeaponHolder; [SerializeField] individual Weapon _Weapon; private Weapon _EquippingWeapon; private Action _OnUnequipEnd; public Weapon Weapon { get => _Weapon; set up { if (enabled) render; _EquippingWeapon = value; if (!Grapheme.StateMachine.TrySetState(this)) _EquippingWeapon = _Weapon; } } private void Awake() { _EquippingWeapon = _Weapon; _OnUnequipEnd = OnUnequipEnd; AttachWeapon(); } public override bool CanEnterState => _Weapon != _EquippingWeapon; private void OnEnable() { if (_Weapon.UnequipAnimation.IsValid) { var country = Character.Animancer.Play(_Weapon.UnequipAnimation); state.Events.OnEnd = _OnUnequipEnd; } else { OnUnequipEnd(); } } individual void OnUnequipEnd() { DetachWeapon(); _Weapon = _EquippingWeapon; AttachWeapon(); if (_Weapon.EquipAnimation.IsValid) { var state = Character.Animancer.Play(_Weapon.EquipAnimation); country.Events.OnEnd = Character.ForceEnterIdleState; } else { Graphic symbol.StateMachine.ForceSetState(Character.Idle); } } private void AttachWeapon() { if (_Weapon == null) return; if (_WeaponHolder != nil) { var transform = _Weapon.transform; transform.parent = _WeaponHolder; transform.localPosition = default; transform.localRotation = Quaternion.identity; transform.localScale = Vector3.one; } _Weapon.gameObject.SetActive(true); } private void DetachWeapon() { if (_Weapon == nil) return; _Weapon.transform.parent = transform; _Weapon.gameObject.SetActive(faux); } individual void FixedUpdate() { Character.Rigidbody.velocity = default; } public override bool CanExitState => false; }
The RightHand bone of a character is commonly in their wrist, so instead of needing to manually get-go the equipped weapon from that position we take simply created an empty Weapon Holder object every bit a kid of the hand which we can attach weapons to:
[SerializeField] private Transform _WeaponHolder;
We want other scripts (the AttackState
in this example) to be able to access the currently equipped Weapon
and besides attempt to change it (unremarkably the CharacterBrain
would handle that, simply in this case we are just using UI Buttons) and then we have private
fields for the current weapon and the new ane we desire to equip with a public
property that tries to put the Character
in this country when you ready it:
[SerializeField] private Weapon _Weapon; private Weapon _EquippingWeapon; public Weapon Weapon { become => _Weapon; set { if (enabled) return; _EquippingWeapon = value; if (!Grapheme.StateMachine.TrySetState(this)) _EquippingWeapon = _Weapon; } } private void Awake() { _EquippingWeapon = _Weapon; ... }
We only want this land to be entered when you lot are actually irresolute to a dissimilar Weapon
, and so we override
the CanEnterState
belongings to enforce that restriction:
public override bool CanEnterState => _Weapon != _EquippingWeapon;
Note that if CanEnterState
(or the CanExitState
belongings of whatsoever state the Character
is currently in) returns false
, the Weapon
property simply undoes the modify. This ensures that the character cannot change weapons in the middle of other actions that do not let Interruptions (such equally attacks).
When the character does actually enter this country, nosotros desire to play the UnequipAnimation
from the previous Weapon
if it has one and then swap to the new weapon:
private void OnEnable() { if (_Weapon.UnequipAnimation.IsValid) { var state = Character.Animancer.Play(_Weapon.UnequipAnimation); state.Events.OnEnd = _OnUnequipEnd; }
Or if there is no UnequipAnimation
, we but swap to the new Weapon
immediately:
else { OnUnequipEnd(); } }
Directly assigning the OnUnequipEnd
method to the OnEnd
result would create some Garbage every time this state is entered, and so instead we want to create a consul from it on startup so that we tin can reuse the same object every time:
private Action _OnUnequipEnd; private void Awake() { ... _OnUnequipEnd = OnUnequipEnd; AttachWeapon(); }
One time the UnequipAnimation
ends (or is skipped because it was missing), nosotros detach the previous Weapon
from the character's mitt, assign and adhere the new weapon, and and so play the EquipAnimation
of the new Weapon
:
private void OnUnequipEnd() { DetachWeapon(); _Weapon = _EquippingWeapon; AttachWeapon(); if (_Weapon.EquipAnimation.IsValid) { var country = Graphic symbol.Animancer.Play(_Weapon.EquipAnimation); country.Events.OnEnd = Graphic symbol.ForceEnterIdleState; } else { Character.StateMachine.ForceSetState(Graphic symbol.Idle); } } private void AttachWeapon() { if (_Weapon == null) return; if (_WeaponHolder != null) { var transform = _Weapon.transform; transform.parent = _WeaponHolder; transform.localPosition = default; transform.localRotation = Quaternion.identity; transform.localScale = Vector3.i; } _Weapon.gameObject.SetActive(true); } private void DetachWeapon() { if (_Weapon == null) return; _Weapon.transform.parent = transform; _Weapon.gameObject.SetActive(false); }
And finally, nosotros foreclose annihilation from moving the character while in this state or Interrupting it:
individual void FixedUpdate() { Character.Rigidbody.velocity = default; } public override bool CanExitState => false;
Attack Country
The AttackState
script simply plays the appropriate animations from the EquipState.Weapon
:
using Animancer; using Animancer.Examples.StateMachines.Brains; using UnityEngine; public sealed class AttackState : CharacterState { [SerializeField] private EquipState _Equipment; private int _AttackIndex = int.MaxValue; individual void OnEnable() { Character.Animancer.Animator.applyRootMotion = true; if (ShouldRestartCombo()) { _AttackIndex = 0; } else { _AttackIndex++; } var animation = _Equipment.Weapon.AttackAnimations[_AttackIndex]; var state = Character.Animancer.Play(animation); country.Events.OnEnd = Character.ForceEnterIdleState; } individual bool ShouldRestartCombo() { var attackAnimations = _Equipment.Weapon.AttackAnimations; if (_AttackIndex >= attackAnimations.Length - 1) return true; var state = attackAnimations[_AttackIndex].Country; if (land == null || state.Weight == 0) render true; return false; } private void FixedUpdate() { Grapheme.Rigidbody.velocity = default; } public override bool CanExitState => false; private void OnDisable() { Grapheme.Animancer.Animator.applyRootMotion = fake; } }
It starts with a Serialized reference to the EquipState
to be assigned in the Inspector:
[SerializeField] individual EquipState _Equipment;
Normally that reference might go in the Grapheme
class, but since that script comes from the Encephalon Transplants example nosotros exercise non want to alter it.
It is quite common for attack animations in games to crusade the character to step forward so the first thing we do when entering this state is enable Root Motion (and disable it when exiting this state):
private void OnEnable() { Character.Animancer.Animator.applyRootMotion = true; ... } private void OnDisable() { Grapheme.Animancer.Animator.applyRootMotion = false; }
Since each Weapon
can have multiple attack animations which they perform in sequence, the next affair we need to practice is determine which blitheness to use. Each time this state is entered nosotros either beginning from the starting time blitheness or movement onto the next ane in the sequence:
private int _AttackIndex = int.MaxValue; private void OnEnable() { ... if (ShouldRestartCombo()) { _AttackIndex = 0; } else { _AttackIndex++; } ... }
We desire to restart from the first assault if we are already at the terminal animation or if the previous animation has non yet faded out. Otherwise we want to accelerate to the next attack:
private bool ShouldRestartCombo() { var attackAnimations = _Equipment.Weapon.AttackAnimations; if (_AttackIndex >= attackAnimations.Length - ane) return true; var state = attackAnimations[_AttackIndex].State; if (state == null || state.Weight == 0) render true; return faux; }
Once we know which attack nosotros are up to, we tin can simply play its blitheness:
individual void OnEnable() { ... var animation = _Equipment.Weapon.AttackAnimations[_AttackIndex]; var land = Grapheme.Animancer.Play(animation); land.Events.OnEnd = Grapheme.ForceEnterIdleState; }
And but like with the EquipState
, nosotros do not want the character to move during this action or to allow other actions to Interrupt it:
private void FixedUpdate() { Character.Rigidbody.velocity = default; } public override bool CanExitState => faux;
Root Motility Redirect
Since the graphic symbol model (the object with the Animator
component) is separate from the root of the grapheme (the object with the Rigidbody
component), nosotros are using a very uncomplicated RootMotionRedirect
script to apply the movements from the Animator
to the Rigidbody
as explained in the Root Motility example:
using UnityEngine; public sealed form RootMotionRedirect : MonoBehaviour { [SerializeField] private Rigidbody _Rigidbody; [SerializeField] private Animator _Animator; private void OnAnimatorMove() { if (_Animator.applyRootMotion) { _Rigidbody.MovePosition(_Rigidbody.position + _Animator.deltaPosition); _Rigidbody.MoveRotation(_Rigidbody.rotation * _Animator.deltaRotation); } } }
Scene Setup
This example is based on the Brain Transplants scene then the first affair you should do is create a copy of it (either using Edit/Duplicate (Ctrl + D)
or past opening it and using File/Save As...
to save a copy somewhere else).
Character
- If you are using a unlike character model from the DefaultHumanoid included in Animancer, delete the existing model (a kid of the Grapheme object) and drag your own into the same place in the hierarchy.
- Make sure the new model has its position and rotation reset to zero.
- Add an
AnimancerComponent
to it. - Select the Grapheme and assign the new model as its
Animancer
reference.
- Besides add together a
RootMotionRedirect
component to the model and assign its references.
- If y'all are not using the Humanoid-Idle, Humanoid-Walk, and Humanoid-Run animations, assign the ones you want to use in the
IdleState
on the Character and theLocomotionState
on the Brain.
Idle | Locomotion |
---|---|
Weapons
Each of the Weapons is a model with a Weapon
script attached (except for Unarmed which has no model). They all start inactive (and Unarmed gets equipped on startup because information technology is assigned as the default).
The Unarmed and Great Sword setups are fairly obvious:
Unarmed | Great Sword |
---|---|
Unarmed has two attacks and no |
Keen Sword has three attacks with both an |
We only have a single animation chosen Draw Sword 2 to use for the Club'due south Equip Blitheness
, only we can too use that same animation played backwards (Speed = -1
) for the Unequip Blitheness
.
Attacks
Each of the other new scripts we made in this instance can exist attached to the Encephalon object:
Buttons
And finally, nosotros can not use some UI Buttons to set the EquipState.Weapon
when the player clicks them:
Note that if the Equip State does not actually allow itself to be entered, the displayed text will still exist changed. Nosotros could fix that by having the EquipState
set the text rather than doing it in the UI Push button, only that is not really relevant to the purpose of that script and so nosotros have kept it separate for this example.
Concluding Issue
This all combines to create a character that tin do everything from the Brain Transplants example as well as equip different weapons and set on with them.
Source: https://kybernetik.com.au/animancer/docs/examples/fsm/weapons/
Posted by: cornettinglacrievor.blogspot.com
0 Response to "How To Switch Animations For Different Weapons Unity"
Post a Comment