DZolotarev Posted November 27, 2023 Posted November 27, 2023 Нужно смешать несколько анимаций со следующей логикой: Персонаж лежит на плоскости и корчится от боли. Происходит внешнее событие. Персонаж встаёт и идёт к заданной точке. Проблема в переключении от анимации вставания к анимации передвижения: похоже, что анимация вставания проигрывается до конца, а потом запускается второй раз и уже в течение этого второго запуска переключается на анимацию передвижения. Никак не могу понять, как эту проблему решить. В качестве образца использовал видео LazyUnigineDude: https://youtu.be/Yjkf6ktHlTc?feature=shared https://youtu.be/RUfz9UAoZEQ?feature=shared https://youtu.be/sV4M8OJSQpI?feature=shared Мой код здесь (C++, Linux).
cash-metall Posted November 28, 2023 Posted November 28, 2023 Здравствуйте! 22 hours ago, DZolotarev said: Проблема в переключении от анимации вставания к анимации передвижения: похоже, что анимация вставания проигрывается до конца, а потом запускается второй раз и уже в течение этого второго запуска переключается на анимацию передвижения. Никак не могу понять, как эту проблему решить. в данный момент в вашем алгоритме все анимации находятся в постоянном проигрывании. т.е. человек одновременно лежит, кашляет, встает и идет вы только меняете веса между этими анимациями. такой подход имеет место быть когда вы имеете зацикленные анимации, т.е. нет начала и конца действия - дышит, идет. а вот смешивать анимацию вставания или кашляния нужно сначала с плавным входом и в конце с плавным выходом. см. аттач картинку как это исправить 1. добавить у анимации флаг playing struct AnimationData final : public Unigine::ComponentStruct { PROP_PARAM(File, Source, nullptr, "Source", nullptr, nullptr, "filter=.anim") PROP_PARAM(Float, WeightMultiplier, 1.0f, "Weight Multiplier", nullptr, nullptr, "min=0.0f") float currentFrame{}; int numFrames{}; bool playing {true}; // флаг что анимация играет. по дефолту лучше чтобы не играла. bool isFinished() const // тут измерение в кадрах. 0.5 кадра это анимация уже точно закончилась и начала уже возвращаться к первому кадру. // поэтому лучше поставить чтото больше чем 1 чтобы не увидеть бленда с первым кадром { return Unigine::Math::compare(static_cast<float>(numFrames), currentFrame, 1.5f); } }; 2. при инициализации останавливаем "вставание" character->setAnimation( State::STANDING_UP, FileSystem::getGUID(StandingUpAnimation->Source).getFileSystemString()); StandingUpAnimation->numFrames = character->getNumFrames(State::STANDING_UP); StandingUpAnimation->playing = false; // не надо играть сразу 3. воспроизводим только ту анимацию, которую надо. при этом надо чтобы у каждого было свое время. в вашем случае для этого можно использовать currentFrame float anim_ifps = Game::getIFps() * AnimationFPS; // deltaTime if (LayingIdleAnimation->playing) // если надо играть LayingIdleAnimation->currentFrame = character->setFrame(State::LAYING_IDLE, LayingIdleAnimation->currentFrame + anim_ifps); // добавляем deltaTime if (MoaningAnimation->playing) MoaningAnimation->currentFrame = character->setFrame(State::MOANING, MoaningAnimation->currentFrame + anim_ifps); if (MildCoughAnimation->playing) MildCoughAnimation->currentFrame = character->setFrame(State::MILD_COUGH, MildCoughAnimation->currentFrame + anim_ifps); if (SevereCoughAnimation->playing) SevereCoughAnimation->currentFrame = character->setFrame(State::SEVERE_COUGH, SevereCoughAnimation->currentFrame + anim_ifps); if (StandingUpAnimation->playing) StandingUpAnimation->currentFrame = character->setFrame(State::STANDING_UP, StandingUpAnimation->currentFrame + anim_ifps); 4. при переходе к состоянию STANDING_UP запускаем анимацию (это надо сделать во всех местах где оно обрабатывается, может вынести куда то в отдельную функцию?) //... else if (event) { prevState = State::LAYING_IDLE; currentState = State::STANDING_UP; StandingUpAnimation->playing = true; // запускаем weight = 0.f; } //... else if (event) { prevState = State::MOANING; currentState = State::STANDING_UP; StandingUpAnimation->playing = true; // запускаем weight = 0.f; } //... 5. ну и напоследок когда анимация завершилась (т.е. на последнем кадре) - останавливаем ее case State::STANDING_UP: if (StandingUpAnimation->isFinished()) { StandingUpAnimation->playing = false; // стоп, хватит prevState = State::STANDING_UP; currentState = State::WALKING; weight = 0.f; } вот такое у меня получилось. видно что большая задержка при переходе между standing_up и walking - так сделана анимация. вы можете поправить анимацию, или сделать чтобы бленд началася раньше, а не только на последнем кадре. по идее такой же подход надо сделать с судорогами и кашелем - чтобы они начинались с начала, а не начинали смешиваться с середины анимации, и вконце не перескакивали на первый кадр - тогда анимации будут без дерганий, плавнее. 3
Recommended Posts