Criando uma janela SDL

2 12 2007

Entendendo a SDL

Antes de entrarmos no código usando a SDL, vamos entender um pouco de seus conceitos. A SDL (Simple Directmedia Layer) é uma biblioteca multi-plataforma criada para facilitar a construção de aplicações envolvendo multimídia. A biblioteca é formada de diversos subsistemas, que fornecem serviços como:

  • Inicialização e finalização: É normal que questionemos o sistema operacional a respeito dos recursos que temos disponíveis para nossos jogos. A SDL simplifica esse processo ao fornecer uma rotina chamada SDL_Init, capaz de inicializar cada módulo específico a ser utilizado. Ela mesmo pergunta ao sistema operacional se os recursos necessários estão ou não disponíveis e nos retorna um código de erro simples de ser tratado;
  • Manipulação do vídeo: A SDL permite que trabalhemos numa janela pixel-a-pixel ou utilizando OpenGL. Este blog trabalhará com a segunda opção. Ela fornece uma maneira fácil e multiplataforma de criarmos a janela do jogo, ou iniciarmos o modo fullscreen. Também facilita a integração com o hardware;
  • Manipulação de imagens: A SDL fornece mecanismos para carregar imagens e manipula-las pixel-a-pixel. O formato suportado nativamente é o .bmp, mas com a biblioteca SDL_Image também podemos carregar a maior parte dos formatos padrão, incluindo .jpg, .gif e .png, com ou sem canal alpha;
  • Mecanismo de eventos: A SDL fornece um mecanismo de eventos simples para tratar diversas formas de entrada do usuário: isso inclui: eventos na janela, ou movimentação com mouse, teclado e joystick;
  • Efeitos sonoros: Um de seus subsistemas fornece módulos para trabalhar com sons nos formados .mid, .mod e .wav. Adicionalmente a SDL_Mixer ainda suporta diversos outros formatos, incluindo .mp3 e .ogg;
  • Temporização: A temporização é essencial em qualquer aplicação multimídia. Com ela conseguimos criar jogos que rodem na mesma velocidade, seja em computadores lentos ou mais rápidos.
  • Acesso a redes: A SDL permite a inicialização e o uso de sockets de maneira simples, facilitando a criação de jogos em rede através do pacote adicional SDL_Net;
  • Multi-threading: Permite disparar multiplas linhas de execução e criar blocos de sincronização de maneira multi-plataforma.

O main

Para construir uma aplicação de janelas é necessário criar um bloco main dependente do ambiente escolhido. No caso do windows, criaríamos o WinMain da seguinte forma:

int APIENTRY WinMain(HINSTANCE hinst, HINSTANCE hinstPrev,
   LPSTR lpCmdLine, int nCmdShow)

Usando a SDL não precisamos nos preocupar com isso. Ela é quem toma conta de fazer as chamadas específicas. Então, para nós, resta apenas a tarefa de declarar um main padrão, com o seguinte formato:

int main(int argc, char* argv[])

Embora o C++ forneça outras alternativas para a assinatura do main, essa é a forma recomendada pela SDL. Se você realmente precisar usar o WinMain, consulte o arquivo /src/main/win32/SDL_main.c para ver como a SDL deve ser inicializada. Isso é um tema avançado e, para os exemplos desse blog, isso não será necessário.Inicialização e finalização

Cada módulo da SDL deve ser inicializado com o comando SDL_Init antes que possa ser utilizado. O comando recebe um argumento inteiro, que é composto utilizando o operador OU binário | com as seguintes flags:

A sdl ainda possui os seguintes flags especiais:

  • SDL_INIT_EVERYTHING: Uma maneira rápida de inicializar todos os subsistemas acima.
  • SDL_INIT_NOPARACHUTE: A inicialização “sem paraquedas” faz com que a SDL ignore todas as mensagens de erros críticos do sistema. Embora seja curioso, não utilizaremos esse flag.
  • SDL_INIT_EVENTTHREAD: Não documentado. Também não é necessário utiliza-lo.

Tipicamente, um jogo grande irá fazer a inicialização com SDL_INIT_EVERYTHING. Para os primeiros exemplos, podemos inicializar os subsistemas apenas de timer e vídeo, como no exemplo abaixo:

SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO);

Os módulos são finalizados através da função SDL_Quit. Você pode simplificar o seu código e colocar a SDL_Quit como parâmetro na função atexit(), assim ela será chamada sempre que o comando exit for dado. A declaração fica assim:

atexit(SDL_Quit);

Abrindo uma janela com a SDLUma vez que o módulo de vídeo está no ar, o próximo passo é definir qual é o modo de vídeo que utilizaremos para a aplicação. A função que permite que façamos isso é a SDL_SetVideoMode e seus parâmetros são: largura, altura, número de bits por pixel (também chamado de bpp ou depth) e, novamente, um inteiro com flags. As mais importantes e comumente usadas são:

  • SDL_SWSURFACE: Indica que queremos criar uma janela que não dependa de hardware de aceleração de vídeo;
  • SDL_HWSURFACE: Indica que precisamos de uma janela com aceleração de vídeo. Essa flag pode ser passada em conjunto com a anterior para suportarmos os dois modos;
  • SDL_ANYFORMAT: Quando o número de bits-por-pixel não é suportado, a SDL tenta emula-lo. Passando essa flag, você informa a SDL que você não se incomoda se ela simplesmente usar o número suportado no lugar;
  • SDL_DOUBLEBUF: Ativa o double-buffering e page-flipping. Esse recurso só estará completamente disponível se a SDL estiver usando uma superfície de hardware, criada ao passar também a flag SDL_HWSURFACE. Caso contrário, a SDL não fará o page-flipping.
  • SDL_NOFRAME: Retira a decoração da tela (barra de título, botões, etc).
  • SDL_FULLSCREEN: Inicializa em tela cheia. A altura e largura passadas nos parâmetros anteriores se referirão a resolução da tela. Se a resolução não puder ser atingida, a SDL utilizará uma resolução menor mais próxima da desejada, mas a janela ficará centralizada numa tela preta. Essa flag ativa automaticamente a SDL_NOFRAME;
  • SDL_OPENGL: Inicializa a janela com OpenGL;

A função SDL_SetVideoMode retorna um ponteiro para a superfícia de desenho da tela, do tipo SDL_Surface. Na SDL, qualquer elemento que possa ser desenhado ou receber desenhos é chamado de superfície (surface).

Que tal colocar tudo isso junto num único programa?

#include "SDL.h"

#include <stdexcept>
#include <iostream>

SDL_Surface* window;

void setup(int width, int height, int bpp, bool fullscreen)
{
    //Inicializamos o subsistema de video.
    if (SDL_Init(SDL_INIT_VIDEO) < 0)
        throw std::runtime_error(SDL_GetError());

    //Definimos as flags.
    int flags = SDL_SWSURFACE;
    if (fullscreen)
        flags != SDL_FULLSCREEN;

    //Tentamos criar a janela
    window = SDL_SetVideoMode(width, height, bpp, flags);

    //Sem sucesso? Lançamos uma exceção com o erro.
    if (window == NULL)
        throw std::runtime_error(SDL_GetError());

    //Configuramos a função de finalização da SDL
    atexit (SDL_Quit);
}

/** Espera o usuário pressionar o x da janela. */
void processEvents()
{
    SDL_Event event;

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

int main(int argc,char* argv[])
{
    try
    {
        setup(640, 480, 8, false); //Faz a mágica acontecer

        while (true)
            processEvents();
    }
    catch (std::exception &e)
    {
        std::cout << "Error: " << e.what();
        exit(1);
    }
}

Rodando esse exemplo, você deve ver uma janelinha abrindo na tela. Não se preocupe com o processamento de eventos por enquanto. Ele foi usado aqui apenas para que você conseguisse ver a janela (caso contrário, ela fecharia logo que abrisse). No próximo artigo, configurar a janela para uso com OpenGL. No futuro, organizaremos tudo numa classe, para reutilizarmos em qualquer jogo que quisermos.Veja também


Ações

Informações

16 respostas

2 12 2007
Inocêncio (Grinvon)

Não sei nada de SDL, mas fiquei interessado. Apenas estudei um pouquinho de Allegro, mas isso faz muito tempo. Seu blog me motivou a estudar um pouco mais de programação de jogos e assim de forma casual, produzir quem sabe algum joguinho legal. Grande abraço Vini e continui com esse blog muito bom.

2 12 2007
vinigodoy

Obrigado! Eu não cheguei a usar o Allegro mas, pelo que já li em blogs de amigos, parece muito interessante.

A SDL foi usada alguns jogos muito famosos como Worms 3D.

2 12 2007
Diogo_RBG

Bem explicadinho, e por isso mesmo pintou umas dúvidas.
* Não sabia que eu tinha que combinar SDL_HWSURFACE | SDL_DOUBLEBUF para que o doublebuf pudesse funcionar, e tbm não sabia que SDL_HWSURFACE | SDL_SWSURFACE funcionavam juntas !
* SDL_Quit repassada para atexit será chamada se exit for chamada ou tbm quando o programa encerrar ?!
Este é o primeiro exemplo em C++ que vejo !

3 12 2007
vinigodoy

Será chamada quando a aplicação terminar também. A função atexit() é chamada sempre que há um termino normal do C++, seja através da função exit ou da finalização do bloco main.

Uma curiosidade. O C++ suporta até 32 funções passadas para o atexit. Para mais informações:
http://www.cplusplus.com/reference/clibrary/cstdlib/atexit.html

Quanto ao exemplo ser em C++. Bem, espero poder trazer um pouco de estilo C++ aqui para o blog. Muita coisa mudou na forma como se programa C++ nos últimos anos. O uso da STL, vários estilos foram melhorados, outros abandonados. Espero poder passar um pouquinho disso por aqui. :)

5 12 2007
Guedes

Olá Vinícius, tem alguns pontos que para mim, estão equivocadas:

“Acesso a redes: A SDL permite a inicialização e o uso de sockets de maneira simples…”.
O SDL não trabalha nativamente com rede, é necessário a biblioteca auxiliar SDL_net.

int APIENTRY WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpCmdLine, int nCmdShow)

“Usando a SDL não precisamos nos preocupar com isso. Ela é quem toma conta de fazer as chamadas específicas. Então, para nós, resta apenas a tarefa de declarar um main padrão”.
O main() padrão do Windows não direciona a saída padrão para a tela, direciona para o arquivo stdout.txt, geralmente os iniciantes precisam visualizar instântaneamente o resultado da saída, para resolver esse problema, usa-se o WinMain().

“Embora o C++ forneça outras alternativas para a assinatura do main, essa é a forma recomendada (para não dizer exigida) pela SDL.”. Na documentação oficial disse “You should be using main() instead of WinMain()…”, ou seja, ‘você deveria estar usando o main() ao invés de WinMain()’, não existe obrigação aqui, é apenas uma sugestão do fabricante, além do mais, existe outra citação: “you need to use WinMain(), take a look at the SDL source code in src/main/win32/SDL_main.c”, ou seja, ’se você necessita usar o WinMain(), de uma olhada no codigo fonte SDL em src/main/win32/SDL_main.c”, o próprio fabricante diz que é possível necessitar do WinMain().

Referência: http://www.libsdl.org/faq.php?action=listentries&category=4#48

5 12 2007
vinigodoy

Sua dica é realmente ótima, obrigado! Se alguém realmente necessitar por algum motivo do WinMain, é uma boa conferir esse arquivo citado na documentação. Fica bom o lembrete de que o que eu falei não é uma imposição, mas uma recomendação que deve atender ao caso geral.

Vou fazer as devidas correções no blog. :)

6 12 2007
skhaz

Otimo artigo, muito bem explicado, parabéns :D

8 12 2007
AvcF

Acompanho seus textos faz algum tempo, muito bons. Publiquei ontem um post que creio que possa ajudar o pessoal que quer desenvolver games de forma independente (http://gamehall.uol.com.br/blogs/loadingtime/2007/12/07/mais-um-pouco-de-game-design-%e2%80%93-planejamento-de-jogo/). Solto a segunda parte dele em breve.Falou!

8 12 2007
vinigodoy

Eu já tinha lido seu texto, pois tenho seu blog no meu reader. Realmente muito bom! Recomendo a todos os leitores!

21 02 2008
MarceloJava

Vini, desculpe estar mandando aquí, porém os e-mails que estou te enviando estão voltando.
Não conseguí compilar esse tutorial, criei o ambiente certinho como vc falou mais na hora de compilar, o arquivo executável não é gerado, qq outro programa que crio em C++ no codeblock gera normal

abaixo o log:
Project : Console application

Compiler : GNU GCC Compiler (called directly)

Directory : C:\DevJogos\project\

——————————————————————————–

Switching to target: default

Compiling: main.cpp

Linking console executable: C:\DevJogos\project\console.exe

mingw32-g++.exe: –mwindows: No such file or directory

Process terminated with status 1 (0 minutes, 1 seconds)

0 errors, 0 warnings

outra forma, deletei o -mwindows no codeblock e joguei o SDL.dll no mesmo diretório (também solicitou ela e não encontrou), mesmo assim ocorre a exception e o arquivo stdout.txt é gerado sem a exception escrita.
Obrigado, pode apagar do blog depois se preferir.

21 02 2008
vinigodoy

Isso geralmente está associado a configuração do compilador.
Vá em settings, compiler&debbuger, clique na aba toolchain executables e verifique se o diretório do compilador está correto na caixa “Compiler’s Instalation Directory”. Se não estiver, coloque o caminho certo (como “C:\DevJogos\MinGW”).

Também é uma boa ir nas abas Search Directories e conferir se o compiler aponta para o diretório bin do mingw e se o linker aponta para o diretório lib.

7 11 2008
lustinha

Gostaria de saber se é possivel colocar barras de rolagem em uma janela SDL?

obrigada

7 11 2008
vinigodoy

Possível certamente é. Mas não é fácil de se fazer no braço.

Talvez seja melhor usar um componente de terceiros que rode sobre SDL, como o aedgui ou o wgui.

7 11 2008
lustinha

Ok, obrigada, vou dar uma olhada nesse aedgui e wgui.

22 10 2009
rodrigo

Cara eu vou tentar usar esse aedgui porém não consigo compilar vc saber os linkers que eu tenho que colocar pra conseguir compilar??

22 10 2009
vinigodoy

Não sei não, só lendo a documentação mesmo.

Deixe um comentário