Ponto V!

Home OpenGL O mecanismo de eventos da SDL
Vinícius Godoy de Mendonça
O mecanismo de eventos da SDLImprimir
Escrito por Vinícius Godoy de Mendonça

Eventos são mensagens que informam que algo aconteceu. São muito úteis para ações esporádicas, tais como o digitar de terminada tecla do teclado ou a exclusão de um objeto. Neste artigo, veremos sobre eventos externos, relacionados ao sistema de janelas e eventos gerados pelo usuário. Também veremos como a SDL encapsula esses eventos e quais estruturas prontas para manipulação de eventos existem.

Fila de eventos

Cada evento gerado no sistema operacional ou no ambiente de janelas é colocado numa fila. Existem vários tipos de eventos que são enfileirados, tais como pressionamento de teclas, redimensionamento de janelas ou movimentação do joystick. A SDL automaticamente lê uma série de eventos interessantes e os converte para seu próprio formato, multiplataforma, formando uma nova fila de eventos. Esses eventos podem ser lidos um-a-um e processados por nosso jogo.

O comando para ler eventos da fila é o SDL_PollEvent. Esse comando retorna 1 se um evento foi retirado da fila ou 0, se não havia nenhum evento pendente. Como parâmetro, ele recebe um ponteiro para uma estrutura chamada SDL_Event, e é essa a estrutura que guardará a informações à respeito do evento gerado.

Tipos de eventos

Quando desenfileiramos um evento, precisamos descobrir o seu tipo para que ele possa ser tratado adequadamente. É o campo type que realiza essa tarefa. A SDL possui 12 tipos de eventos:

  • SDL_ActiveEvent: Eventos de ativação estão relacionados ao fato da janela estar ou não ativa ou se ela está ou não recebendo eventos de mouse ou de teclado;
  • SDL_KeyboardEvent: Eventos de teclado. Indica se uma tecla foi pressionada ou solta. Informa em qual tecla isso ocorreu e que teclas especiais (como shift, alt ou ctrl) estavam pressionadas no momento em que isso ocorreu;
  • SDL_MouseMotionEvent: Movimentação do mouse. Informa a nova posição do mouse, bem como a quantidade de pixels movimentada desde a última posição. Também informa o estado dos botões enquanto isso ocorreu;
  • SDL_MouseButtonEvent: Ocorre quando um dos botões do mouse é clicado ou solto. A posição do mouse onde isso ocorreu também é reportada;
  • SDL_JoyAxisEvent, SDL_JoyBallEvent, SDL_JoyHatEvent, SDL_JoyButtonEvent: Eventos de joystick. Informam sobre movimentações no joystick, pressionamento de botões, etc. O tema joystick é um pouco mais complexo, pois envolve também detecção dos joysticks e suas capacidades. Por isso será tratado em detalhes num artigo futuro. Se você estiver muito curioso, pode dar uma olhada em seu funcionamento aqui.
  • SDL_QuitEvent: Informa que o usuário deseja sair da aplicação. Isso geralmente ocorre quando o “x” no canto superior direito da janela é clicado. Pode também ocorrer no caso do pressionamento das teclas de atalho para a mesma função, tal como CTRL+F4 no Windows.
  • SDL_SysWMEvent: Indica que um evento específico do sistema de janelas não tratado pela SDL ocorreu. Sua estrutura informa um ponteiro para esse evento. O formato dos dados desse ponteiro é específico do sistema operacional utilizado. Pode ser usado, por exemplo, para se implementar copy&paste na sua aplicação, mas dificilmente precisaremos dele.
  • SDL_ResizeEvent: Informa que a janela foi redimensionada. Informa também o novo tamanho da janela.
  • SDL_UserEvent: Uma estrutura que pode ser usada para armazenar eventos criados por você. Embora a SDL forneça esse mecanismo, veremos estruturas muito mais interessantes e orientadas a objetos em artigos futuros.

Revisitando o processEvents

Agora que já sabemos um pouco mais sobre os eventos, podemos rever o método processEvents do artigo anterior com um pouco mais de detalhes:

void processEvents()
{
    //Primeiro, precisamos criar uma estrutura
    //SDL_Event que guardará informações à respeito
    //do evento gerado
    SDL_Event event;

    //Enquanto temos eventos para processar
    //lemos o evento recebido
    while (SDL_PollEvent(&event) != 0)
    {
        //Verificamos o tipo do evento
        switch (event.type)
        {
            //Se é um evento requisitando a
            //saída da aplicação, ou seja,
            //o pressionamento do 'x' da janela
            case SDL_QUIT:
                exit(0); //Fechamos a aplicação
                break;
        }
    }
}

Eventos de teclas

Muito mais claro, não? Que tal alterarmos o processEvents() e o processLogics() para rodar o triângulo através do pressionar das setas? Os eventos de teclas ocorrem sempre que uma tecla é solta ou pressionada. Como queremos movimentar o triângulo enquanto a tecla estiver pressionada, precisaremos guardar o estado da tecla em duas variáveis auxiliares. Vamos declara-las no início do nosso programa:

//Controle do tempo
Uint32 lastTicks;
Uint32 ticks;

//Estado das teclas
bool leftPressed;
bool rightPressed;

Não se esqueça de definir o valor dessas variáveis para false antes do loop principal começar.Agora, vamos alterar o processEvents para definir o valor dessas variáveis sempre que as teclas forem soltas ou pressionadas.

void processEvents()
{
    SDL_Event event;

    while (SDL_PollEvent(&event) != 0)
    {
        switch (event.type)
        {
            case SDL_QUIT:
                exit(0); //Fechamos a aplicação
                break;

            //Testamos se a tecla foi pressionada
            case SDL_KEYDOWN:
                if(event.key.keysym.sym == SDLK_LEFT)
                    leftPressed = true;
                else if (event.key.keysym.sym == SDLK_RIGHT)
                    rightPressed = true;
                break;

            //Testamos se a tecla foi solta
            case SDL_KEYUP:
                if(event.key.keysym.sym == SDLK_LEFT)
                    leftPressed = false;
                else if (event.key.keysym.sym == SDLK_RIGHT)
                    rightPressed = false;
                break;
        }
    }
}

Como notamos no código, apenas saber que um evento de tecla ocorreu não é suficiente para sabermos para que direção girar o triângulo. Para isso, tivemos que ler informações do evento, que nos foram trazidas na estrutura key, do tipo SDL_KeyboardEvent. Uma das informações mais importantes carregadas por essa estrutura é o keysym.sym. Esse dado refere-se a qual tecla foi pressionada. Cada tecla da SDL é mapeada para uma constante, iniciada por SDLK_. Essa constante independe se a tecla é maiúscula ou minúscula. Você pode ver a lista completa de constantes clicando aqui. Agora, basta alterarmos o processLogics(). Somaremos ou subtrairemos a distância de rotação do triângulo de acordo com a tecla pressionada pelo usuário:

void processLogics()
{
    //Distância para girar (em graus) =
    //velocidade (0.180f) * tempo (ticks)
    float distance = 0.180f * ticks;

    //Está com a seta esquerda pressionada?
    if (leftPressed)
        degreesToRotate += distance;
    //Está com a seta direita pressionada?
    else if (rightPressed)
        degreesToRotate -= distance;
}

Fácil não? Você pode baixar o código completo aqui.

Questionando valores diretamente

Você notou que tivemos que criar uma variável booleana para cada tecla pressionada? Imagine se tivessemos que criar uma variável também para os botões do mouse. Seria trabalhoso se nosso jogo tivesse dezenas de teclas. Além disso, se nosso jogo tiver várias classes, é muito provável que essa informação fique duplicada. Os projetistas da SDL também notaram isso e resolveram facilitar o trabalho criando maneiras de questionarmos diretamente os estados do teclado e do mouse.

Podemos, a qualquer momento, ler o estado das teclas usando a função SDL_GetKeyState. Essa função nos retorna um vetor primitivo, preenchido com o estado de todas as teclas conhecidas pela SDL. Como parâmetro, ela aceita um ponteiro para um inteiro, que será preenchido com o tamanho do vetor de teclas. Esse ponteiro pode ser nulo se não desejarmos essa informação (e isso é o que normalmente ocorre). O retorno da função é o vetor propriamente dito.

Da mesma forma, existe a função SDL_GetMouseState e SDL_GetRelativeMouseState, que aceita como parâmetros variáveis que serão carregadas com a posição do mouse, e retorna o estado dos botões do mouse. A diferença das duas funções é que SDL_GetMouseState irá retornar as posições absolutas do mouse na tela, enquanto SDL_GetRelativeMouseState retorna apenas a diferença em x e y desde a última movimentação.

Alterar o programa do artigo anterior usando essas funções seria ainda mais fácil. Não precisaríamos criar qualquer variável global adicional, nem mexer no método processEvents(). O que precisaríamos era apenas alterar o processLogics() para:

void processLogics()
{
    //Distância para girar (em graus) =
    //velocidade (0.180f) * tempo (ticks)
    float distance = 0.180f * ticks;

    //Lemos o estado das teclas
    Uint8* keys = SDL_GetKeyState(NULL);

    //Está com a seta esquerda pressionada?
    if (keys[SDLK_LEFT])
        degreesToRotate += distance;
    //Está com a seta direita pressionada?
    else if (keys[SDLK_RIGHT])
        degreesToRotate -= distance;
}

O código completo dessa solução pode ser baixado aqui. É importante ressaltar que, os valores dessas funções é atualizado pelo SDL_PollEvent. Isso significa que, ainda que você só se utilize das funções SDL_GetXXX, deverá haver em algum ponto do seu código o while que faz a leitura da fila de eventos. Isso geralmente não é um problema, pois é sempre interessante tratar o evento SDL_Quit.

Resumindo

  • Você aprendeu que eventos são enfileirados, e que o método SDL_PollEvent é responsável por recolher esses eventos;
  • Que a estrutura SDL_Event é um union das diversas estruturas de eventos disponíveis, e é usada para descobrirmos informações sobre o evento ocorrido;
  • Quais os tipos de eventos que a SDL possui;
  • Que podemos usar os métodos SDL_GetKeyState e SDL_GetMouseState para questionar a SDL diretamente sobre o estado do mouse e teclado, o que facilita o tratamento desses dispositivos;

A prática leva a perfeição

Agora chegou a hora de você colocar a mão na massa. Altere qualquer um dos programas para:

  1. Fechar a janela quando a tecla ESC for pressionada. Faça isso alterando o método processEvents();
  2. Movimentar o triângulo para esquerda e direita também de acordo com os botões do mouse. Altere o método processLogics() usando o SDL_GetMouseState().

Links para documentação estão descritos acima. Você também pode consultar esse manual, caso ainda se sinta inseguro.


Comentários (18)
  • PotHix  - SDL Rocks
    avatar

    Æ!!

    SDL é realmente é muito legal, e é bom ver algumas descrições/exemplos dela em português, assim quem sabe a galera conhece melhor os pederes dessa biblioteca. :)

    Há braços

  • Mateus  - Movimentação
    avatar

    Tentei modificar o código com o comando glTranslatef, para movimentar o triangulo no eixo z.

    Se afastando e aproximando da tela com as teclas 'w' e 's', mais depois de apertar 1 ou 2 vezes o triangulo some da tela. Como faço para aumentar essa visão?

  • Vinícius Godoy de Mendonça
    avatar

    Vamos ver isso em alguns artigos, quando aprendermos a configurar a perspectiva da visão.

    Existe um comando para isso no OpenGL, mas ele não é tão simples de usar.

  • rogerio l leite  - Caso eu queira criar comandos para diversos evento
    avatar

    Oi Vinicius

    estou programando com a SDL, notei que este seu tutorial tá bem explicado, e como percebi, a SDL é capaz de devolver valores de estados das teclas pressionadas, mas se nenhuma tecla estiver pressionada?
    A idéia inicial é que, se o evento se tratar de um evento para movimentar ou criar ações para um personagem é que se nada estiver pressionado ele fique em modo de espera, e assim faça algum movimento aleatório( sem detalhes especificos ) como sentar e ficar lendo um livro( um exemplo de animação ).
    não sei se estou errado mas eu poderia colocar um "else" no bloco de comando "if( SDL_PollEvent( &evento ) )" ??

    Pergunta: bom, para efeito coloquei, mas tenho duvidas se isto é vantajoso..., afinal funcionou..heheh

    quem sabe isso pode dar ideia para um outro artigo

  • Bruno Crivelari Sanches
    avatar

    Olá Rogério,

    se nenhuma tecla é pressionada não é gerado evento algum (ao menos evento de teclas).

    Note que o Vini usa um while, que é para garantir que todos eventos sejam lidos num único frame, se você trocou para if o processamento dos seus eventos vai demorar um pouco mais e isso não é bom.

    Outra questão é que nem sempre os eventos gerados são teclas, então fazer um else no SDL_PollEvent para deixar o personagem numa animação de parado não faz muito sentido, pois se o jogador simplesmente mover o mouse isso vai afetar a animação, pois movimento do mouse também gera eventos. Além disso a SDL gera eventos não relacionadas a dispositivos de entrada, que vão acabar afetando as animações também.

    O ideal nessa situação é setar flags quando alguma tecla de movimentar for pressionada e limpar os flags quando as teclas forem soltas, assim na hora de decidir qual animação tocar você checa os flags, se nenhum estiver setado fique na animação de parado, senão toque a animação apropriada.

  • Vinícius Godoy de Mendonça
    avatar

    Complementando o que o Bruno disse.

    No lugar de uma flag, use uma variável long que vai somando o valor do tempo calculado nesse loop. Zere essa variável sempre que um evento de tecla for pressionado.

    Assim, você saberá exatamente quanto tempo passou enquanto o usuário não pressionou tecla nenhuma.

    Como ele falou, é importante manter o while e testar o tipo de evento. Outros eventos que não tem absolutamente nada a ver com entradas do jogador (como avisos de que sons terminaram de tocar, ou que a janela foi redimensionadas) também serão capturados pelo SDL_PollEvent.

  • Koolen  - Dica para dinamica de ensino
    avatar

    Vinícius, tudo bem!?

    Estou seguindo seu post de treinamento do codeblock, muito bacana estou facinado e pretendo virar a noite lendo todos, até ter uma noção agradavél.

    Queria dar uma dica, pelomenos na dinamica de ensino eu to copiando o código e colando na IDE, sempre que puder fazer isso, acho que seria legal, por que torna-se atrativo na dinamica do estudo e a cada momento faz com que estimule a continuar a leitura. Bom e so isso.

    Um abraço, até próximo post.

  • Vinícius Godoy de Mendonça
    avatar

    Valeu, obrigado. Nada impede você de copiar e colar o código em sua IDE favorita.

    Eu sempre prefiro explicar bem os conceitos, nem que quem leia precise as vezes trabalhar um pouco mais.

    Mas na maioria das vezes haverá um link para um código compilável.

  • Irving  - Muito massa seus tutorias
    avatar

    Parabens pelos seus tutorias. Estão muito legais eles.

  • Vinícius Godoy de Mendonça  - :)
    avatar

    Valeu Irving. :)

  • Rafael Vieira Braga  - Dúvida com eventos
    avatar

    Coloquei minha função de ler eventos num while, porém, tenho que ficar apertando múltiplas vezes os botões(teclado ou mouse) para ocorrer o que quero(adicionar ou subtrair valores de variáveis que controlam angulo e posição). Estou fazendo algo errado? Tem como manter um estado das teclas ou botões?

  • Vinícius Godoy de Mendonça
    avatar

    Você criou as variáveis booleanas, como o artigo explica?

  • Rafael Vieira Braga  - Dúvida com eventos
    avatar

    :0
    Se burrice matasse, não atentei o fato de ter de usar as varáveis booleanas e os estados "DOWN" e "UP" dos botões e teclas... vou testas aqui. Mais uma vez, MUITO obrigado, Ó sábio GURU!!! huhauahua
    Brigadão mesmo.
    Até a próxima!

  • Silas Silva Brasil  - Dúvida com o printf e std::cout
    avatar

    Vinícius, td bem?

    Estou com uma dúvida muito simples, as funções printf e std::cout funcionam quando se usa SDL, pois tentei fazer uns testes para ver se aparecia algo na tela mas não aconteceu. Por que?

    Obrigado e Parabéns vc tem posts excelentes.

    Outra pergunta, queria fazer um programinha para manipular imagens, assim sendo, o program terá barra de menu, barra de ferramentas, etc. O senhor me aconselharia usar SDL ou outra biblioteca?

  • Anônimo
    avatar

    cout e printf não funcionam pois são rotinas feitas para saídas em terminais de texto, uma janela SDL é em modo gráfico.

    Para manipular imagens com botões, menus, etc. Não vejo muita vantagem em usar SDL. Se for algo que precisa ser feito em C++, use Qt.

  • Silas Silva Brasil
    avatar

    Oh meu amigo, muito obrigado pela dica, eu vi no SDL e C++ uma oportunidade de me aprofundar em uma nova linguagem, no entanto, vou ter de continuar no Java mesmo pq o projeto no qual estou engajado não obriga o uso de C++ e tbm, a parte de interface gráfica com o usuário não é o mais importante.

    Assim sendo, vou continuar a ler os posts sobre Java2D.


    Abração e muito obrigado!

  • Elvis Platini
    avatar

    Boa tarde, estou usando SDL no meu projeto de iniciação cientifica, estou tendo certa dificuldade para selecionar o arquivo que vou tratar, pelo caminho com o LoadBMP da tudo certo mas queria abrir uma janela do explorer para isso, e nunca fiz isso em C e não encontro função no SDL que o faça :/
    Obrigado pela atenção :)

  • ViniGodoy
    avatar

    Não existe uma opção para fazer isso na SDL. Você tem três opções:

    a) Fazer sua aplicação trabalhar por linha de comando;

    b) Usar uma chamada específica do seu sistema operacional, no caso do Windows a GetOpenFileName: http://msdn.microsoft.com/en-us/library/windows/desktop/ms646927(v=vs. 85).aspx

    c) Substituir a SDL por outra API gráfica mais completa, como o QT.

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