Jump to content

Смешивание анимаций (C++)


photo

Recommended Posts

Нужно смешать несколько анимаций со следующей логикой:

  1. Персонаж лежит на плоскости и корчится от боли.
  2. Происходит внешнее событие.
  3. Персонаж встаёт и идёт к заданной точке.

Проблема в переключении от анимации вставания к анимации передвижения: похоже, что анимация вставания проигрывается до конца, а потом запускается второй раз и уже в течение этого второго запуска переключается на анимацию передвижения.

Никак не могу понять, как эту проблему решить.

В качестве образца использовал видео LazyUnigineDude:

Мой код здесь (C++, Linux).

Link to comment

Здравствуйте! 

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 - так сделана анимация. вы можете поправить анимацию, или сделать чтобы бленд началася раньше, а не только на последнем кадре. 

 

по идее такой же подход надо сделать с судорогами и кашелем - чтобы они начинались с начала, а не начинали смешиваться с середины анимации, и вконце не перескакивали на первый кадр - тогда анимации будут без дерганий, плавнее. 

Screenshot_1.jpg

  • Like 3
Link to comment
×
×
  • Create New...