Aula 06
Aprimoramento de Código com a Criação da Classe de Jogo
Summary
# Resumo da Aula de SDL
## Introdução
Na aula de hoje, o foco é aprimorar o código do jogo, que atualmente está muito concentrado no arquivo `main.cpp`. O objetivo é organizar a lógica em funções e classes, tornando o código mais legível e manutenível.
## Estrutura Atual
- O arquivo `main.cpp` contém a função `main`, que é o ponto de partida do programa.
- Atualmente, a função `main` é responsável por várias tarefas:
- Inicialização do SDL
- Criação da janela
- Configuração do jogo (como azulejos e posição do jogador)
- Loop do jogo
## Objetivos
- Extrair a lógica da função `main` em funções separadas.
- Criar uma classe chamada `Game`, que conterá a lógica do jogo.
- Manter o código inicial simples, sem criar muitas classes.
## Organização do Código
1. **Criação de Arquivos**: Será criado um novo arquivo `jogo.cpp` e um header `jogo.h` para a implementação da classe `Game`.
2. **Organização da Lógica**: A lógica do jogo será divida em funções especiais:
- Função de inicialização do jogo
- Função do loop do jogo
- Função de limpeza (fechamento do programa)
## Implementação
- A classe `Game` será implementada com os métodos necessários.
- A primeira função a ser criada será a `iniciar` e sua implementação ocorrerá em `jogo.cpp`.
- A função `criarJanela` será responsável por criar a janela do jogo e avaliar erros usando `SDL_GetError`.
## Exemplos de Funções
- Função `iniciar()`: Inicializa os sistemas SDL.
- Função `criarJanela()`: Cria a janela e a superfície usando SDL.
- Função `desligar()`: Executa a limpeza dos recursos do SDL.
## Compilação e Erros
- A necessidade de incluir os arquivos corretamente é fundamental para evitar erros durante a compilação.
- Erros podem ser causados pela falta de declarações ou inclusão de variáveis.
## Conclusão
A aula terminou com a estrutura do código já começada a ser organizada em métodos e uma classe. Na próxima aula, será dado seguimento ao aprimoramento do código, incluindo a implementação do loop do jogo e mais funções.
Video Transcript
Olá pessoal, bem vindo a mais uma aula de SDL.
Nesta aula vamos aprimorar o código, vamos fazer o seguinte, você vê que toda a lógica
que a gente tem do jogo está presente no arquivo main.cpp, o arquivo principal.
O arquivo main tem a função main na linha 172 e a função que o programa
inicia, quando a gente roda o programa ele vai bater em main primeiro, isso é onde
tudo começa, mas normalmente é bom a gente extrair toda lógica para fora da função main.
A gente quer deixar isso aqui bem limpinho e bem curto e o que vai acontecer é que a gente vai
chamar certas funções de main e essas funções terão suas próprias responsabilidades.
No caso que a gente tem agora, main está sendo responsável por muitas coisas diferentes,
como por exemplo o init do SDL, a criação de uma janela, da superfície, a configuração dos
azulejos, posição do jogador e tem o game loop que é a repetição de jogo.
Então está tudo dentro do main, mas o main já tem muita responsabilidade aqui,
então a gente vai extrair cada lógica, a gente vai tentar no melhor possível
fazer uma função para cada parte do jogo, principalmente a função de iniciar as
coisas e configurar a função do game loop que é rodando o jogo e a função de limpeza que
é no final para poder fechar todos os sistemas. São esses três mais básicos e dentro de cada um
a gente vai ter subfunções com responsabilidades diferentes. Então nesta aula nós vamos criar
uma classe chamada game, vou chamar essa classe jogo, na verdade vou chamar de main no game in
English, qualquer nome, mas vai ser a classe que eu vou colocar toda essa lógica do jogo lá.
É claro que a gente sempre pode fazer mais e mais classes e é isso que a gente
faz em produção, no mundo real, várias várias classes, mas eu só vou deixar simples para a
gente começar o início de uma aprimoração. Vai ser bem pesado no sentido de que tem muito
código que a gente precisa mover para o outro arquivo e tal e mudar umas coisas para poder
funcionar com aquela classe no estilo de programação de objetos, através de
a gente vai fazer uma instância da classe do jogo e daquela instância nós vamos chamar os
métodos ou funções membros. Tá bom? Então para iniciar aqui vamos criar um novo arquivo,
vou chamar em português mesmo jogo.cpp vou criar também o header.jogo.h. Então no header file
vai ser a declaração da classe do jogo e a implementação vai ser do arquivo.cpp.
Do arquivo cpp você vai fazer o include do jogo.h para poder obter as definições do header.
Dentro do jogo.h vamos fazer classes. Jogo.
Tá. Deixa eu ver aqui, eu estou usando para os braces as chaves na mesma linha, certo?
Então vai ser aqui class.jogo.h.
Ah, certo, vamos fazer assim.
E lá do meio a gente vai criar aqui em cima uma instância jogo.jogo. Então vamos criar
um novo variável chamado jogo do tipo jogo que é da classe lá que a gente vai ter que incluir
nesse arquivo. Então lá em cima você pode dar um include.a include.jogo.h.
Vamos só ver se funciona, vou fechar aqui, re-compilar com o j mais mais na linha de comando.
Então funcionou esse problema, ele não recomou.
Agora se eu criar aqui, por exemplo, a função, eu vou falar a palavra o chave public e tudo
debaixo de public serão membros public, se isto é, a gente pode chamar de fora, né?
Por exemplo, a gente pode usar a instância que a gente fez na função main chamar
jogo.o.nome.membroPublic. Função. Então vou dizer que vou chamar inicial, vou criar,
declarar a função que não retorna nada do nome iniciar, não leva nenhum parâmetro, tá? E o ponto
vivo. Aí eu vou fazer aqui no jogo.cpp a implementação, você põe o nome da classe,
dois pontos, né? Como name space, com a espécie de name space. E vamos definir que da classe
de jogo o método iniciar e retorna void, não tem retorno. Eu só vou deixar aqui pra ver se
não tem nenhum problema, tá? Eu declarei no header, né? Implementei aqui no cpp, agora vou
lá do main, vou chamar essa função aqui embaixo dentro da função main. Logo após a instanciação
do jogo, falar de jogo.iniciar. Vamos chamar e ver se, não faz nada agora, mas vamos ver se a
chamada de função não dá problema. Então já teve um problema aqui que eu fiz algo errado,
que é que eu fiz errado. Na função main referência não definida para jogo iniciar.
Vamos ver. Public void iniciar. Jogo. Ah, eu sei o que é. Então, como eu adicionei um
incluir aqui do jogo, né? Ele não tem a implementação, porque eu não inclui o novo
adicione. Jogo.cpp na hora da compilação. Então, eu tenho que adicionar a g++,
não só o main.cpp, mas o jogo.cpp pra ele poder criar o arquivo de objeto pra isso.
Eu vou linkar com a sdli2 e vou dar logo após way.out ser gerado. Pronto, então não teve
problema aí. Ter que adicionar o arquivo lá pra o comando de compilação.
Tá bom? Fechar aqui. Então, essa função iniciar vai ser a primeira coisa que a gente vai fazer.
Então, o que é que é o início do jogo? Normalmente, o início do jogo a gente vai
iniciar os sistemas e substistemas necessários pra poder rodar o jogo. Nesse caso, como a
gente usa o sdli, a gente tem que iniciar com sdli.net. Então, isso vai ser uma das coisas que a gente vai
pôr lá no iniciar. Então, vamos lá. Então, essa parte do sdli.net a gente vai recortar.
Aí eu vou lá pro jogo.cpp na implementação do método iniciar e vou colar aqui. Mas agora,
vamos ver aqui um negócio. A gente tá usando um sdli.net. Isso requer o include do sdli.
Então, a gente tem que se lembrar disso. Então, o sdli log também requer o include do sdli
e o sdli get error também. Então, aqui em cima eu posso incluir o include. Como é que eu tinha
que ver antes, deixa eu ver aqui. Se é com as o angle, dessa maneira aqui. Então, eu vou usar
menor que sdli 2 bar sdli.net h. E com isso, ele já vai reconhecer essas três funções e a constante
ok. Então, voltando aqui lá embaixo pro método pra função main. Vamos ver se não dá problema.
Salvar o arquivo. Salvei. Salvei. Vou dar. Então, ah, porque o problema aqui. Vamos ver.
Jogo cpp. Então, o problema está retornando o inteiro 1, mas a função é
função que não tem valor de retorno. Então, quando essa função estava lá no main, o valor de
retorno 1 significa que teve erro. Se você supera no sistema de Unix e tal, o valor de retorno
se for zero significa que foi tudo sucesso sem nenhum erro. O valor diferente de zero é o valor
de erro, né? Que houve algum erro e esse erro é identificado pelo número inteiro. Nesse caso,
eu puse número 1, né? Que é um generalizado, né? Erro em geral. Então, o que fazer aqui, né?
Qual as opções? Eu posso retornar o inteiro lá do iniciar. Isso é
coisa que a gente poderia fazer. Por exemplo, se, eif, a gente podia adicionar um if
lá no main e o iniciar por diferente de zero, gente, vai retornar 1. Isso é um maneira ou você pode
já abortar aqui, né? É sair desse programa na mesma hora através de uma chamada de um
talvez exit, algo assim. Depende de você, né? Deixa eu ver aqui o que faz.
Vamos pensar. Então, eu vou usar o exit, mas se você quiser fazer algo diferente, pode fazer, tá?
Vou chamar o exit com número 1 pra ele poder já sair logo na mesma hora.
Tá? Então, tem problema aí.
Esse da problema é se simular um erro aqui, por exemplo, tá igual a zero.
Ele realmente sai do programa, que eu simulei o erro pra botei igual a zero lá, só pra poder rodar
essa parte. Aí não deu certo e saiu mesmo. Voltando aqui, revertendo. Então tá aí, voltando ao main.
Vamos aqui. Tem a criação da janela, né?
Tem a curva do jogador e tal e tal. Vamos ver aqui nessa criação de janela aqui.
Então, nessa parte da criação de janela, tem a chamada pro Create Window e ele vai criar
a janela e ele criou uma variável que é usada em várias partes, né? Nesse caso, tá checando o erro
e tá passando aqui no Get Window Surface pra pegar a superfície da janela.
Então, essa variável aqui é usada até aqui no Game Loop também. Então, vai ser um pouco mais.
A gente não pode só recortar e colar lá como é que a gente vai poder utilizar
a variável Window aqui, que é usada em outros passos do código, que não são de responsabilidade
da função lá que a gente vai criar agora, que é a função de criar uma janela. Bem, a classe,
nos permite definir membros privados, né? Esses membros privados podem ser acessados
de cada método, né? Da instância da classe. Então, com isso, não vai ter problema
quando a gente mover o código que depende na variável Window, tá? Então, o que vai acontecer?
Vamos pegar essa variável aqui Window e vamos defini-la, tá? Como um membro privado da classe de jogo.
Então, deixa eu copiar aqui, deixa eu copiar essa parte do lado esquerdo na linha 179,
lá pro header do jogo .h. Agora, antes, né, da palavra, pra chave public, né? É claro que eu poderia
dizer private, dois pontos, mas é implícito que é private. Então, eu vou colocar aqui, colar
a USDL Window, ponteiro, né? Estrela. Então, essa variável vai ser acessível de qualquer função aqui
que você declarar dentro da classe, né? As instâncias que serão criadas, no caso, eu só tenho uma
instância da variável jogo lá em Main, vai poder acessar essa variável aqui, vai poder definir
o valor e acessar. Então, certo? Agora, com essa variável aqui, a gente vai fazer uma função,
essa função, voltando aqui pro main, posso cpp. A função será a responsabilidade da criação
de uma janela, vai ser essa parte aqui, tá?
Então, vamos aqui. Feita também essa parte da Windows Surface, que é pra poder pegar a superfície
da janela, chamada do GetWindowsSurface. É claro que você poderia, toda vez que precisar
da superfície da janela, você poderia chamar essa função aqui, toda vida que precisar.
Mas se você não quiser ter que chamar essa função todo o tempo, você pode também definir
Windows Surface como um membro privado da classe jogo. Seria a mesma coisa que a gente fez agora
com Windows, só copiar essa parte aqui, tá? E vamos lá no jogo.h, na nova linha 3,
sdlsurface.actaresco.windowsurface, aí poderia também ser acessível de qualquer
função membro das instâncias de jogo. Voltando ao main aqui, como a gente já fez isso,
o resto aqui é outras coisas que não são relacionadas à criação de uma janela,
então vou parar aqui na linha 192. Então, vamos recortar da linha 179 a linha 192.
Então, recortamos e vamos lá para o arquivo jogo.cpp, vou colar aqui, mas vai ter que ser
dentro de uma função, essa função vai ser responsável pela criação da janela. Então,
vou falar aqui, eu vou definir uma função chamada de criar janela e vou colar aquele código,
como a gente está usando c++, tem que definir umas coisas aqui, essa função criar janela,
não vai retornar nada, então vai ser do tipo void de retorno. Como ela estará dentro,
será um método da classe jogo, vou ter que dizer jogo 2 pontos, 2 pontos,
antes do nome da função, não tem nenhum parâmetro, tá certo? A gente fez a definição aqui,
a nota que é a linha 12 aqui tem a redeclaração da variável, se a gente deixar assim,
ele vai pensar que é variável local desse bloco da função, isso não é legal porque a gente quer
que a gente se refira àquele membro privado da instância, então você não pode redeclarar,
você tem que remover esse tipo aqui, para poder se referir ao atributo
lá que foi declarado na linha 2 do blueprint da classe, no arquivo jogo.h.
Mesma coisa para o Windows Surface na linha 3, a gente volta ao jogo.cpp, na linha 25,
remove esse tipo da variável aqui, que vai ser só uma definição em vez de declaração lá do tipo.
Então temos aqui na linha 12 o sdl create window para poder usar isso e essas outras constantes,
e o sdl getError tem que ter o sdl 2 que já está aqui, inclusive na linha 2, inclusive.
Muito bem, agora as linhas de 16 a 17 está usando a nossa constante que não é
reconhecível nesse arquivo, então vamos voltar ao membro sdl.cpp e vamos procurar esse negócio.
Cadê a função? Eu acho que está lá em cima, né? Vamos ver. Então aqui está
o window width e o window height, certo? Então
são definidos como variáveis de escopo global no arquivo que vem,
então a gente vai mover esse também, então vamos recortar aqui.
O que é que eu vou fazer? Vou voltar lá no jogo.h. Essas variáveis também podemos,
em vez de ser global lá no arquivo main, vai ser somente um membro privado da classe jogo.
Então com isso vai ser acessível de cada função membro, nesse caso a função de
criar janela. Então vou colar aqui na nova linha 4.
Opa!
E eu vou
deixar aqui, certo? Window width e window height. E essas constantes,
nós podemos deixar a definição aqui. É claro que a gente
poderia defini-las no constructor, ainda não temos um constructor,
um construtor, mas pode também definir lá, mas eu vou deixar aqui mesmo, tá?
Então com isso deixa o salvar o arquivo e voltar lá para o jogo.cpp,
a gente resolveu o problema das linhas 16 e 17, agora essas variáveis serão reconhecidas,
porque tem uma include do jogo.h. E é um membro privado, cada um das variáveis.
Então é acessível a esse método, criar janela. Então vamos aqui, na linha 20,
ele checa se realmente criou a window. Ele checa, faz a comparação com o no,
olha que está usando o std, cr, não deu certo, o std get error e o return. A gente
não está usando mais return, então vamos usar o exit aqui para ele poder sair do programa na mesma hora.
Window surface, get window surface, certo. Agora o problema que eu vi na linha 21 é o uso do
cr error aqui, o cr, né? Para poder usar isso a gente vai ter que incluir o IO stream, né?
Então vamos lá em cima, vou incluir também include IO stream,
io stream,
certo.
Com essa definição já aqui, voltamos ao main.cpp, vai lá para a função main,
depois do iniciar a gente vai falar jogo.criar janela, vamos chamar a função aqui para poder
fazer aquela lógica que tinha aqui antes, que criar a janela e define a variável window,
e logo após pega a superfície da window e põe na variável window surface.
Recompilando aqui vários erros,
sepra bom, vamos lá para o primeiro erro, né? Rola lá de pra cima.
O problema que eu removia a variável window,
do window width, do arquivo main.cpp, agora ele está reclamando porque eu acho que eu
também irei usar essa variável em outros lugares, né? Onde é que eu tinha usado aqui?
Ah, eu tinha usado aqui na linha 125 do main.cpp, 134 assim por diante.
Ah, o que eu posso fazer? Então, como ele está sendo usado aqui, né?
Por enquanto eu vou deixar, eu vou reverter a minha mudança aqui, na verdade eu vou
pegar as variáveis e é só deixar lá copiada, vou copiar e deixar lá como antes, só para até
que a gente remova todas essas funções, certo?
Então, só por enquanto, depois a gente remove. Agora qual é a outra?
Está dizendo aqui na função main, classe jogo não tem membro chamado criar janela.
Por que será? Jogo.h eu não, então eu fiz a implementação do criar janela,
mas eu não declarei no header, né? Então aqui dentro de public, de baixo de public,
depois da linha do inicial falar void, criar janela e abre, fecha, parênteses,
e o dois ponto e vila. Então com isso ele vai declarar, não tem nenhum parâmetro, né?
E vou recopilar, porque ainda tem mais erros. E agora o problema é da aquelas variáveis window,
né? Então bem, vamos ver aqui. Windows surface não foi declarada, porque ele também está sendo usado
em os locais e o window também. Então a gente chegou num ponto aqui que vai meio, né? Que vai ser
meio pesado, a gente vai ter que esperar um pouco até resolver todos esses erros. Então em vez de
em vez de esperar que tudo dê certo na mesma hora, a gente vai ter que esperar um pouco,
certo? Então vamos continuar aqui. Volta na main, a gente vai ter que continuar a mover esses
códigos até que não tenha mais uso da mesma variável. Tá bom? Então eu espero um pouquinho aí,
vamos continuar.
Então tem esse bullsai que é do game loop, então esse vai relacionado ao game loop,
tem um evento, vai ser relacionado a lidar com os eventos de jogador, então isso que
a gente vai separar esses caras. Então o CT2 ele faz o que, ele define a cor do jogador,
e tal, e tal. Então vamos ver essa cor de jogador usado na hora de pyrrack.
Então só lá, né? Só aqui e lá.
E depois disso tem a declaração dos azulejos, posicionando a entrada e saída do jogador,
carregar o mapa, posicionando o jogador inicial, imprimir mapa e tal e tal.
As funções de imprimir mapa, carregar mapa, vai ser fácil só mover lá, vai ser o nome da mesma
função, pintar e tal e tal. Então vamos ver que tá meio, deixa eu fazer uma mudança aqui no sair.
Então eu vou mover esse bullsai igual a false, antes do game loop na linha 202,
porque não precisa ir lá em cima, não é relacionado. Então vou mover.
Agora aqui na Westside da L.E. Event, vamos ver onde está sendo usado. O event está sendo usado aqui
também no game loop, então deixa eu também mover isso. Por lá.
Linha 203. Agora temos a cor do jogador que é usada só na pyrrack, dentro do game loop.
Então como é usado aqui, e bem, vai ter a função do game loop, a função do game loop não é
responsável pelo configuração da cor do jogador, então vou separar isso mesmo. Eu vou pegar
uma linha da cor do jogador e vou colocar dentro de uma função, certo?
Então vai ser bem simples, recortamos a linha 180.
Deixa eu ver aqui. E vamos lá no jogo.cpp. Eu vou criar a função chamada de
configurar a cor do jogador ou setar a cor do jogador, tá?
Vou pigurar a cor do jogador.
Então essa função vai ser retornada, então vai ser void aqui, não tem nenhum parâmetro,
então vazia. Bem, a função pega o Windows Surface que já é acessível,
devido a ser um membro privado. Formato disso e tal e tal, chama sdl map rgb,
a gente já incluiu sdl nesse arquivo, então é reconhecido essa função. Agora,
se a gente declarar a variável aqui com o tipo de arquivo, vai ser variável local
desse bloco, então quando sair, não vai ser mais acessível do Game Loop.
Lembra que o Game Loop tá usando a cor do jogador essa variável? Então vou ter que
pegar essa variável e fazer o quê? Torná-la um membro privado da classe jogo.
Então no jogo.h na nova linha 6 eu vou botar isso, colar o int 32 espaço cor jogador.v.
E com isso já declarado aqui, salvo arquivo, vai e volta pro jogo. Eu não preciso
disso dessa parte aqui do tipo e só defino a variável, tá? Que é aquela variável membro privado.
Com essa função aqui, eu preciso definir que pertence a classe jogo, então jogo 2.2.
E vamos copiar essa assinatura de função aqui, lá pro jogo.h na nova linha 10,
remove o name space aqui, o jogo 2.2. E tem um configurar o cor jogador.v no final.
Vamos lá, volta pro main. Lá do main você fala jogo.configurar o jogador,
abre, fecha, parêntese, ponto e vírgula. Então ainda vai dar eu, né? A gente tentava
copiar lá porque a gente tem aquelas aparências do window e tal. Então a gente vai ter que continuar mais, tá?
Então vamos fazer só mais um antes da gente ter um intervalo.
Vamos fazer um aqui na hora aqui.
Estroui o window. Vamos fazer o bem fácil, tá? Vamos pular um pouco aqui, vamos pular pro final.
Como a gente fez o iniciar e essas partes aqui, vai ter o game loop aqui chegando que é bem pesado.
Aí no final sempre tem o quê? Depois do game loop sempre tem a limpeza, né? Que vai ser
destruição da janela e o desligar do sistema SD, né? Sub-systems. Então vamos pegar
esse aqui, ó, tá? E vamos fazer uma função pra poder desligar, tá bom? Então vou fazer a função
de ligar que vai fazer essas coisas aqui. Então vou recortar o SDL destroy window e o SDL quit.
Eu vou fazer em vez de falar jogo.disligar. Essa função a gente vai lá no jogo.h
Cria aqui void.delegar. Abre parentes, fez parentes, ponto e vilo. Vai no jogo.cpp.
Põe aqui, né? Disligar. Pertencia ao jogo. Pesse pontos, dois pontos, antes do nome da função
e antes do nome. Quando a MSP está e o nome da função tem o tipo de retorno do tipo, né?
O tipo do retorno que é void. Eu vou colar. E tem aqui as funções de SDL destroy window.
O window já é assassivo? Por quê? Porque está no membro privado na linha 2 do class.jogo.
Então tá certo, SDL quit também, porque aqui nesse arquivo foi incluído o SDL.
Legal? Então vamos ver aqui. Vai ser só assim mesmo. Agora vou remover
essa linha 219, que não preciso. Então a gente pulou lá no final só pra poder fazer essa
função bem rápida. Então está quase. Tem um game loop, tem algumas funções aqui de
pintar e tal. Carregamento mapa, tá? Então vamos descansar um pouquinho aqui.
Ter o intervalo antes da gente continuar, tá? Então eu te vejo na próxima,
onde a gente vai continuar a nosso aprimoramento e a mover esse código lá pra class.jogo. Te vejo lá então.
Nenhum comentário ainda (loading...)
Nenhum comentário ainda (loading...)
Gostou da aula? 😆👍
Apoie nosso trabalho com uma doação: