#0 

05-03-2017 20:29:21

jonath313
Abonné
Date d'inscription: 28-12-2009
Messages: 240

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’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.

Dernière modification par jonath313 (05-03-2017 22:37:18)

Hors ligne


#1 

06-03-2017 14:32:03

Magun
SleekThink Producer
Lieu: Punakha
Date d'inscription: 18-11-2007
Messages: 910
Corrections: 2
Site web

Sympas, merci à toi smile

pour aller plus loin, je pense que tu pourrais coder cela a coup de template
et de foncteur pour généré une matrice qui correspond au action et au suite d'animation

en théori il "faudrais" faire une couche d'abstraction et tout ça, en pratique les template vont supprimer les mécasime de table virtuel
ou les éventuel dereferencement nécéssaire à l'utilisation d'un objet -> par reference du coup, bref c'est plus rapide

a voir, je ne pensse pas avoir le temps cette semaine, mais j'esserais de te faire un code d'exemple pour te montrer
le but étant de te d'étacher des contraintes d'implémentation qui ajoute des sections de code a tester quand ton code "grossit"

puisque finalement tu risque de te retrouver avec plusieurs sections qui peuvent devenir complexe à maintenir à meusure que tu rajoute des actions
et apres tu peux avoir du code en double ... :p, bon je ne sais pas si je suis très claire ...

mais ça peux être utile pour certain wink

Hors ligne


#2 

06-03-2017 21:15:13

jonath313
Abonné
Date d'inscription: 28-12-2009
Messages: 240

Merci de tas réponse Magun. En fait ce code à rien avoir avec ce que je voulais faire à la base. Mais je trouvais le principe sympas pour créer une séquence de succession d'animations.

Je me rend compte que je suis toujours dépendant des tests "if" et comme le sujet "animation binding" j'aurais préféré utiliser ce system (fsm) pour manager les animations d'un personnage.

Etre dans un état "Idle" et de là passer à des animations toutes prédéfinies. C'est un peut le foutoir, je voudrais limiter le nombre de sous-modules implémenté dans le code car çà devient vite une usine à gaz.

Je suis curieux d'en savoir plus sur ton modèle de template.

Hors ligne


#3 

14-03-2017 22:30:41

Magun
SleekThink Producer
Lieu: Punakha
Date d'inscription: 18-11-2007
Messages: 910
Corrections: 2
Site web

Salut
voila un petit code, dit moi ce que tu en pensse ? smile

Code c++ :


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

//! ==========================================================
// petite definition recursive d'une list en template'
template <int HEAD, class TAIL>
struct IntList
{
    enum { head = HEAD };
    typedef TAIL tail;
};

struct IntListEnd {};

// iteration sur la list "template" pour trouve un etat
template <class STATELIST>
struct SwitchTemplate {
    template <class CONTEXT>
    static typename CONTEXT::ReturnType work(int state, CONTEXT & context)
    {
        return ((STATELIST::head == state) ?
                 context.template operator()<STATELIST::head>() :
                 SwitchTemplate<typename STATELIST::tail>::work(state, context));
    }
};

// fin de l'iteration recursive -> retourne l'état courant
template <>
struct SwitchTemplate<IntListEnd>
{
    template <class CONTEXT>
    static typename CONTEXT::ReturnType work(int state, CONTEXT & context)
    {
        return typename CONTEXT::ReturnType();
    }
};
//! ==========================================================

template <class CONTEXT, class STATELIST = typename CONTEXT::StateList>
struct StateMachine
{
    CONTEXT & context;
    int state;

    template <class RET, class DATA> struct CallEvent {
        typedef RET ReturnType;
        CONTEXT & context;
        DATA & data;

        template <int STATE> RET operator()(){
            return context.template event<STATE>(data);
        }
    };

    template <class RET, class DATA> struct CallEventConst {
        typedef RET ReturnType;
        CONTEXT & context;
        const DATA & data;

        template <int STATE> RET operator()(){
            return context.template event<STATE>(data);
        }
    };

    template <class RET> struct CallEventNoData {
        typedef RET ReturnType;
        CONTEXT & context;

        template <int STATE> RET operator()(){
            return context.template event<STATE>();
        }
    };

    struct CallEnter {
        typedef void ReturnType;
        CONTEXT & context;

        template <int STATE> ReturnType operator()(){
            return context.template enter<STATE>();
        }
    };

    StateMachine(CONTEXT & c) : context(c), state(STATELIST::head) {
        CallEnter cee = {context};
        SwitchTemplate<STATELIST>::work(state, cee);
    }

    void changeState(const int newstate){
        state = newstate;
        CallEnter cee = {context};
        SwitchTemplate<STATELIST>::work(state, cee);
    }

    void work(){
        CallEventNoData<int> ce = {context};
        int newstate = SwitchTemplate<STATELIST>::work(state, ce);
        if(newstate != state)
            changeState(newstate);
    }

    template <class EVENT>
    void work(const EVENT & ev){
        CallEventConst<int, EVENT> ce = {context, ev};
        int newstate = SwitchTemplate<STATELIST>::work(state, ce);
        changeState(newstate);
    }

    template <class EVENT>
    void work(EVENT & ev){
        CallEvent<int, EVENT> ce = {context, ev};
        int newstate = SwitchTemplate<STATELIST>::work(state, ce);
        changeState(newstate);
    }
};

#define LIST1(a)                IntList<a,IntListEnd>
#define LIST2(a,b)              IntList<a,LIST1(b) >
#define LIST3(a,b,c)            IntList<a,LIST2(b,c) >
#define LIST4(a,b,c,d)          IntList<a,LIST3(b,c,d) >
#define LIST5(a,b,c,d,e)        IntList<a,LIST4(b,c,d,e) >
#define LIST6(a,b,c,d,e,f)      IntList<a,LIST5(b,c,d,e,f) >
#define LIST7(a,b,c,d,e,f,g)    IntList<a,LIST6(b,c,d,e,f,g) >
#define LIST8(a,b,c,d,e,f,g,h)  IntList<a,LIST7(b,c,d,e,f,g,h) >
//! ==========================================================

using namespace std;
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
irr::scene::IAnimatedMesh* ePlayer;
IAnimatedMeshSceneNode* NodePlayer;

//! ==========================================================

enum States
{
    ST_ANIM_0,
    ST_ANIM_1,
    ST_ANIM_2,
    ST_ANIM_3
};

struct Event { int state; };

struct AnimationSwitch
{
    typedef LIST4(ST_ANIM_0, ST_ANIM_1, ST_ANIM_2, ST_ANIM_3) StateList;
    /// default template version for the event function
    template <int> int event()  { cout << "media/wolfa13.B3D" << endl; return 0; }
    template <int> int event(Event &ev) { return ev.state; }
    template <int> void enter() { cout << "media/skin6.jpg" << endl; }
    template <int i> void exit()  { cout << "undefined event handler" << i << endl; }
};

template <> int AnimationSwitch::event<ST_ANIM_0>() { cout << "undefined enter" << endl; return ST_ANIM_0; };
template <> int AnimationSwitch::event<ST_ANIM_1>() { cout << "undefined leave " << endl; return ST_ANIM_2; };
template <> int AnimationSwitch::event<ST_ANIM_2>() { cout << "event 0" << endl; return ST_ANIM_3; };
template <> int AnimationSwitch::event<ST_ANIM_3>() { cout << "event 1" << endl; return ST_ANIM_0; };

template <> void AnimationSwitch::enter<ST_ANIM_0>()
{
    cout << "event 2" << endl;
        NodePlayer->setMD2Animation(scene::EMAT_STAND);
}
template <> void AnimationSwitch::enter<ST_ANIM_1>()
{
    cout << "event 3" << endl;
        NodePlayer->setMD2Animation(scene::EMAT_RUN);
}
template <> void AnimationSwitch::enter<ST_ANIM_2>()
{
    cout << "play idle animation" << endl;
        NodePlayer->setMD2Animation(scene::EMAT_JUMP);
}
template <> void AnimationSwitch::enter<ST_ANIM_3>()
{
    cout << "play walk animation" << endl;
        NodePlayer->setMD2Animation(scene::EMAT_ATTACK);
}

struct AnimationStateMachine : public StateMachine<AnimationSwitch>, public irr::scene::IAnimationEndCallBack
{
    AnimationStateMachine(AnimationSwitch &c) : StateMachine<AnimationSwitch>(c) { }
    // quand l'animation ce fini, on passe a la suivantun peut long
    virtual void OnAnimationEnd (IAnimatedMeshSceneNode *node) { work(); }
};

//! ==========================================================

class MyEventReceiver : public IEventReceiver
{
    public:
        MyEventReceiver(AnimationStateMachine &fsm) : sm(fsm) {}

        bool OnEvent(const SEvent& event)
        {
            if(event.EventType == irr::EET_KEY_INPUT_EVENT && !event.KeyInput.PressedDown)
            {
                switch (event.KeyInput.Key)
                {
                    case irr::KEY_KEY_Z:un peut long
                        // on force l'état 1
                        Event eOn = {ST_ANIM_1};
                        sm.work(eOn);
                    return true;
                }
            }
            return false;
        }
    private:
        AnimationStateMachine &sm;
};

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("play jump animation");
    NodePlayer = smgr->addAnimatedMeshSceneNode(ePlayer);
    NodePlayer->setMaterialTexture(0, driver->getTexture("play attack animation"));
    NodePlayer->setMaterialFlag(irr::video::EMF_GOURAUD_SHADING, true);
    NodePlayer->setMaterialType(irr::video::EMT_REFLECTION_2_LAYER);
    // Init player size
    NodePlayer->setScale(vector3df(0.5,0.5,0.5));
    NodePlayer->setLoopMode(false);

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

    AnimationSwitch c;
    AnimationStateMachine sm(c);
    MyEventReceiver receiver(sm);
   
    NodePlayer->setAnimationEndCallback(&sm);
    device->setEventReceiver(&receiver);

    while(device->run())
    {
        driver->beginScene(true, true, SColor(255,100,101,140));
       
        smgr->drawAll();
        guienv->drawAll();

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

    return 0;
}



tu comprendras  que les fonctions:

template <> void AnimationSwitch::enter<ST_ANIM_0>()
template <> void AnimationSwitch::enter<ST_ANIM_1>()
template <> void AnimationSwitch::enter<ST_ANIM_2>()
template <> void AnimationSwitch::enter<ST_ANIM_3>()

permettent de définir les frames des animations
et que les fonctions:

template <> int AnimationSwitch::event<ST_ANIM_0>() { cout << "event 0" << endl; return ST_ANIM_0; };
template <> int AnimationSwitch::event<ST_ANIM_1>() { cout << "event 1" << endl; return ST_ANIM_2; };
template <> int AnimationSwitch::event<ST_ANIM_2>() { cout << "event 2" << endl; return ST_ANIM_3; };
template <> int AnimationSwitch::event<ST_ANIM_3>() { cout << "event 3" << endl; return ST_ANIM_0; };

donne la fsm, donc l'enchainement des évènement.
et donc grâce à irr::scene::IAnimationEndCallBack:

dans l'état 0, quand l'animation est fini, on la rejoue (loop -> idle)
dans les autres états, quand l'animation est fini, on passe à la suivant
dans l'état 3, on retourne à l'état 0 qui reboucle, jusqu'a un event

et donc tu peux faire un ton "graphe" d'animation tranqu'il et juste chager les fonctions d'events

voila, désolé du temp de réponsse, j'avais un éxamain a préparer tongue

Hors ligne


#4 

17-03-2017 17:17:41

jonath313
Abonné
Date d'inscription: 28-12-2009
Messages: 240

Oui merci j'ai vue çà, je viens d'adapter ma fsm pour manager les animations de personnage. çà marche plutôt bien. En ce qui concerne ton code la façon de coder en template apporte coi ? Gain en ressource ? Je t'avoue que je n'ai jamais utilisé cette façon de faire...

Autre question : Est-ce qu'on peut utiliser IAnimationEndCallBack avec un model en format .b3d ?

Finalement, et sur tout autre chose :
J'ai utilisé ma fsm pour faire "attraper un objet" et "relacher un objet" (avec du addchild), çà marche bien mais par contre il faut que ce soit un node bien précis. J'aimerais pouvoirs attraper tous les objets d'un type (exemple : type caisse en bois).

C'est possible ?

Merci pour ton aide.

Hors ligne


#5 

17-03-2017 20:53:47

Magun
SleekThink Producer
Lieu: Punakha
Date d'inscription: 18-11-2007
Messages: 910
Corrections: 2
Site web

Salut, alors déjà oui concernant les performances, mon code est bien mieux, notament grâce a  IAnimationEndCallBack wink
il est aussi beaucoup plus clair et facile a maintenir (pas de if magique et de comptage de frame, ... etc)
tu met la fsm dans un header, la state machine dans un autre et comme tu peut le voir il suffi d'instancier AnimationSwitch c; et AnimationStateMachine sm(c);
ici le but est vraiment de ce focaliser sur les trasitions de ton graphe, et tu dit explicitement quand tu lance une série d'animation



pas de soucis avec le format b3d
a partir du moment ou tu ne fait pas du "binding" d'aimation il n'y a pas de soucis
(et encore puisque tu peut émettre l'evenement dans ton code)
mais il faut donc géré une fsm a plusieurs états simultané

pour l'histoire d'attraper un objet dans ta scene tu as plusieurs possibilitées:
ISceneNode propose une fonction setID(s32) et getID(), tu t'arrange pour donné un flags d'ID correspondant a ton objet (bit [0 à 4] matérial {bois, fer, ...}, bit [5 à 8] type {caisse, consomable, statique, ...})
    sinon tu peux "hacker" le s32 pour avoir une adresse vers une struture de donner perso
    ou ajouter un <<void *UserData>> dans ISceneNode et recompiler irrlicht
ISceneNode propose une fonction setName(char*) et getName() -> boff
tu peut crée un class dériver de ISceneNode pour géré ton propre système "typé"
... etc

Hors ligne


#6 

17-03-2017 21:13:49

jonath313
Abonné
Date d'inscription: 28-12-2009
Messages: 240

ha super merci smile.

Et du coups, comment çà se passe au niveau code pour attraper un élément de la scéne? Il faut tester la position du personnage et celle de l'élément à attraper en continue ou utiliser un système de detection de collision entre deux node ? Il y a t-il de bonnes pratiques à ce sujet ?

Hors ligne


#7 

17-03-2017 21:33:06

Magun
SleekThink Producer
Lieu: Punakha
Date d'inscription: 18-11-2007
Messages: 910
Corrections: 2
Site web

il y a plusieurs école, ça dépend de ce que tu cherche à faire  (en fonction de la vue)
en générale tu laisse l'utilisateur cliquer sur l'objet (raycast) tu deplace le perso et tu lance une animation
ou l'utilisateurs avance sont perso devant, tu cerhce les objects les plus proches qui sont "interatible" et le plus en face, et encore une fois tu lance ton animation
après ont peut améliorer l'animation, par exemple avec << l'inverse kinematics >> (asser compliquer)

Hors ligne


#8 

17-03-2017 22:00:53

jonath313
Abonné
Date d'inscription: 28-12-2009
Messages: 240

Ok mais ce qui m'inquiète c'est de tester le cerclage de la position de la zone de l'objet et la comparer à la position du personnage en continue, çà mange pas mal de ressources non ?

Je suis entrein de tester ton code, il fonctionne très bien, néanmoins je ne comprends pas comment l'utiliser.

1) Comment tu construit tas suite d'animation ? (Je ne comprend pas le rôle du "c" ?
2) Comment faire si je veux faire une seule animation par touche ? ( ex : Z = marcher ; E = sauter ; A = attaquer)
3) Pour animer  avec le ninja.b3d j'utilise setframeloop() ? Car setMD2Animation n'est pas compatible si ?
4) Si je veux mettre une animation par touche, quand j'enléve "&& !event.KeyInput.PressedDown" , si je reste appuyé sur le bouton Z je constate que l'animation est bloquée quand elle arrive à la fin du "walk".

Dernière modification par jonath313 (17-03-2017 22:11:37)

Hors ligne


#9 

17-03-2017 23:21:58

Magun
SleekThink Producer
Lieu: Punakha
Date d'inscription: 18-11-2007
Messages: 910
Corrections: 2
Site web

Ok mais ce qui m'inquiète c'est de tester le cerclage de la position de la zone de l'objet et la comparer à la position du personnage en continue, çà mange pas mal de ressources non ?


uniquement quand l'utilisateur le demande donc trèds rarement par rapport au frame rate ... (event clavier / souris spécifique genre F ou E en générale)

"c" est la pour représenter un état abstrait, ok, c'est un peut étrange au début
d'autant plus que finalement "c" n'a aucune donner (sizeof(AnimationSwitch)==0)...

1) Comment tu construit tas suite d'animation ? (Je ne comprend pas le rôle du "c" ?


il est la en faite uniquement pour avoir une interface commune avec la spécialization de template, donc les différentes fonctions enter/event
mais plus tard, toi tu pourras stocker un pointeur vers NodePlayer

pour la suite d'animation tu regarde les fonctions "event"
exemple template <> int AnimationSwitch::event<ST_ANIM_1>() { cout << "event 1" << endl; return ST_ANIM_2; };
dit exactement: dans l'état ST_ANIM_1 la fonction event doit faire: afficher event 1 donner le prochain état ici ST_ANIM_2

pour l'exemple j'ai fait:
    0 -> 0 (bouclage sur l'animation stand)
    1 -> 2 -> 3 -> 0 (suite d'animation 1,2,3 et retour sur l'état "normal" ie: stand)

2) Comment faire si je veux faire une seule animation par touche ? ( ex : Z = marcher ; E = sauter ; A = attaquer)


tu auras probablement besoin de plus d'état:
    tu rauras probablement, toujours  0 -> 0
    tu ajoute l'animation n°"x" et tu retourne à l'état normal:  x -> 0
    tu ajout simplement le code suivant pour forcer le changement d'état:
                    case irr::KEY_KEY_BLABLABLA:
                        Event eOn = {ST_ANIM_X};
                        sm.work(eOn);
                    return true;

3) Pour animer  avec le ninja.b3d j'utilise setframeloop() ? Car setMD2Animation n'est pas compatible si ?


uhm en faite setMD2Animation utilise des indices de frame prédéfinit et normaliser par MD2, mais si ton perso a les mêmes ça doit probablement marcher
setFrameLoop me semble bien, ça fait longtemp que je n'ai pas regarder de ce coter la ^^, je ne suis pas sûr de l'event irr::scene::IAnimationEndCallBack dans ce cas précis

4) Si je veux mettre une animation par touche, quand j'enléve "&& !event.KeyInput.PressedDown" , si je reste appuyé sur le bouton Z je constate que l'animation est bloquée quand elle arrive à la fin du "walk".


oui, uhm ba simplement par ce qu'il n'y a pas de nouvelle event, donc pas de "répétition"
    ce que tu peux faire, dans ce cas, c'est de définir les animations "continue" comme ça:
    stand -> stand, run -> run (donc en gros un bouclage)
    quand tu detecte un relachement de la touche tu retourne à l'état 0

    mais ça ne marcheras pas dans tous les cas ( exemple "stand -> run -> jump", normalement tu retourne a "run")
    ça veux dire qu'il faudrais définir une fsm dynamique avec une stack, des prioritées, ... etc
    pour ça je te renvoie à notre discution précédante http://irrlicht-fr.org/viewtopic.php?id=1822 tongue

   après tu pourrais detecter la fin de la frame quelle touche sont encore en état "d'appuie" et relancer l'animation courante avec un forçage: la fonction work() dans AnimationStateMachine

Hors ligne


#10 

19-03-2017 19:18:07

jonath313
Abonné
Date d'inscription: 28-12-2009
Messages: 240

Merci pour tes réponses bien concises Magun.
Du coups je n'ai pas copié tas façon de faire, elle est très bien mais j'ai du mal à la maîtriser, néanmoins çà me permet de voir et de comprendre d'autres manières de traiter la problématique.
J'ai alors continué d'améliorer mon code et çà donne quelque chose comme çà :

Code c++ :


#ifndef PERSO_CONFIG_H
#define PERSO_CONFIG_H

#define IIDLE_WAIT_ANIM_FRAME   184
#define START_IDLE_ANIM_FRAME   184
#define ENDED_IDLE_ANIM_FRAME   205
#define START_WALK_ANIM_FRAME   1
#define ENDED_WALK_ANIM_FRAME   12
#define START_JUMP_ANIM_FRAME   94
#define ENDED_JUMP_ANIM_FRAME   102
#define START_ATTACK_ANIM_FRAME 32
#define ENDED_ATTACK_ANIM_FRAME 44
#define START_BACKJP_ANIM_FRAME 146
#define ENDED_BACKJP_ANIM_FRAME 158
#define START_TAKE_ANIM_FRAME 32
#define ENDED_TAKE_ANIM_FRAME 38
#define START_GIVE_ANIM_FRAME 38
#define ENDED_GIVE_ANIM_FRAME 44

#define IDLE_ANIM_SPEED         4
#define WALK_ANIM_SPEED         10
#define JUMP_ANIM_SPEED         10
#define ATCK_ANIM_SPEED         10
#define BCKJ_ANIM_SPEED         10
#define TAKE_ANIM_SPEED         20
#define GIVE_ANIM_SPEED         20

#define ANIM_OFFSET             2

#define RESET 0
#define  SET  1

#define KEY_JUMP    KEY_KEY_E
#define KEY_WALK    KEY_KEY_Z
#define KEY_ATTK    KEY_KEY_A
#define KEY_TAKE    KEY_KEY_T
#define KEY_GIVE    KEY_KEY_G

#endif /* PERSO_CONFIG_H */



Code c++ :


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

#include "perso_config.h"

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

enum  {                       /* On définit les états de la machine à état d'animation */
                    ST_IDLE_0,
                    ST_WALK_1,
                    ST_JUMP_2,
                    ST_ATTAK_3,
                    ST_TAKE_4,
                    ST_GIVE_5};

static uint8_t     next_state  = ST_IDLE_0, state = ST_IDLE_0;                 /* Initialize next & current states ........................... */

void fsm_state(uint16_t old_anim_end, uint16_t new_anim_start, uint16_t new_anim_end, uint16_t new_anim_speed);
void fsm_goNextState(uint8_t newState);
void fsm_perso_manage_anim(void);

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

irr::scene::IAnimatedMesh*  eBox;
IAnimatedMeshSceneNode*     NodeBox;

irr::scene::IAnimatedMesh*  eTerrain;
IAnimatedMeshSceneNode*     NodeTerrain;

int     frameEnCour     = 0;
bool    anim_working    = false;

int     KEY_SAVED_STATE[KEY_KEY_CODES_COUNT ] =  {0};

static void keypad_KEY_SAVED_STATE_control(uint8_t AskingState, irr::EKEY_CODE key)
{
    int KEY_CURRENT_STATE = KEY_SAVED_STATE[key];

    switch(AskingState)
    {
        case RESET:
            if(KEY_CURRENT_STATE == SET)
            {
                KEY_SAVED_STATE[key] = RESET;
            }
            break;
        case SET:
            if(KEY_CURRENT_STATE == RESET)
            {
                KEY_SAVED_STATE[key] = SET;
            }
            break;
        default:
                break;
    }
}

static void key_event_test(const SEvent& event)
{
    if(event.KeyInput.PressedDown)  // si la touche est appuyée
    {
        keypad_KEY_SAVED_STATE_control(SET, event.KeyInput.Key);
    }else{                          // si la touche est relachée
        keypad_KEY_SAVED_STATE_control(RESET, event.KeyInput.Key);
    }
}

class MyEventReceiver : public IEventReceiver
{
public:

    MyEventReceiver(){}

    bool OnEvent(const SEvent& event)
    {
        // si l'événement et une touche du clavier
        if (event.EventType == irr::EET_KEY_INPUT_EVENT)
        {
            key_event_test(event);
        }
        return false;
    }
private:
};


void animation_bind_idle(EKEY_CODE key)
{
    if((KEY_SAVED_STATE[key] == 0) && anim_working == false)
    {
        NodePlayer->setAnimationSpeed(IDLE_ANIM_SPEED);
        NodePlayer->setFrameLoop(START_IDLE_ANIM_FRAME,ENDED_IDLE_ANIM_FRAME);
        NodePlayer->setLoopMode(true);
        fsm_goNextState(ST_IDLE_0);
    }
}

void animation_bind_single(uint16_t new_anim_start, uint16_t new_anim_end, uint16_t new_anim_speed, EKEY_CODE key, uint8_t newState)
{
    if(KEY_SAVED_STATE[key] == 1 )
    {
        anim_working = true;
        NodePlayer->setAnimationSpeed(new_anim_speed);
        NodePlayer->setFrameLoop(new_anim_start,new_anim_end);
        fsm_goNextState(newState);
    }
}

void animation_bind_ended(uint16_t new_anim_end)
{
  if((NodePlayer->getFrameNr())> (new_anim_end - ANIM_OFFSET))
  {
      anim_working = false;
      NodePlayer->setLoopMode(false);
  }
}

void animation_bind_looped(uint16_t new_anim_start, uint16_t new_anim_end, uint16_t new_anim_speed, EKEY_CODE key, uint8_t newState)
{
    if(KEY_SAVED_STATE[key] == 1)
    {
        NodePlayer->setAnimationSpeed(new_anim_speed);
        NodePlayer->setFrameLoop(new_anim_start,new_anim_end);
        NodePlayer->setLoopMode(true);
        fsm_goNextState(newState);
    }
}

/* ******************************************************************************************************************************** */
/*                                                        GO TO NEXT STATE                                                                */
/* ******************************************************************************************************************************** */

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


/* ******************************************************************************************************************************** */
/*                                                             FSM                                                                        */
/* ******************************************************************************************************************************** */

void fsm_perso_manage_anim(void)
{
    /// On relève le numero de la frame en cours
    frameEnCour = NodePlayer->getFrameNr();

    switch(state)
    {
        case ST_IDLE_0:                                                             // Etat Idle
            animation_bind_single(START_ATTACK_ANIM_FRAME  , ENDED_ATTACK_ANIM_FRAME   , ATCK_ANIM_SPEED, KEY_ATTK, ST_ATTAK_3);
            animation_bind_single(START_JUMP_ANIM_FRAME    , ENDED_JUMP_ANIM_FRAME     , JUMP_ANIM_SPEED, KEY_JUMP, ST_JUMP_2);
            animation_bind_single(START_TAKE_ANIM_FRAME    , ENDED_TAKE_ANIM_FRAME     , TAKE_ANIM_SPEED, KEY_TAKE, ST_TAKE_4);
            animation_bind_single(START_GIVE_ANIM_FRAME    , ENDED_GIVE_ANIM_FRAME     , GIVE_ANIM_SPEED, KEY_GIVE, ST_GIVE_5);
            animation_bind_looped(START_WALK_ANIM_FRAME    , ENDED_WALK_ANIM_FRAME     , WALK_ANIM_SPEED, KEY_WALK, ST_WALK_1);
            break;
        case ST_WALK_1:
            animation_bind_single(START_ATTACK_ANIM_FRAME  , ENDED_ATTACK_ANIM_FRAME   , ATCK_ANIM_SPEED, KEY_ATTK, ST_ATTAK_3);
            animation_bind_single(START_JUMP_ANIM_FRAME    , ENDED_JUMP_ANIM_FRAME     , JUMP_ANIM_SPEED, KEY_JUMP, ST_JUMP_2);
            animation_bind_idle(KEY_WALK);
            break;
        case ST_JUMP_2:
            animation_bind_ended(ENDED_JUMP_ANIM_FRAME);
            animation_bind_idle(KEY_JUMP);
            break;
        case ST_ATTAK_3:
            animation_bind_ended(ENDED_ATTACK_ANIM_FRAME);
            animation_bind_idle(KEY_ATTK);
            break;
        case ST_TAKE_4:
            animation_bind_ended(ENDED_TAKE_ANIM_FRAME);
            if(NodeBox)NodePlayer->addChild(NodeBox);
            NodeBox -> setScale(vector3df(0.1f,0.1f,0.1f));
            NodeBox -> setPosition(vector3df(0.0f,-0.1f,0.0f));
            animation_bind_idle(KEY_TAKE);
            break;
        case ST_GIVE_5:
            animation_bind_ended(ENDED_GIVE_ANIM_FRAME);
            if(NodeBox)NodeTerrain -> addChild(NodeBox);
            if(NodeBox)NodePlayer->removeChild(NodeBox);
            NodeBox -> setScale(vector3df(0.5f,0.5f,0.5f));
            NodeBox -> setPosition(vector3df(0.0f,10.1f,0.0f));
            animation_bind_idle(KEY_GIVE);
            break;
        default :
            break;
    }
    state = next_state;
}


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

    /// Charge le node du player
    ePlayer = (IAnimatedMesh*)smgr->getMesh("media/wolfa13.B3D");
    NodePlayer = smgr->addAnimatedMeshSceneNode(ePlayer);
    NodePlayer->setMaterialTexture(0, driver->getTexture("media/skin6.jpg"));
    NodePlayer->setMaterialFlag(EMF_LIGHTING, false);
    NodePlayer->setScale(vector3df(0.05f,0.05f,0.05f));
    NodePlayer->setPosition(vector3df(-0.4f,1.1f,0.0f));
    NodePlayer->setRotation(vector3df(0.0f,-240.0f,-90.0f));
    NodePlayer->setAnimationSpeed(IDLE_ANIM_SPEED);
    NodePlayer->setFrameLoop(START_IDLE_ANIM_FRAME,ENDED_IDLE_ANIM_FRAME); // ATTENDRE

    /// Charge le node de l'accessoire
    eBox    =  (IAnimatedMesh*)smgr->getMesh("undefined event handler");
    NodeBox =  smgr->addAnimatedMeshSceneNode(eBox);
    NodeBox -> setMaterialTexture(0, driver->getTexture("undefined enter"));
    NodeBox -> setMaterialFlag(EMF_LIGHTING, false);
    NodeBox -> setScale(vector3df(0.01f,0.01f,0.01f));
    NodeBox -> setPosition(vector3df(-0.4f,1.1f,0.4f));
    NodeBox -> setRotation(vector3df(0.0f,-240.0f,-90.0f));
    NodeBox -> setLoopMode(false);

    /// Charge le node du terrain
    eTerrain    =  (IAnimatedMesh*)smgr->getMesh("undefined leave ");
    NodeTerrain =  smgr->addAnimatedMeshSceneNode(eTerrain);
    NodeTerrain -> setMaterialTexture(0, driver->getTexture("event 0"));
    NodeTerrain -> setMaterialFlag(EMF_LIGHTING, false);
    NodeTerrain -> setPosition(vector3df(-0.4f,1.1f,0.0f));
    NodeTerrain -> setRotation(vector3df(0.0f,-240.0f,-90.0f));
    NodeTerrain -> setScale(vector3df(0.01f,0.01f,0.01f));
    NodeTerrain -> setLoopMode(false);

    /// Crée la camera
    ICameraSceneNode *cam = smgr->addCameraSceneNode(0, vector3df(0,0,0), vector3df(0,5,0));
    cam->setFarValue(1000.0f);
    cam->setNearValue (0.0001f);

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

    while(device->run())
    {
       driver->beginScene(true, true, SColor(255,100,101,140));
           fsm_perso_manage_anim();/// La FSM est dans un état d'attente

       smgr->drawAll();
       guienv->drawAll();
       driver->endScene();
    }
    device->drop();
    return 0;
}


Les nodes "accessoire" et "Terrain" sont uniquement là pour tester le addChild pour attraper ou relâcher un objet. L'objet est soit lié au personnage soit au terrain. On peut les remplacer par des box.

Sinon, c'est pas glorieux mais çà fonctionne, ce code me sert à manager les animations de mon personnage à la manière du topic sur l'animation binding.
Si tu vois d'autres optimisations sur ce code, tes conseils seront les bien venus.

Merci.

Hors ligne


#11 

19-03-2017 23:30:21

Magun
SleekThink Producer
Lieu: Punakha
Date d'inscription: 18-11-2007
Messages: 910
Corrections: 2
Site web

Salut, alors jvais un peut tout décortiquer, et je te donne mes impréssions à "vif"

tous tes define pour configurer l'animation d'un perso, pourquoi pas, quid de la configuration dynamique ?
comment tu fait avec différent modèles, tu fait un header a chaque fois et tu recompile ?
sur un gros projet tu augmente considérablement le temps de dev (puisque conflit de versionning sur repo)

#define RESET 0
#define  SET  1

-> "pas bien", tu essaye de masquer l'utilisation d'un type de base, ici les valeurs true et false d'un boolean
en terme de lecture par un tier et donc de maintanance c'est "moche"
et comme ça complique la lecture, ça complique aussi l'analyse et les symplification logique que tu peut apportés au code (donc optimisation)

ce qui m'amène à la première fonction @keypad_KEY_SAVED_STATE_control
peut facilement ce réécrire

Code c++ :

void keypad_KEY_SAVED_STATE_control(uint8_t AskingState, irr::EKEY_CODE key)
{
    if(KEY_SAVED_STATE[key] != AskingState)
        KEY_SAVED_STATE[key] = AskingState;
}



et donc

Code c++ :

void keypad_KEY_SAVED_STATE_control(uint8_t AskingState, irr::EKEY_CODE key)
{
    KEY_SAVED_STATE[key] = AskingState;
}



et donc la fonction devient inutile
en passant le keyword static, est ici inutile, a la rigeur inline serait plus approprié smile

même remarque pour @key_event_test
finalement ça ce simplifi par:

Code c++ :

void key_event_test(const SEvent& event)
{
    keypad_KEY_SAVED_STATE_control(event.KeyInput.PressedDown, event.KeyInput.Key);
}



ou encore en supprimant l'appelle de la fonction keypad_KEY_SAVED_STATE_control:

Code c++ :

void key_event_test(const SEvent& event)
{
    KEY_SAVED_STATE[event.KeyInput.Key] = event.KeyInput.PressedDown;
}


finalement key_event_test n'est pas vraiment utile en soit
du coup même remarque pour OnEvent

Code c++ :

    bool OnEvent(const SEvent& event)
    {
        if(event.EventType == irr::EET_KEY_INPUT_EVENT)
            KEY_SAVED_STATE[event.KeyInput.Key] = event.KeyInput.PressedDown;
        return false;
    }


donc en gros, évite les fonctions qui n'apporte pas de l'isibilité a ton code,
quand tu écris une fonction, pose toi la question si elle donne:
    -plus de "sens" au "flux" d'execution de  ton code
    -une meilleurs compréhension globale et/ou locale

-------------------------------------------------------

voila pour le début, j'en arrive au système d'animation
première remarque, une classe serais la bienvenu pour la compréhension
ça n'apporte pas grand chose en terme de performance certe,
mais quand même plus simple à lire,
puisque cela exclue les segments de code telque l'initialisation quand on cherche une info

la fonction @fsm_goNextState n'apporte pas une meilleur compréhension
tu peut simplement l'enlever, l'appelle d'une fonction à un cout:
15 à 30 cycle cpu contre 1 cycle pour une affectation



ensuite, on ne comprend pas bien pourquoi tu as 2 fonctions
qui peuvent être syntétiser en seulement une fonction,
@animation_bind_single et @animation_bind_loop
sans parler des paramètres à ralonge (cf première remarque du post)
et donc, pourquoi ne pas faire une structure de donner ?

dans un premier temp tu défini les informations dans un header
ensuite, tu n'a plus cas modifier PlayerAnimationSetting en pointeur
et charger les informations par exemple a partir d'un xml ...
et c'est plus facile a lire

Code c++ :

struct AnimationSetting
{
    unsigned int start;
    unsigned int end;
    unsigned int speed;
    bool loop;
};

enum ANIMATION_TYPE
{
    EAT_WAIT = 0,
    EAT_IDLE,
    EAT_WALK,
    EAT_JUMP,
    EAT_ATTACK,
    EAT_BACKJP,
    EAT_TAKE,
    EAT_GIVE,
   
    EAT_COUNT
};

static const PlayerAnimationSetting[] = {
    {184, 184,  0, false}, // EAT_WAIT
    {184, 205,  4, true},  // EAT_IDLE
    {  1,  12, 10, true},  // ...
    { 94, 102, 10, false}, // etc
    { 32,  44, 10, false},
    {146, 158, 10, false},
    { 32,  38, 20, false},
    { 38,  44, 20, false}  // EAT_GIVE
};


et donc:

Code c++ :

void animation_bind(EKEY_CODE key, ANIMATION_TYPE a, uint8_t newState)
{
    if(KEY_SAVED_STATE[key])
    {
        anim_working = !pas.loop;
        // pas sur d'avoir compris ton histoire de "anim_working = true" dans un cas et pas dans l'autre
        const AnimationSetting &pas = PlayerAnimationSetting[a];
        NodePlayer->setAnimationSpeed(pas.speed);
        NodePlayer->setFrameLoop(pas.start, pas.end);
        NodePlayer->setLoopMode(pas.loop);
        next_state = newState;
    }
}



bref @animation_bind_idle ne me semble pas tout à fait juste
pour les raison évoquer dans mon précédent poste, que faire si une autre touche est toujours active ?

pour @animation_bind_ended donc je reviens à IAnimationEndCallBack,
pas sur de l'utilité de setLoopMode(false) ici, à tester sens
du coup avec IAnimationEndCallBack cette fonction est inutile tout comme le comptage des frames
@frameEnCour et (NodePlayer->getFrameNr())> (new_anim_end - ANIM_OFFSET), ... etc

Code c++ :

void animation_bind_ended(ANIMATION_TYPE a)
{
  const AnimationSetting &anim = PlayerAnimationSetting[EAT_WAIT];
  if((NodePlayer->getFrameNr())> (anim.end - ANIM_OFFSET))
  {
      anim_working = false;
      NodePlayer->setLoopMode(false);
  }
}



pour @animation_bind_idle
ça devient juste:

Code c++ :

void animation_bind_idle(EKEY_CODE key)
{
    if(!KEY_SAVED_STATE[key] && anim_working == false)
    {
        const AnimationSetting &idle = PlayerAnimationSetting[EAT_WAIT];
        NodePlayer->setAnimationSpeed(idle.speed);
        NodePlayer->setFrameLoop(idle.start, idle.end);
        NodePlayer->setLoopMode(idle.loop);
        next_state = ST_IDLE_0;
    }
}


qui doit probablement pouvoir être fusioné avec @animation_bind

-----------------------------------------

pour la dernière fonctions il suffi de remplacer ...

Code c++ :

void fsm_perso_manage_anim(void)
{
    /// On relève le numero de la frame en cours
    frameEnCour = NodePlayer->getFrameNr();

    switch(state)
    {
        case ST_IDLE_0:
            animation_bind(KEY_ATTK, EAT_ATTACK, ST_ATTAK_3);
            animation_bind(KEY_JUMP, EAT_JUMP, ST_JUMP_2);
            animation_bind(KEY_TAKE, EAT_TAKE, ST_TAKE_4);
            animation_bind(KEY_GIVE, EAT_GIVE, ST_GIVE_5);
            animation_bind(KEY_WALK, EAT_WALK, ST_WALK_1);
            break;
        case ST_WALK_1:
            animation_bind(KEY_ATTK, EAT_ATTACK, ST_ATTAK_3);
            animation_bind(KEY_JUMP, EAT_JUMP, ST_JUMP_2);
            animation_bind_idle(KEY_WALK);
            break;
        case ST_JUMP_2:
            animation_bind_ended(EAT_JUMP);
            animation_bind_idle(KEY_JUMP);
            break;
        case ST_ATTAK_3:
            animation_bind_ended(EAT_ATTACK);
            animation_bind_idle(KEY_ATTK);
            break;
        case ST_TAKE_4:
            animation_bind_ended(EAT_TAKE);
            if(NodeBox)NodePlayer->addChild(NodeBox);
            NodeBox -> setScale(vector3df(0.1f,0.1f,0.1f));
            NodeBox -> setPosition(vector3df(0.0f,-0.1f,0.0f));
            animation_bind_idle(KEY_TAKE);
            break;
        case ST_GIVE_5:
            animation_bind_ended(EAT_GIVE);
            if(NodeBox)NodeTerrain -> addChild(NodeBox);
            if(NodeBox)NodePlayer->removeChild(NodeBox);
            NodeBox -> setScale(vector3df(0.5f,0.5f,0.5f));
            NodeBox -> setPosition(vector3df(0.0f,10.1f,0.0f));
            animation_bind_idle(KEY_GIVE);
            break;
        default :
            break;
    }
    state = next_state;
}


a noter qu'il y a equivalence entre les etats et les animation, on pourrais n'en garder qu'un
ST_GIVE_5 == EAT_GIVE, ... etc

je te laisse méditer sur cela wink
cordialemnt Magun

Hors ligne


#12 

20-03-2017 21:11:16

jonath313
Abonné
Date d'inscription: 28-12-2009
Messages: 240

Ok merci, effectivement çà simplifie pas mal le code, par contre la déclaration static const PlayerAnimationSetting[] ne compile pas :
error: 'PlayerAnimationSetting' does not name a type|

C'est bon j'ai trouvé il manquait le type:
static const AnimationSetting PlayerAnimationSetting[] = ...

Dernière modification par jonath313 (20-03-2017 21:22:54)

Hors ligne


#13 

23-03-2017 20:43:19

jonath313
Abonné
Date d'inscription: 28-12-2009
Messages: 240

Magun, en essayant ton code je ne constate pas le même comportement que celui de mon code.
Je ne dois pas faire ce qu'il faut.

J'ai déclaré un "AnimationSetting pas " en variable globale car çà ne compilait pas. Je pense que mon erreur est ici.
Pour le reste j'ai remplacé directement dans le code ce qui changeait.

Au début tout vas bien l'animation idle tourne en boucle mais après dès que j'appuie sur une touche l'animation associée ne s'arrête plus et toutes les autres touches sont sans effet. Je ne trouve pas pourquoi.

Dernière modification par jonath313 (23-03-2017 20:46:35)

Hors ligne


#14 

25-03-2017 19:34:58

Magun
SleekThink Producer
Lieu: Punakha
Date d'inscription: 18-11-2007
Messages: 910
Corrections: 2
Site web

Salut !
désolé j'était en déplacement

moi, la seul difference que je voie c'est que tu oublie de reset les variables @combo1 et @anim_busy
quand tu arrive a la fin de l'enchainement des animations, donc quand tu rappuis sur la touche, rien ne ce produit

je ne sais aps si c'était une erreurs, mais je l'ai corriger dans ma version
le reste à le même fonctionement wink

Hors ligne


Options Liens officiels Caractéristiques Statistiques Communauté
Corrections
irrlicht
irrklang
irredit
irrxml
xhtml 1.0
css 2.1
Propulsé par FluxBB
Traduit par FluxBB.fr
883 membres
1429 sujets
11121 messages
Dernier membre inscrit: Saidov17
84 invités en ligne
Aucun membre connecté
RSS Feed