Jump to content

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


photo

Recommended Posts

 

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

Помогите пожалуйста разобраться с кодом, пример брал здесь:

Проблема заключается в том, при нажатии на пробел, анимация удара по мячу часто проигрывается не до конца, затем при следующем нажатии доигрывается остаток анимации, иногда проигрывается два раза (((

Вот код:

#pragma once

#include <Unigine.h>

class AnimationState : public Unigine::ComponentBase
{

public:

	COMPONENT_DEFINE(AnimationState, ComponentBase)
	COMPONENT_INIT(Init)
	COMPONENT_UPDATE(Update)


	PROP_PARAM(File, IdleAnim)
	PROP_PARAM(File, WalkAnim)
	PROP_PARAM(File, R_WalkAnim)
	PROP_PARAM(File, RunAnim)

	struct AnimationData final : public Unigine::ComponentStruct
	{
		PROP_PARAM(File, Source, nullptr, "Source", nullptr, nullptr, "filter=.anim")

		float currentFrame{};
		int numFrames{};
		bool playing {true}; // флаг что анимация играет. по дефолту лучше чтобы не играла.

		int isFinished() const
		// тут измерение в кадрах. 0.5 кадра это анимация уже точно закончилась и начала уже возвращаться к первому кадру.
		// поэтому лучше поставить чтото больше чем 1 чтобы не увидеть бленда с первым кадром
		{return Unigine::Math::compare(static_cast<float>(numFrames), currentFrame, 0.5f);}
		//{return (static_cast<float>(numFrames) == currentFrame);}
	};

	// declaration of the structured parameter named "ShootingAnimation" of the AnimationData type declared above
	PROP_STRUCT(AnimationData, ShootingAnimation);
	// declaration of the structured parameter named "PassAnimation" of the AnimationData type declared above
	PROP_STRUCT(AnimationData, PassAnimation);

protected:
	void Init();
	void Update();


private:

	Unigine::ObjectMeshSkinnedPtr MainCharacter;
	float Weight = 0;
	bool isWeightChanged = false;

	enum ANIM_STATE {PROCEDURAL = 0, IDLE, WALK, REVERSE_WALK, RUN, SHOOTING, PASS, COUNT};
	ANIM_STATE MainState = ANIM_STATE::IDLE, PrevState = ANIM_STATE::IDLE;
	void ChangeState();
	void ResetWeight();

};

 

 

#include "AnimationState.h"

REGISTER_COMPONENT(AnimationState)

void AnimationState::ResetWeight() {

	isWeightChanged = true;

	if (isWeightChanged) {
		Weight = 0;
		isWeightChanged = false;
	}
}

void AnimationState::ChangeState() {
	
	switch (MainState) {

	case AnimationState::IDLE:
		if (Unigine::Input::isKeyPressed(Unigine::Input::KEY_W) && Unigine::Input::isKeyPressed(Unigine::Input::KEY_LEFT_SHIFT))
					{ResetWeight(); MainState = ANIM_STATE::RUN; PrevState = ANIM_STATE::IDLE;}
		if (Unigine::Input::isKeyPressed(Unigine::Input::KEY_W)) 
					{ResetWeight(); MainState = ANIM_STATE::WALK; PrevState = ANIM_STATE::IDLE;}
		if (Unigine::Input::isKeyPressed(Unigine::Input::KEY_S))
					{ResetWeight(); MainState = ANIM_STATE::REVERSE_WALK; PrevState = ANIM_STATE::IDLE;}
		if (Unigine::Input::isKeyUp(Unigine::Input::KEY_SPACE))
					{ShootingAnimation->playing = true; ResetWeight(); MainState = ANIM_STATE::SHOOTING; PrevState = ANIM_STATE::IDLE;}
		if (Unigine::Input::isKeyUp(Unigine::Input::KEY_J))
					{PassAnimation->playing = true; ResetWeight(); MainState = ANIM_STATE::PASS; PrevState = ANIM_STATE::IDLE;}

		MainCharacter->lerpLayer(ANIM_STATE::PROCEDURAL, PrevState, ANIM_STATE::IDLE, Weight * 2);
		break;
	case AnimationState::WALK:
		if (Unigine::Input::isKeyPressed(Unigine::Input::KEY_LEFT_SHIFT))
					{ResetWeight(); MainState = ANIM_STATE::RUN; PrevState = ANIM_STATE::WALK;}
		if (Unigine::Input::isKeyUp(Unigine::Input::KEY_W))
					{ResetWeight(); MainState = ANIM_STATE::IDLE; PrevState = ANIM_STATE::WALK;}

		MainCharacter->lerpLayer(ANIM_STATE::PROCEDURAL, PrevState, ANIM_STATE::WALK, Weight * 1.65f);
		break;
	case AnimationState::REVERSE_WALK:
		if (Unigine::Input::isKeyUp(Unigine::Input::KEY_S))
					{ResetWeight(); MainState = ANIM_STATE::IDLE; PrevState = ANIM_STATE::REVERSE_WALK;}

		MainCharacter->lerpLayer(ANIM_STATE::PROCEDURAL, PrevState, ANIM_STATE::REVERSE_WALK, Weight * 1.65f);
		break;
	case AnimationState::RUN:
		if (Unigine::Input::isKeyUp(Unigine::Input::KEY_LEFT_SHIFT))
					{ResetWeight(); MainState = ANIM_STATE::WALK; PrevState = ANIM_STATE::RUN;}
		if (Unigine::Input::isKeyUp(Unigine::Input::KEY_W))
					{ResetWeight(); MainState = ANIM_STATE::IDLE; PrevState = ANIM_STATE::RUN;}

		MainCharacter->lerpLayer(ANIM_STATE::PROCEDURAL, PrevState, ANIM_STATE::RUN, Weight * 3);
		break;
	case AnimationState::SHOOTING:
		if (ShootingAnimation->isFinished())
					{ShootingAnimation->playing = false; ResetWeight(); MainState = ANIM_STATE::IDLE; PrevState = ANIM_STATE::SHOOTING;}

		MainCharacter->lerpLayer(ANIM_STATE::PROCEDURAL, PrevState, ANIM_STATE::SHOOTING, Weight * 6);
		break;
	case AnimationState::PASS:
		if (PassAnimation->isFinished())
					{PassAnimation->playing = false; ResetWeight(); MainState = ANIM_STATE::IDLE; PrevState = ANIM_STATE::PASS;}

		MainCharacter->lerpLayer(ANIM_STATE::PROCEDURAL, PrevState, ANIM_STATE::PASS, Weight * 6);
		break;

	default:
		break;
	}
}

void AnimationState::Init() {

	const char* Idle = Unigine::FileSystem::getGUID(IdleAnim.getRaw()).getFileSystemString();
	const char* Walk = Unigine::FileSystem::getGUID(WalkAnim.getRaw()).getFileSystemString();
	const char* RWalk = Unigine::FileSystem::getGUID(R_WalkAnim.getRaw()).getFileSystemString();
	const char* Run = Unigine::FileSystem::getGUID(RunAnim.getRaw()).getFileSystemString();

	MainCharacter = Unigine::checked_ptr_cast<Unigine::ObjectMeshSkinned>(node);

	MainCharacter->setNumLayers(ANIM_STATE::COUNT);

	int IdleNum = MainCharacter->addAnimation(Idle);
	int WalkNum = MainCharacter->addAnimation(Walk);
	int R_WalkNum = MainCharacter->addAnimation(RWalk);
	int RunNum = MainCharacter->addAnimation(Run);

	MainCharacter->setAnimation(ANIM_STATE::PROCEDURAL, IdleNum); // Procedural
	MainCharacter->setAnimation(ANIM_STATE::IDLE, IdleNum);
	MainCharacter->setAnimation(ANIM_STATE::WALK, WalkNum);
	MainCharacter->setAnimation(ANIM_STATE::REVERSE_WALK, R_WalkNum);
	MainCharacter->setAnimation(ANIM_STATE::RUN, RunNum);

	MainCharacter->setAnimation(ANIM_STATE::SHOOTING, Unigine::FileSystem::getGUID(ShootingAnimation->Source).getFileSystemString());
	ShootingAnimation->numFrames = MainCharacter->getNumFrames(ANIM_STATE::SHOOTING);
	ShootingAnimation->playing = false; // не надо играть сразу

	MainCharacter->setAnimation(ANIM_STATE::PASS, Unigine::FileSystem::getGUID(PassAnimation->Source).getFileSystemString());
	PassAnimation->numFrames = MainCharacter->getNumFrames(ANIM_STATE::PASS);
	PassAnimation->playing = false; // не надо играть сразу
}

void AnimationState::Update() {

	float anim_ifps = Unigine::Game::getIFps() * 30; // deltaTime

	MainCharacter->setFrame(ANIM_STATE::PROCEDURAL, Unigine::Game::getTime() * 30);
	MainCharacter->setFrame(ANIM_STATE::IDLE, Unigine::Game::getTime() * 30);
	MainCharacter->setFrame(ANIM_STATE::WALK, Unigine::Game::getTime() * 30);
	MainCharacter->setFrame(ANIM_STATE::REVERSE_WALK, Unigine::Game::getTime() * 30);
	MainCharacter->setFrame(ANIM_STATE::RUN, Unigine::Game::getTime() * 30);

	if (ShootingAnimation->playing) { // запускаем только если надо играть
		ShootingAnimation->currentFrame = MainCharacter->setFrame(ANIM_STATE::SHOOTING, ShootingAnimation->currentFrame + anim_ifps);
	}

	if (PassAnimation->playing) { // запускаем только если надо играть
		PassAnimation->currentFrame = MainCharacter->setFrame(ANIM_STATE::PASS, PassAnimation->currentFrame + anim_ifps);
	}

	Weight = Unigine::Math::clamp(Weight + Unigine::Game::getIFps(), 0.0f, 1.0f);

	ChangeState();
	//Unigine::Console::message("Weight is: %f \n", Weight);
	Unigine::Console::message("Current frame from ShootingAnimation is: %f \n", ShootingAnimation->currentFrame);
	Unigine::Console::message("ShootingAnimation isFinished: %f \n", ShootingAnimation->isFinished());
}

Видео:

 

Иногда в состоянии IDLE, в isFinished() пролетают какие то космические значения, фото ниже (в конце видео данный глюк так же можно заметить):

image.thumb.png.c5aac896fbd666d3ea85be58a25b3b6c.png

Помогите пожалуйста разобраться, что я сделал не так?

Link to comment

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

В аттаче как раз пример как можно это сделать (на пробел осуществляется пинок, движение на t, g, f, h): 

 

Для работы примера можно импортнуть пекедж в пустой проект, добавить файлы SimpleStateMachine.cpp/h в проект и после чего собрать. Мир для тестов: state_machine.world.

Спасибо.

simple_state_machine.upackage

How to submit a good bug report
---
FTP server for test scenes and user uploads:

Link to comment
×
×
  • Create New...