Ponto V!

Home Ogre 3D Aprimorando o protótipo de Pac-Man II
Jonathan Ohara de Araujo
Aprimorando o protótipo de Pac-Man IIImprimir
Escrito por Jonathan Araujo

No artigo anterior, mostrei um pouco mais sobre o SDKTrays. Nesse artigo vamos aprimorar novamente o protótipo PacMan adicionando mais alguns elementos essenciais para futuros artigos e inserindo dessa vez animação para os personagens. Como esse artigo é uma continuação do protótipo de Pac-Man é essencial você ter lido o artigo Aprimorando o protótipo de Pac-Man I.

clip_image002

Reestruturando o Protótipo

Nesse protótipo foram criados alguns novos arquivos para representar os objetos fantasma e Pac Man, além disso foi criado uma classe que essas duas estendem que é a classe BaseCharacter

Classe BaseCharacter

Essa classe tem alguns atributos que são compartilhados entre a classe Fantasma e a classe Pac Man. Veja o trecho de código a seguir:

class BaseCharacter 
{
    public:
        BaseCharacter();
        
        virtual ~BaseCharacter(void);

        void iniciarAnimacao();
        void pararAnimacao();
        void animar( Ogre::Real time );

        Ogre::SceneNode * getNo();
        void setNo( Ogre::SceneNode * _no );

        Ogre::Entity * getEntidade();
        void setEntidade( Ogre::Entity * _entidade);

        bool isGhost();
        void setGhost( bool _ghost );

        bool isPacMan();
        void setPacMan( bool _pacMan );

    protected:
        Ogre::SceneNode * no;
        Ogre::Entity * entidade;

        bool ghost;
        bool pacMan;
};

Nessa classe temos 3 métodos que serão responsáveis pela animação do personagem:

void iniciarAnimacao();

void pararAnimacao();

void animar( Ogre::Real time );

Vou explicar mais detalhadamente o que elas fazem no trecho Animação nesse artigo.

Após isso temos apenas os getters and setters para entidade e nós.

Classe Fantasma

Na classe fantasma as diferenças são:

  • Criado Enum para definir a cor do fantasma e um método get e set para esse atributo;
  • Criado um método para gerar o movimento randômico (o código apenas foi movido da classe tutorial para cá). Veja o trecho a seguir:
enum CorFantasma{ AZUL, LARANJA, ROSA, VERMELHO };

CorFantasma getCorFantasma();

void setCorFantasma( CorFantasma _corFantasma );

void gerarMovimento();
Classe Cenário

Criamos a classe Cenário. Veja o trecho a seguir:

class Cenario 
{
    private:
        static Square cenarioSquare[LINHAS][COLUNAS];
        static char cenario[LINHAS][COLUNAS];
        static Ogre::SceneNode* cenarioNode;
        static std::vector fantasmas;

        static Square criarSquare(int x, int y, Ogre::String* sceneNodeName, Ogre::String* entityName);

        static void destruirFantasmas();

    public:

        static void updateObjectPosition(BaseCharacter * baseCharacter, int oldX, int oldY, int newX, int newY);

        static const Square &getSquare(int row, int col);

        static PacMan *carregar(Ogre::SceneManager &sceneManager);

        static void animarObjetos(Ogre::Real time);

        static void gerarMovimento();
};

Nessa classe temos vários métodos e atributos estáticos. Vamos a eles começando pelos métodos e atributos privados:

static Square cenarioSquare[LINHAS][COLUNAS];

static char cenario[LINHAS][COLUNAS];

static Ogre::SceneNode* cenarioNode;

static std::vector fantasmas;

static Square criarSquare(int x, int y, Ogre::String* sceneNodeName, Ogre::String* entityName);

static void destruirFantasmas();

Todos os métodos exceto o destruir fantasma foram métodos movidos da classe tutorial para essa nova classe.

Já o método destruirFantasma limpa os objetos fantasmas. Veja o trecho a seguir:

std::vector Cenario::fantasmas;

...

...

...

void Cenario::destruirFantasmas()
{
    for(int i = 0, num = fantasmas.size(); i  < num; ++i)
    {
        delete fantasmas[i];
    }

    fantasmas.clear();
}

Agora os métodos públicos:

static void updateObjectPosition(BaseCharacter * baseCharacter, int oldX, int oldY, int newX, int newY);

static const Square &getSquare(int row, int col);

static PacMan *carregar(Ogre::SceneManager &sceneManager);

static void animarObjetos(Ogre::Real time);

static void gerarMovimento();  

Sendo que estes são responsáveis por:

  • O primeiro método é o updateObjectPosition que agora fica responsável por mover os objetos dentro da nossa matriz de cenário, ou seja, esse método é chamado sempre que um objeto é movido atualizando a matriz do cenário.
  • Carregar: é outro método movido da classe tutorial, onde o vetor de char é transformado no vetor de Square e onde são carregados os objetos;
  • animarObjetos: esse método chama o método animar de todos os fantasmas;
  • gerarMovimento: esse método chama o método gerarMovimento de todos os fantasmas.
Classe Principal

Algumas coisas mudaram na classe principal. No antigo anterior as entidade e nós dos fantasmas e Pac Man estavam nessa classe, agora foram movidas para a classe BaseCharacter veja as modificações dos atributos privados da classe.

Antes:

Ogre::SceneNode *cenarioNode,
                *pacManNode,
                *fantasmaVermelhoNode,
                *fantasmaRosaNode,
                *fantasmaLaranjaNode,
                *fantasmaAzulNode;

Agora:

Ogre::SceneNode *cenarioNode;
PacMan * pacMan;

Fantasma    *fantasmaAzul,
            *fantasmaLaranja,
            *fantasmaRosa,
            *fantasmaVermelho;

No resto da classe foram deletados os métodos movidos citados anteriormente pela chamada do método.

Foi feita uma modificação também no método frameRenderingQueued que será melhor explicada abaixo.

Animação

Foi adicionado aos modelos 3D um esqueleto bem simples com 1 ou dois bones (ossos), esses arquivos no formato .blender estão disponíveis em pastaDoProjeto/work/media/models/blender.

Nenhuma modificação é feita na leitura dos objetos, todas as propriedades de animação estão contidas no objeto entidade.

Vamos ver a implementação dos 3 métodos responsáveis pela animação citadas anteriormente:

void BaseCharacter::iniciarAnimacao()
{
    entidade->getAnimationState("idle")->setTimePosition(0);
    entidade->getAnimationState("idle")->setEnabled(true);
    entidade->getAnimationState("idle")->setLoop(true);
}

//-------------------------------------------------------------------------------

void BaseCharacter::pararAnimacao()
{
    entidade->getAnimationState("idle")->setEnabled(false);
}

//-------------------------------------------------------------------------------

void BaseCharacter::animar( Ogre::Real time )
{
    entidade->getAnimationState("idle")->addTime( time );
}

Para pegar a animação usamos o método getAnimationState( nomeDaAnimacao ) onde o argumento é uma Ogre::String com o nome da animação. Note que uma entidade pode ter N animações, pode-se também pegar todas elas através do método getAllAnimationStates().

Fazendo uma analogia a animação ela funciona parecida com um vídeo. Toda vez que queremos ver o vídeo colocamos ele no início:

entidade->getAnimationState("idle")->setTimePosition(0);

Após isso damos “play” no vídeo para começar a animação:

entidade->getAnimationState("idle")->setEnabled(true);

E por últimos ligamos o “repeat” para a animação ser continua:

entidade->getAnimationState("idle")->setLoop(true);

Para parar a animação setamos o enabled como false:

entidade->getAnimationState("idle")->setEnabled(false);

A única diferença de um vídeo é que na animação precisamos a cada frame “alimentar” a animação, se a animação não for “alimentada” a animação entrará em pausa. Veja o trecho de código a seguir:

entidade->getAnimationState("idle")->addTime( time );

Note que temos um argumento nesse método que é o tempo que “alimentaremos” a animação. Aqui podemos modificar essa variável time aumentando ou diminuindo-a fazendo efeitos como Fast Foward ou Slow Motion.

Esse método animar é chamado na classe principal onde temos o método frameRenderingQueued assim como o timeSinceLastFrame. Veja como ficou o método frameRendering Queued:

// Método da Interface FrameListener, é chamado no Loop Principal
bool frameRenderingQueued(const Ogre::FrameEvent& evt)
{
    if (desligar) return false;

    pacMan->animar( evt.timeSinceLastFrame );

    fantasmaAzul->animar( evt.timeSinceLastFrame );
    fantasmaLaranja->animar( evt.timeSinceLastFrame );
    fantasmaRosa->animar( evt.timeSinceLastFrame );
    fantasmaVermelho->animar( evt.timeSinceLastFrame );

    // Parar o programa caso a janela seja fechada
    if(mWindow->isClosed())
        return false;

    // Caputra entrada de teclado
    mKeyboard->capture();

    // Caputra entrada de Mouse
    mMouse->capture();

    return true;
}

Conclusão

Nesse artigo aprimoramos um pouco mais o protótipo. A principal modificação é que agora os personagens fantasma e PacMan do protótipo tem animação.

No próximo artigo mostrarei mais sobre a exportação de animações no blender que deixei em aberto no artigo Criando conteúdo para Ogre no Blender além de mostrar algumas dicas de como trabalhar com múltiplas animações no mesmo objeto.

download


Comentários (3)
  • rui davi  - AI
    avatar

    ola , eu to com uma duvida em python , gostaria de saber se com python e possivel simular AI (INTELIGENCIA ARTIFICIAL) , do tipo que quando o boneco passa perto do inimigo ele começa a atacar ?

  • Vinícius Godoy de Mendonça
    avatar

    Isso é possível em qualquer linguagem de programação. A IA está mais nos algorítmos utilizados, não na linguagem.

  • Vitor Almeida da Silva  - Parabéns
    avatar

    Parabéns pelo artigo Jonathan , é sempre útil artigos que explicam um jogo completo (e são os que mais dão trabalho para fazer).

    A propósito, estou desenvolvendo um Pac-Man (com um IA bem elaborada) em Unity 3d e gostaria de pedir para utilizar os seus modelos (com o devido crédito é claro) se for possível.

    Obrigado (independente da resposta).

Escrever um comentário
Your Contact Details:
Gravatar enabled
Comentário:
[b] [i] [u] [url] [quote] [code] [img]   
:angry::0:confused::cheer:B):evil::silly::dry::lol::kiss::D:pinch::(:shock:
:X:side::):P:unsure::woohoo::huh::whistle:;):S:!::?::idea::arrow:
Security
Por favor coloque o código anti-spam que você lê na imagem.
LAST_UPDATED2  

Busca

Linguagens

Twitter