есть анимация ходьбы персонажа (Влево, Вправо, Вверх и Вниз) вызываемая при помощи индексов.
Существует задача : сохранять положение анимации персонажа (оставлять спрайт персонажа повёрнутым в сторону ходьбы "речь идёт о 2D сцене").
Для выполнения данной задачи написал скрипт (будет ниже), а так же создал анимационные клипы в 1 кадр (Где на каждом из них - персонаж повёрнут в нужную сторону).
Суть проблемы - анимация долго думает перед тем как смениться, персонаж уже начинает идти, а анимация, к примеру - смотреть налево, остаётся и только через пол секунды (может меньше) сменяется (Exit time на каждом переходе выставил на 0).
Думаю, проблема в скрипте или моём методе реализации данной процедуры. Прошу помощи и критики, заранее спасибо! (Мой скрипт)
Animator _anim;
bool a;
bool d;
bool w;
bool s;
void Start()
{
a = false;
d = false;
w = false;
s = false;
_anim = GetComponent<Animator>();
}
void FixedUpdate() {
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
if (h == -1)
{
_anim.SetInteger("State", 3);
a = true;
}
else if (h == 1)
{
_anim.SetInteger("State", 4);
d = true;
}
else if (v == 1)
{
_anim.SetInteger("State", 1);
w = true;
}
else if (v == -1 )
{
_anim.SetInteger("State", 2);
s = true;
}
else if (a == true && d == false && w == false && s == false)
{
_anim.SetInteger("State", 7);
a = false;
}
else if (d == true && a == false && w == false && s == false)
{
_anim.SetInteger("State", 6);
d = false;
}
else if (w == true && d == false && a == false && s == false)
{
_anim.SetInteger("State", 8);
w = false;
}
else if (s == true && d == false && w == false && a == false)
{
_anim.SetInteger("State", 5);
s = false;
}
}
}
У вас ошибка в этих if (h == -1) строках. Значение Input.GetAxis хоть вертикальная, хоть горизонтальная не сразу становится -1 или 1, а постепенно, с шагом 0.05f. Note: The Horizontal and Vertical ranges change from 0 to +1 or -1 with increase/decrease in 0.05f steps. GetAxisRaw has changes from 0 to 1 or -1 immediately, so with no steps. Соответственно у вас и меняется анимация когда доберется до значения 1 или -1. Вам надо делать проверки такого рода: if (h > 0){...} if (h < 0) {...}. По поводу FixedUpdate() или Update() там же сказано: This is frame-rate independent; you do not need to be concerned about varying frame-rates when using this value, поэтому делайте в зависимости от вашей логики.
В итоге - получился вот такой код, который исправно работает.
P.s. Спасибо добрым людям.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Anim : MonoBehaviour {
Animator _anim;
bool a; // Создаём переменные которые могут иметь значения (true или false).
bool d; // И будем их использовать для определения - была ли нажата клавиша или нет.
bool w;
bool s;
void Start() // При запуске программы, делаем переменные равными - false (Кнопка не была нажата)
{
a = false;
d = false;
w = false;
s = false;
_anim = GetComponent<Animator>();
}
void Update() // В данной функции действия происходят каждый кадр
{
float h = Input.GetAxis("Horizontal"); // Создаём переменную, которая будет принимать значение (-1 , 0 , 1) в зависимости от горизонтального перемещения
// Тем самым, сообщая, какая клавиша нажата - если -1 то "А", если 1 то "D", если 0 то не нажата
float v = Input.GetAxis("Vertical");// Создаём переменную, которая будет принимать значение (-1 , 0 , 1) в зависимости от вертикального перемещения
// Тем самым, сообщая, какая клавиша нажата - если -1 то "S", если 1 то "W", если 0 то не нажата
if (h < 0) // Если нажата кнопка налево (В моём случае это - "A"), выполнить следующее
{
_anim.SetInteger("State", 3); //Сменить индекс проигрываемой анимации, тем самым сменив её
a = true; // Задать значение переменной "a" - true
d = false;// Остальные переменный, как и в каждом последующем случае, мы делаем равными "false" ибо мы не всегда
// будем двигаться в одну сторону, возможно движение и наискосок, посредством одновременного нажатия двух клавиш
// а для данного метода включения анимации, необходимо иметь не более одной переменной со значением "true"
w = false;
s = false;
}
else if (h > 0)// Если нажата кнопка направо (В моём случае это - "D"), выполнить следующее
{
_anim.SetInteger("State", 4); // Здесь действия одни и те же, лишь меняется индекс анимации и переменная, которая станет - true
d = true;
a = false;
w = false;
s = false;
}
else if (v > 0)// Если нажата кнопка вверх (В моём случае это - "W"), выполнить следующее
{
_anim.SetInteger("State", 1);
w = true;
d = false;
a = false;
s = false;
}
else if (v < 0)// Если нажата кнопка вниз (В моём случае это - "S"), выполнить следующее
{
_anim.SetInteger("State", 2);
s = true;
d = false;
w = false;
a = false;
}
else if (a == true && d == false && w == false && s == false)// На данном этапе, осуществляется проверка - какая клавиша была нажата
// тем самым, определяя в какую сторону мы двигались и запуская соответствующую анимацию
{
_anim.SetInteger("State", 7);
a = false; // После того, как наша анимация, в данном случае (смотреть налево), включена. Нужно вернуть переменную "a" в значение - false
}
else if (d == true && a == false && w == false && s == false)
{
_anim.SetInteger("State", 6);
d = false;
}
else if (w == true && d == false && a == false && s == false)
{
_anim.SetInteger("State", 8);
w = false;
}
else if (s == true && d == false && w == false && a == false)
{
_anim.SetInteger("State", 5);
s = false;
}
}
}
Довольно странный у вас автомат в плане переходов. Если объект движется, например вправо, то встать в неподвижную позу вправо - RightStay он может только после движения вправо, но никак не из любого состояния.
В итоге машина состояний становится чуть более логичной, что ли:
Что по логике вашего кода, что просто по человеческой логике начать смотреть вправо только после того, как персонаж двигался вправо. Такое работает со всеми направлениями.
Я использовал точно такие же номера состояний для переходов, как и у вас в коде - конфликтов быть не должно.
Также в аниматор был добавлен триггер, переключение которого будет условием перехода из состояния движения в состояния "простоя":
Для всех переходов от движения в любую сторону в "простой" в соответствующую сторону нужно добавить вот такой переход от нового триггера:
Ну и наконец финальный код, который, прошу заметить, очень сильно убавил в количестве строк и ненужных проверок с кучей ненужных булевых значений :)
public class Anim : MonoBehaviour {
Animator _anim;
void Start () {
_anim = GetComponent<Animator>();
}
void Update () {
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
if (h < 0) {
_anim.SetInteger("State", 3);
}
else if (h > 0) {
_anim.SetInteger("State", 4);
}
else if (v > 0) {
_anim.SetInteger("State", 1);
}
else if (v < 0) {
_anim.SetInteger("State", 2);
}
else {
// выставляет несуществующее состояние, чтобы не сработал переход
// AnyState -> MoveState
// по-хорошему это НЕ нужно выставлять каждый кадр
// лучше запомнить h и v предыдущего кадра, если предыдущий кадр
// и текущий равны - выставление State`а можно опустить
_anim.SetInteger("State", -1);
// вот такая простая манипуляция c конечным автоматом
// позволяет легко перейти в соответствующее idle состояние
// если мы находимся в движении - будет выбрана верная сторона
// если мы не двигаемся - триггер поменяет состояние, но ничего не поменяется
_anim.SetTrigger("Stopped");
}
}
}
Апостиль в Лос-Анджелесе без лишних нервов и бумажной волокиты
Основные этапы разработки сайта для стоматологической клиники
Продвижение своими сайтами как стратегия роста и независимости