Historique des modifications - Message

Message #11762

Sujet: Séquenceur d'animation de personnage


Type Date Auteur Contenu
Dernière modification 05-03-2017 21:37:18 jonath313
Ce programme utilise le principe des machines à état (FSM : Finit State Machine) pour imposer un séquencement (ordre de succession d'opérations) au programme.
Ici, l'appuie sur une touche lancera une succession d'animations imaginée par l'utilisateur.

Par exemple, on voudrait , en fonction d'un événement clavier, lancer un "combo" (combinaison d'animations), le séquencement suivra alors l'ordre des animations imposée par l'utilisateur.

J'ai fais ce programme pour montrer le principe de base. On peut imaginer que par la suite nous aurons plusieurs machines à état.
Chacune figée dans un état d'attente.
Si on appui sur une touche, on active la combinaison d'animation 1, l'animation est "busy", l'appui sur d'autres touches devient inopérant jusqu'à ce que la machine à état fsm_combo1 ait terminée son séquencement.

Le code fait en sorte que vous ayez juste à changer les valeurs des start / end / speed des frames au début du fichier (#define).

On peut ajouter des états ou en enlever ( enum => ST_ANIM_X).

Voici le code :

Code c++ :

#include <iostream>
#include <irr/irrlicht.h>
#include <stdio.h>

using namespace std;
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

/* Fige le début et la fin de chaque animation */
#define IIDLE_WAIT_ANIM_FRAME   0
#define START_WALK_ANIM_FRAME   0
#define ENDED_WALK_ANIM_FRAME   80
#define START_JUMP_ANIM_FRAME   80
#define ENDED_JUMP_ANIM_FRAME   200
#define START_ATTACK_ANIM_FRAME 300
#define ENDED_ATTACK_ANIM_FRAME 380

/* Fige les vitesses de chaque animation */
#define IDLE_ANIM_SPEED         0
#define WALK_ANIM_SPEED       100
#define JUMP_ANIM_SPEED        20
#define ATCK_ANIM_SPEED        50

#define ANIM_OFFSET             2

/* On définit les états de la machine à état ............. */
enum 		{ ST_ANIM_0, ST_ANIM_1,	ST_ANIM_2,	ST_ANIM_3};  
/* Initialize next & current states ........................... */
static uint8_t 	next_state  = ST_ANIM_0, state = ST_ANIM_0;


void fsm_goNextStep(uint8_t newState);
void fsm_combo1_test(void);

irr::scene::IAnimatedMesh* ePlayer;
 /* Le node du player .......................................... */
IAnimatedMeshSceneNode* NodePlayer;                                                  

int     frameEnCour =0;
bool    combo1 = false;
bool    anim_busy = false;

class MyEventReceiver : public IEventReceiver
{
public:

    MyEventReceiver(){}

    bool OnEvent(const SEvent& event)
    {
        if (anim_busy == false && event.EventType == irr::EET_KEY_INPUT_EVENT && !event.KeyInput.PressedDown)
        {
            switch (event.KeyInput.Key)
            {
            case irr::KEY_KEY_Z:
                combo1 = true;     /// Lance la fsm pour l'exemple
                return true;
            case irr::KEY_KEY_Q:
                return true;
            case irr::KEY_KEY_D:
                return true;
            case irr::KEY_KEY_S:
                return true;
            case irr::KEY_KEY_X:
                return true;
            default:
                break;
            }
        }
        return false;
    }
private:
};


int main( int argc, char** argv )
{
    /// Initialisation
    IrrlichtDevice *device = createDevice( video::EDT_SOFTWARE, dimension2d<u32>(1024,768), 32, false, false, false,0);
    if (!device)    return 1;
    IVideoDriver* driver = device->getVideoDriver();
    ISceneManager* smgr = device->getSceneManager();
    IGUIEnvironment* guienv = device->getGUIEnvironment();

    /// Charge le node du player
    ePlayer = (irr::scene::IAnimatedMesh*)smgr->getMesh("media/wolfa13.B3D");
    NodePlayer = smgr->addAnimatedMeshSceneNode(ePlayer);
    NodePlayer->setMaterialTexture(0, driver->getTexture("media/skin6.jpg"));
    NodePlayer->setMaterialFlag(irr::video::EMF_GOURAUD_SHADING, true);
    NodePlayer->setMaterialType(irr::video::EMT_REFLECTION_2_LAYER);
    // Init player size
    NodePlayer->setScale(vector3df(0.05,0.05,0.05));
    // Init animation
    NodePlayer->setAnimationSpeed(IDLE_ANIM_SPEED);
    NodePlayer->setFrameLoop(IIDLE_WAIT_ANIM_FRAME,IIDLE_WAIT_ANIM_FRAME); // ATTENDRE
    NodePlayer->setLoopMode(false);

    smgr->addCameraSceneNode(0, vector3df(0,30,-40), vector3df(0,5,0));

    /// Crée le receveur d'événements.
    MyEventReceiver receiver;
    device->setEventReceiver(&receiver);

	while(device->run())
	{
		driver->beginScene(true, true, SColor(255,100,101,140));
                /// La FSM est dans un état d'attente
                fsm_combo1_test();
                /// On relève le numero de la frame en cours
                frameEnCour = NodePlayer->getFrameNr();

		smgr->drawAll();
		guienv->drawAll();

		driver->endScene();
	}
	device->drop();

	return 0;
}


/*   GO TO NEXT STATE  */
/* Function for going to next state automatically ............. */
void fsm_goNextStep(uint8_t newState)  
{
/* Stay to actual state : leds flashing ....................... */
	next_state = newState;		   
}

/*  FSM */
void fsm_combo1_test(void)
{
	switch(state)
	{
	    case ST_ANIM_0:                                                              // Etat Idle
            if(combo1 == true)                                                           // MARCHER
            {
                combo1      = false;
                anim_busy   = true;
                NodePlayer->setAnimationSpeed(WALK_ANIM_SPEED);
                NodePlayer->setFrameLoop(START_WALK_ANIM_FRAME,ENDED_WALK_ANIM_FRAME);
                NodePlayer->setLoopMode(false);
                fsm_goNextStep(ST_ANIM_1);
            }
	        break;
		case ST_ANIM_1:
            if((NodePlayer->getFrameNr())> (ENDED_WALK_ANIM_FRAME - ANIM_OFFSET))                // SAUTER
            {
                NodePlayer->setAnimationSpeed(JUMP_ANIM_SPEED);
                NodePlayer->setFrameLoop(START_JUMP_ANIM_FRAME,ENDED_JUMP_ANIM_FRAME);
                NodePlayer->setLoopMode(false);
                fsm_goNextStep(ST_ANIM_2);
            }
            break;
		case ST_ANIM_2:
            if((NodePlayer->getFrameNr())> (ENDED_JUMP_ANIM_FRAME - ANIM_OFFSET))               // ATTAQUER
            {
                NodePlayer->setAnimationSpeed(ATCK_ANIM_SPEED);
                NodePlayer->setFrameLoop(START_ATTACK_ANIM_FRAME,ENDED_ATTACK_ANIM_FRAME);
                NodePlayer->setLoopMode(false);
                fsm_goNextStep(ST_ANIM_3);
            }
            break;
		case ST_ANIM_3:
            if((NodePlayer->getFrameNr())> (ENDED_ATTACK_ANIM_FRAME  - ANIM_OFFSET))         // ATTENDRE
            {
                NodePlayer->setAnimationSpeed(IDLE_ANIM_SPEED);
                NodePlayer->setFrameLoop(IIDLE_WAIT_ANIM_FRAME,IIDLE_WAIT_ANIM_FRAME);
                NodePlayer->setLoopMode(false);
                anim_busy   = false;
                fsm_goNextStep(ST_ANIM_0);
            }
            break;
		default :
            break;
	}
	state = next_state;
}


Voila, j&#8217;espère que cela vous sera utile. J'ai appliqué cela à des animations mais on peut imaginer pleins d'autres cas d'utilisation (exemple séquencement d'un déplacement d'un objet, ...).
Cette machine a état est linéaire, mais on peut imaginer que à chaque état nous ayons plusieurs états futurs possible.
Création du message 05-03-2017 19:29:21 jonath313
Ce programme utilise le principe des machines à état (FSM : Finit State Machine) pour imposer un séquencement (ordre de succession d'opérations) au programme.
Ici, l'appuie sur une touche lancera une succession d'animations imaginée par l'utilisateur.

Par exemple, on voudrait , en fonction d'un événement clavier, lancer un "combo" (combinaison d'animations), le séquencement suivra alors l'ordre des animations imposée par l'utilisateur.

J'ai fais ce programme pour montrer le principe de base. On peut imaginer que par la suite nous aurons plusieurs machines à état.
Chacune figée dans un état d'attente.
Si on appui sur une touche, on active la combinaison d'animation 1, l'animation est "busy", l'appui sur d'autres touches devient inopérant jusqu'à ce que la machine à état fsm_combo1 ait terminée son séquencement.

Le code fait en sorte que vous ayez juste à changer les valeurs des start / end / speed des frames au début du fichier (#define).

On peut ajouter des états ou en enlever ( enum => ST_ANIM_X).

Voici le code :

Code c++ :

#include <iostream>
#include <irr/irrlicht.h>
#include <stdio.h>

using namespace std;
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

/* Fige le début et la fin de chaque animation */
#define IIDLE_WAIT_ANIM_FRAME   0
#define START_WALK_ANIM_FRAME   0
#define ENDED_WALK_ANIM_FRAME   80
#define START_JUMP_ANIM_FRAME   80
#define ENDED_JUMP_ANIM_FRAME   200
#define START_ATTACK_ANIM_FRAME 300
#define ENDED_ATTACK_ANIM_FRAME 380

/* Fige les vitesses de chaque animation */
#define IDLE_ANIM_SPEED         0
#define WALK_ANIM_SPEED       100
#define JUMP_ANIM_SPEED        20
#define ATCK_ANIM_SPEED        50

#define ANIM_OFFSET             2

/* On définit les états de la machine à état ............. */
enum 		{ ST_ANIM_0, ST_ANIM_1,	ST_ANIM_2,	ST_ANIM_3};  
/* Initialize next & current states ........................... */
static uint8_t 	next_state  = ST_ANIM_0, state = ST_ANIM_0;


void fsm_goNextStep(uint8_t newState);
void fsm_combo1_test(void);

irr::scene::IAnimatedMesh* ePlayer;
 /* Le node du player .......................................... */
IAnimatedMeshSceneNode* NodePlayer;                                                  

int     frameEnCour =0;
bool    combo1 = false;
bool    anim_busy = false;

class MyEventReceiver : public IEventReceiver
{
public:

    MyEventReceiver(){}

    bool OnEvent(const SEvent& event)
    {
        if (anim_busy == false && event.EventType == irr::EET_KEY_INPUT_EVENT && !event.KeyInput.PressedDown)
        {
            switch (event.KeyInput.Key)
            {
            case irr::KEY_KEY_Z:
                combo1 = true;     /// Lance la fsm pour l'exemple
                return true;
            case irr::KEY_KEY_Q:
                return true;
            case irr::KEY_KEY_D:
                return true;
            case irr::KEY_KEY_S:
                return true;
            case irr::KEY_KEY_X:
                return true;
            default:
                break;
            }
        }
        return false;
    }
private:
};


int main( int argc, char** argv )
{
    /// Initialisation
    IrrlichtDevice *device = createDevice( video::EDT_SOFTWARE, dimension2d<u32>(1024,768), 32, false, false, false,0);
    if (!device)    return 1;
    IVideoDriver* driver = device->getVideoDriver();
    ISceneManager* smgr = device->getSceneManager();
    IGUIEnvironment* guienv = device->getGUIEnvironment();

    /// Charge le node du player
    ePlayer = (irr::scene::IAnimatedMesh*)smgr->getMesh("media/wolfa13.B3D");
    NodePlayer = smgr->addAnimatedMeshSceneNode(ePlayer);
    NodePlayer->setMaterialTexture(0, driver->getTexture("media/skin6.jpg"));
    NodePlayer->setMaterialFlag(irr::video::EMF_GOURAUD_SHADING, true);
    NodePlayer->setMaterialType(irr::video::EMT_REFLECTION_2_LAYER);
    // Init player size
    NodePlayer->setScale(vector3df(0.05,0.05,0.05));
    // Init animation
    NodePlayer->setAnimationSpeed(IDLE_ANIM_SPEED);
    NodePlayer->setFrameLoop(IIDLE_WAIT_ANIM_FRAME,IIDLE_WAIT_ANIM_FRAME); // ATTENDRE
    NodePlayer->setLoopMode(false);

    smgr->addCameraSceneNode(0, vector3df(0,30,-40), vector3df(0,5,0));

    /// Crée le receveur d'événements.
    MyEventReceiver receiver;
    device->setEventReceiver(&receiver);

	while(device->run())
	{
		driver->beginScene(true, true, SColor(255,100,101,140));
                /// La FSM est dans un état d'attente
                fsm_combo1_test();
                /// On relève le numero de la frame en cours
                frameEnCour = NodePlayer->getFrameNr();

		smgr->drawAll();
		guienv->drawAll();

		driver->endScene();
	}
	device->drop();

	return 0;
}


/*   GO TO NEXT STATE  */
/* Function for going to next state automatically ............. */
void fsm_goNextStep(uint8_t newState)  
{
/* Stay to actual state : leds flashing ....................... */
	next_state = newState;		   
}

/*  FSM */
void fsm_combo1_test(void)
{
	switch(state)
	{
	    case ST_ANIM_0:                                                              // Etat Idle
            if(combo1 == true)                                                           // MARCHER
            {
                combo1      = false;
                anim_busy   = true;
                NodePlayer->setAnimationSpeed(WALK_ANIM_SPEED);
                NodePlayer->setFrameLoop(START_WALK_ANIM_FRAME,ENDED_WALK_ANIM_FRAME);
                NodePlayer->setLoopMode(false);
                fsm_goNextStep(ST_ANIM_1);
            }
	        break;
		case ST_ANIM_1:
            if((NodePlayer->getFrameNr())> (ENDED_WALK_ANIM_FRAME - ANIM_OFFSET))                // SAUTER
            {
                NodePlayer->setAnimationSpeed(JUMP_ANIM_SPEED);
                NodePlayer->setFrameLoop(START_JUMP_ANIM_FRAME,ENDED_JUMP_ANIM_FRAME);
                NodePlayer->setLoopMode(false);
                fsm_goNextStep(ST_ANIM_2);
            }
            break;
		case ST_ANIM_2:
            if((NodePlayer->getFrameNr())> (ENDED_JUMP_ANIM_FRAME - ANIM_OFFSET))               // ATTAQUER
            {
                NodePlayer->setAnimationSpeed(ATCK_ANIM_SPEED);
                NodePlayer->setFrameLoop(START_ATTACK_ANIM_FRAME,ENDED_ATTACK_ANIM_FRAME);
                NodePlayer->setLoopMode(false);
                fsm_goNextStep(ST_ANIM_3);
            }
            break;
		case ST_ANIM_3:
            if((NodePlayer->getFrameNr())> (ENDED_ATTACK_ANIM_FRAME  - ANIM_OFFSET))         // ATTENDRE
            {
                NodePlayer->setAnimationSpeed(IDLE_ANIM_SPEED);
                NodePlayer->setFrameLoop(IIDLE_WAIT_ANIM_FRAME,IIDLE_WAIT_ANIM_FRAME);
                NodePlayer->setLoopMode(false);
                anim_busy   = false;
                fsm_goNextStep(ST_ANIM_0);
            }
            break;
		default :
            break;
	}
	state = next_state;
}


Voila, j&#8217;espère que cela vous sera utile. J'ai appliqué cela à des animations mais on peut imaginer pleins d'autres cas d'utilisation (exemple séquencement d'un déplacement d'un objet, ...).
Cette machine a état est linéaire, mais on peut imaginer que à chaque état nous ayons plusieurs états futurs possible.

Retour

Options Liens officiels Caractéristiques Statistiques Communauté
Préférences cookies
Corrections
irrlicht
irrklang
irredit
irrxml
Propulsé par Django
xhtml 1.0
css 2.1
884 membres
1440 sujets
11337 messages
Dernier membre inscrit: Saidov17
152 invités en ligne
membre en ligne: -
RSS Feed