Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
O que me incentivou ?
Já vai fazer um ano que eu me cadastrei no fórum PDJ. Desde então, reparei que a
grande dúvida de todos os iniciantes é: POR ONDE EU COMEÇO
Pensando nisso, comecei a escrever esse ebook para ensinar desde o mais
básico até o mais avançado da programação de jogos. Os exercícios, como vocês podem
ver, não têm “respostas no final”. Não tem mesmo, não foi por que eu esqueci. A idéia é
que todos resolvam os exercícios utilizando o fórum (do PDJ e/ou do GameDev), por que
assim, ajudando um ao outro, acredito que seja mais fácil aprender.
No final do “curso”, o “aluno” deverá saber:
– Algoritmos
– C++
– SDL
– Blender 3D
– OpenGL
– GIMP
Bom, chega de conversa fiada e vamos ao trabalho !!!
Site PDJ: http://www.pdj.com.br/
Site GameDev: http://www.gamedev.com.br/
1
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Introdução
A idéia desse “curso” é ensinar ao leitor como montar algoritmos e logo depois
traduzilos para c++ sendo os algoritmos voltados à programação de jogos. Nesta parte
do curso você não aprenderá a utilizar imagens, objetos ou modelos seja em 2D ou 3D.
Você aprenderá a montar a estrutura do jogo em c++. Para utilizar imagens,... você usará
como auxílio o OpenGL por exemplo. Mas isso é assunto para outra parte do livro. Por
hora, nos preocupemos em aprender a melhor parte. Programação.
No começo, é apenas um livro de c++ normal. Quando já soubermos repetições,
variáveis, funções e estruturas entraremos na programação de joguinhos simples (coisas
como RPG's de papel, estilo D&D). Não desanime. Tudo tem um começo. O começo para
fazer jogos é o que você verá a seguir.
Obs.: Algumas vezes os usuários mais experientes verão que eu escrevi uma ou outra
besteira... for proposital ! Leia o livro todo e verá “correções” mais para a frente.
2
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Capítulo 1 – As variáveis
{Zankoku na tenshi no you ni
Shounen yo shinwa ni nare Evangelion}
Como um anjo cruel
jovem tornese uma lenda
Não vou ficar com conversa fiada. Vamos ao que interessa !!
Primeiramente é necessário aprender o funcionamento de algoritmos. Para isso
mostrarei um algoritmo bem simples e explicarei o seu funcionamento logo depois.
Algoritmo
declare i, j numérico
i < 0
j < 0
escreva “digite um valor”
leia i
escreva “digite outro valor”
leia j
escreva “a soma dos valores é” i+j
fim algoritmo
“Algoritmo” e “fim algoritmo dispensa comentários.
“declare” é uma palavrachave. Ela diz que você está declarando uma variável.
O que é uma variável ouço você dizer. Para entender o funcionamento de uma variável
imagine aquele “brinquedinho” em que temos vários buracos de diferentes formas e
devemos introduzir o objeto correspondente à sua forma. (Fig. 1)
d
a c
b
d
a c
b
3
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Quando você declara uma variável você pode imaginar analogamente que você
está fazendo um buraco de uma determinada forma. As letras “a”, “b”, “c” e “d” são os
nomes desses buracos. A palavrachave “numérico” está indicando a forma desse buraco
(no algoritmo, “i” e “j” possuem a mesma forma. Analogamente você poderia dizer que “i”
e “j” são circulares por exemplo).
As duas linhas seguintes dizem “i<0” e “j<0”. Isso significa que você está dizendo
que o valor numérico de “i” é “0” e o mesmo para o “j”.
Como o nome diz, variável significa “que varia”, ou seja, você pode atribuir o valor
que você quiser à variável no momento em que desejar. Analogamente ao “brinquedo das
formas” você pode imaginar que os objetos podem ter cores diferentes (as variáveis “i” e
“j” podem ter vários valores, contanto que seja um número). (Fig 2)
d
a c
b
d d d
A palavrachave “escreva” diz ao computador “escreva isso aqui”. Uma frase deve
ficar entre parênteses. Quando você se refere a uma variável você não coloca
parênteses.
A palavrachave “leia” diz ao computador “manda o usuário digitar um valor para
essa variável”.
Traduzindo para o c++, teríamos:
#include <iostream.h>
int main()
{
int i = 0;
int j = 0;
cout << “Digite um valor: ”;
cin >> i;
cout << “Digite outro valor: ”;
cin >> j;
cout << “a soma dos valores eh: ” << i+j << endl;
return 0;
}
“#include <iostream.h>” significa: “Computador: Procure o significado dos meus códigos
4
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
nessa biblioteca (iostream.h, no caso)”.
Em “int main(){}” você disse ao computador que o seu código principal está lá dentro
(dentro das chaves). Funções ou qualquer outra coisa, veremos mais pra frente. Por
enquanto vamos trabalhar dentro do “main(){}” somente.
“int i = 0” é o mesmo que
“declare i numérico
i<0”,
o mesmo serve para o “j”.
“cout << “texto”;” é o mesmo que “escreva “texto” ”
“cin >>;” é o mesmo que “leia”
“endl” pula uma linha
Por enquanto, esqueça o “return 0;”, digite e não reclame ;).
Compilando:
O primeiro passo para compilar é baixando um compilador (lol?). Eu usarei o Dev C++
PDJ que se encontra na área de downloads em: http://www.pdj.com.br/. Baixe, instale e
abra. Quando você abrir verá a seguinte tela:
Tela inicial do Dev C++
O primeiro passo é criar um novo arquivo fonte ou projeto. Como nosso programa é
simples, vamos criar um arquivo fonte.
5
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Criando um arquivo fonte
Ao criar um novo arquivo fonte, um novo quadro aparecerá na sua tela habilitado para
escrita. Digite o código nesse quadro.
Código digitado no quadro de edição
6
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Antes de fazer a coisa toda funcionar, salve seu arquivo.
Salvando o arquivo
Após salvar o arquivo é hora de compilar e executar!
Compilando e executando
7
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Se tudo ocorreu bem, nenhum erro aparecerá. Podem também aparecer warnings, que é
o nosso caso agora.
Exemplo de warning
Esse warning não é nada demais. Ele só diz que a biblioteca “iostream.h” é muito velha e
que ele encontrou uma versão nova.
Se o seu compilador não compilar o código acima ou apresentar algum aviso,
substitua:
#include <iostream.h>
por:
#include <iostream>
using std::cout;
using std::cin;
ou
#include <iostream>
using namespace std;
e não tente saber qual a diferença
8
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Oops! Meu programa fechou sozinho e eu não pude ver o resultado! T___T
Para resolver esse problema acrescente a biblioteca “conio.h” e use a função
getch(). Você pode também incluir “stdlib.h” OU “cstdlib” e usar
“system(“pause”)” (pause é uma função do “prompt do msdos”. Só funciona no
windows.
#include <iostream.h>
#include <conio.h>
int main()
{
int i = 0;
int j = 0;
cout << “Digite um valor: ”;
cin >> i;
cout << “Digite outro valor: ”;
cin >> j;
cout << “a soma dos valores eh: ” << i+j << endl;
getch();
return 0;
}
Veja o código agora usando o “namespace std”
“using namespace std;”
9
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Obs.: Repare que na declaração das variáveis “i” e “j”, utilizase a palavrachave “int”. Isso
se deve ao fato de que as linguagens de programação não aceitam um valor que tenha
um ponto flutuante (3,1415, por exemplo) atribuído a uma variável declarada como inteira
(int). Para utilizar números com ponto flutuante, utilizase, no lugar de “int”, a palavra
chave “float”.
Agora veja o seguinte algoritmo:
Algoritmo
declare a, b numérico
a < 0
b < 0
escreva “Digite um valor para a:”
leia a
escreva “Digite um valor para b:”
leia b
escreva a*b
fim algoritmo
Esse algoritmo é muito simples. Ele pede para digitar um valor para “a” e um valor
para “b”. Depois de digitados ele escreve a soma dos dois. Você pode fazer isso com
todos os operadores. Veja a lista deles:
Em C++:
#include <iostream.h>
int main()
{
int a = 0;
int b = 0;
cout << “Digite um valor para a:”;
cin >> a;
cout << “Digite um valor para b:”;
cin >> b;
cout << a*b;
return 0;
}
Nos jogos:
Na programação de jogos você vai usar muuuitas variáveis. Mas é fácil perceber que você
10
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
poderia utilizar esse recurso para, por exemplo, armazenar quantos tiros, vidas, HP,
mana, etc o seu personagem possui. Como incrementar ou decrementar você verá
adiante.
Aplicação:
Como esse ebook foi feito para futuros programadores de jogos, nada mais justo do que
fazer um joguinho a cada final de capítulo. O joguinho desse capítulo é muito simples.
Você especifica quantos tiros você deu e a que distância e o programa fala se você matou
o personagem (calma, tenha muita imaginação aqui hehe).
001. #include <iostream.h>
002.
003. int main()
004. {
005. int n_tiros = 0;
006. int distancia = 100;
007. int vida_inimigo = 400;
008. cout << "Digite o numero de tiros: ";
009. cin >> n_tiros;
010. cout << "Digite a distancia: ";
011. cin >> distancia;
012. vida_inimigo = vida_inimigo(n_tiros*10)/distancia;
013. cout << vida_inimigo;
014. return 0;
015. }
11
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Capítulo 2 – Condicionais e loops em algoritmos
{ Hakuna Matata, isto é, sem problemas – Timão e Pumba }
Imagine que você queira que, dependendo do valor digitado, o programa execute
uma função diferente, por exemplo:
Algoritmo
declare cond, a, b numérico
a < 0
b < 0
cond < 0
escreva “Digite um valor para a”
leia a
escreva “Digite um valor para b”
leia b
escreva “O que você quer fazer? 1 = a+b e 2 = ab”
leia cond
se cond = 1
então
escreva a+b
fim se
se cond = 2
então
escreva ab
fim se
fim algoritmo
Sempre compare códigos com a vida real. Nesse caso podemos comparar com
um professor dando uma bronca (se você não fizer isso então eu te coloco para
fora). É idiota mas funciona.
Nesse caso o algoritmo pergunta se o usuário quer somar ou subtrair os dois
valores digitados. É simples não ?
Agora veja em C++:
#include <iostream.h>
int main()
{
int a = 0;
int b = 0;
int cond = 0;
cout << “Digite um valor para a:”;
cin >> a;
cout << “Digite um valor para b:”;
cin >> b;
cout << “O que você quer fazer? 1: a+b e 2: ab”;
cin >> cond;
12
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
if(cond == 1){
cout << a+b;
}
if(cond == 2){
cout << ab;
}
return 0;
}
O operador ‘=’ serve para ATRIBUIÇÃO. O operador ‘==’ serve para
COMPARAÇÃO. Nos algoritmos o que você conhece por ‘<‘ (seta para a
esquerda) é equivalente ao ‘=’ do C/C++. Já o ‘=’ em algoritmo equivale ao ‘==’
do C/C++.
Imagine agora que você gostaria que uma certa ação seja repetida um número finito de
vezes, sendo que este número pode ou não ser determinado por você, por exemplo:
Você tem 100 alunos. Você quer digitar as 4 notas dos seus alunos em um programa e
quer que este calcule a média. Você poderia fazer assim:
Algoritmo
declare nota1, nota2, nota3, nota4, média numérico
escreva “digite a nota 1”
leia nota1
escreva “digite a nota 2”
leia nota2
.
.
.
média < (nota1+nota2+nota3+nota4)/4
escreva média
fim algoritmo
E cada vez que você quisesse calcular a média de um novo aluno você deveria reiniciar o
programa ou chamar a função de alguma maneira ¬¬. Ou, talvez fosse melhor utilizar uma
estrutura de repetição.
Veja um método inteligente de utilizar uma estrutura de repetição nesse caso (das notas):
Algoritmo
declare nota, média, contador, contaaluno numérico
média < 0
contador < 1
contaaluno < 1
repita
se contaaluno > 100
então interrompa
fim se
repita
se contador > 4
então interrompa
13
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
fim se
escreva “digite a nota” contador
leia nota
média < média + nota
contador < contador + 1
fim repita
escreva “a média do aluno” contaaluno “é” média/4
contaaluno < contaaluno + 1
fim repita
fim algoritmo
O programa ficou mais complexo, porém, ficou mais funcional. Antes você tinha todos
aqueles problemas. Agora ficou mais inteligente. Essa é a função de uma estrutura de
repetição.
Veja agora a explicação do código (o que você ainda não sabe):
A palavrachave repita, dispensa comentários. O que está dentro de:
repita
.
.
.
fim repita
É executado um certo número de vezes. O número de vezes é determinado pela condição
de parada. Repare nas linhas:
se(alguma coisa ocorrer)
então interrompa
fim se
A palavrachave “interrompa” se refere à repetição em que esta está incluída. Resumindo,
quando o programa verifica e vê que a condição (x>9, por exemplo) é verdadeira ele
interrompe a repetição em que o “se...fim se” está incluído. Para que o número atinja a
condição de parada, alguma variável deverá ser incrementada a cada ciclo. Veja:
repita
se x > 9
então interrompa
fim se
escreva x
x < x + 1
fim repita
Quando o computador ler “fim repita” ele voltará para “repita” e começará um novo ciclo.
Se o x não for maior que 9 (5, por exemplo) ele continuará. Na tela aparecerá:
5
Depois o x (que está valendo 5) aumentará 1 (passará a ser 6)
14
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Como 6 não é maior que 9, na tela aparecerá:
6
Enfim, acho que você entendeu.
Como o C++ possui várias estruturas de repetição, essas serão abordadas em capítulos
distintos.
Para finalizar este capítulo, imagine que você queira que o programa apresente dois
comportamentos diferentes, dependendo do resultado da condição. Veja:
repita
se x > 9
então interrompa
senão escreva x
fim se
x < x + 1
fim repita
Enquanto o x não for maior que 9, o programa escreverá o número atual
Para entender melhor, veja a simulação (suponha que x começa valendo 1):
Vamos simular o algoritmo abaixo:
repita
se x > 9
então interrompa
fim se
escreva x
x < x + 1
fim repita
x < 1
1 > 9? Não!
escreva 1
x < x + 1
2 > 9? Não!
escreva 2
x < x + 1
3 > 9? Não!
escreva 3
x < x + 1
4 > 9? Não!
escreva 4
15
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
x < x + 1
5 > 9? Não!
escreva 5
x < x + 1
6 > 9? Não!
escreva 6
x < x + 1
7 > 9? Não!
escreva 7
x < x + 1
8 > 9? Não!
escreva 8
x < x + 1
9 > 9? Não!
escreva 9
x < x + 1
10 > 9? Sim!
então interrompa
Agora veja a simulação do seguinte algoritmo:
repita
se x > 9
então interrompa
senão se x < 5
então escreva “x ainda é menor que 5”
fim se
fim se
escreva “x é igual a” x
x < x + 1
fim repita
x < 3
x > 9? Não!
x < 5? Sim!
escreva “x ainda é menor que 5”
escreva “x é igual a” 3
x < x + 1
x > 9? Não!
x < 5? Sim!
escreva “x ainda é menor que 5”
escreva “x é igual a” 4
16
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
x < x + 1
x > 9? Não!
x < 5? Não!
escreva “x é igual a” 5
x < x + 1
x > 9? Não!
x < 5? Não!
escreva “x é igual a” 6
x < x + 1
x > 9? Não!
x < 5? Não!
escreva “x é igual a” 7
x < x + 1
x > 9? Não!
x < 5? Não!
escreva “x é igual a” 8
x < x + 1
x > 9? Não!
x < 5? Não!
escreva “x é igual a” 9
x < x + 1
x > 9? Sim!
escreva “x é igual a” 5
x < x + 1
então interrompa
Repare que enquanto x foi menor que 5 ele escreveu “x ainda é menor que 5”. Isso se
deve ao “senão se”.
Uma boa idéia para entender algoritmos é fazer os seus próprios e simulálos.
Simule também os que eu não simulei e veja os resultados.
Exercícios resolvidos
1) Dadas as pontuações de 2 jogadores A e B, faça um algoritmo que organizeas em
outras 3 variáveis A1 e B1 em ordem crescente de pontuação.
A primeira etapa é pensar no que o exercício propõe. Nesse caso temos 3 números (A, B)
e queremos organizalos de tal maneira que A1 < B1.
17
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Algoritmo
declare A, B, A1, B1 numérico
leia A
leia B
se A < B
então
A1 < A
B1 < B
senão
A1 < B
B1 < A
fim se
fim algoritmo
2) Um jogo muito famoso a uns anos atrás (hoje em dia nem tanto) foi pokémon para
game boy (e eu que tinha prometido não ensinar a fazer pokémon). Na ultima
versão para game boy color desse jogo, você podia escolher entre sexo masculino
e feminino. Caso fosse masculino os personagens te tratavam como “boy”, senão
você era tratada como “girl”. Elabore um algoritmo que trate 0 como “boy” e 1 como
“girl”. Caso o usuário digite 0 esse algoritmo deve falar “Olá mané!”, senão deve
dizer “Olá minininha ^^”.
Mais uma vez, devemos entender o problema. Esse apesar de possuir uma
“historinha” é mais simples que o anterior. Ele pede para você “conversar” com o
usuário levando em conta o sexo. Muito simples.
Algoritmo
declare A numérico
leia A
se A = 0
então escreva “Olá mane!”
senão se A = 1
então escreva “Olá minininha ^^”
fim se
fim algoritmo
3) É muito comum você precisar de reposição de energia em um jogo. Em alguns
jogos essas reposições se encontram dentro de baús. Quando o personagem abre
o baú ele recebe 1 ponto de vida a mais e o baú fica vazio. Caso o baú não se
esvazie, o personagem pode ter “vida infinita” e para isso basta ele voltar ao baú e
recuperar todos seus pontos de vida. Em um jogo comum essa idéia é trabalhada e
o personagem pode pegar somente uma vez o ponto de vida contido naquele baú.
Porém, todos os “gammers” sabem que na maioria dos jogos existem os “cheat
codes” (códigos de trapaça, trapaça). Elabore um algoritmo que permite ao
personagem pegar vida no baú somente uma vez a não ser que ele digite o código
“soufodamemo”.
Bom, nesse caso o algoritmo vai ficar bem precário, já que você ainda não sabe como
fazer aparecer um menu apertando uma tecla (e nem aprenderá nesse livro... afinal
18
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
aqui só será tratado o básico). Mesmo assim podemos fazer o seguinte:
O usuário aperta A para praticar uma ação, Q para sair e C para usar um código.
Vamos ver como fica esse algoritmo.
Algoritmo
declare vida, baú numérico
declare tecla, código texto
vida < 1
baú < 1
tecla < ‘A’
repita
se tecla = ‘Q’
então interrompa
escreva “O que deseja fazer? A = ação, Q = sair”
se tecla = ‘A’
então
leia tecla
escreva “Veja! um baú. Deseja abrir o baú? S/N”
leia tecla
se tecla = ‘S’
então se baú = 1
vida < vida + 1
senão escreva “O baú está vazio”
fim se
fim se
senão se tecla = ‘C’
escreva “Digite o código!”
leia código
se código = “soufodamemo”
então baú < baú + 1
senão escreva “Código não reconhecido”
fim se
fim se
fim repita
fim algoritmo
Nos jogos:
Você poderia usar uma estrutura de repetição, por exemplo, para que enquanto o
contador de vidas fosse maior que 0 a tela de gameover não aparecesse. Poderia ainda,
utilizar para que a cada vez que você pressionasse o botão “ctrl” seria decrementado 1 do
contador de tiros. Existem centenas de utilidades nos jogos.
Aplicação:
Pense no programa do capítulo passado. Ele apenas falava com quanta vida ficou o
oponente. Isso não tem graça nenhuma! Vamos arrumar agora o algoritmo para falar se o
oponente morreu:
19
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
001. Algoritmo
002. declare n_tiros, distancia, vida_inimigo numérico
003. n_tiros < 0
004. distancia < 100
005. vida_inimigo < 400
006. escreva "Digite o numero de tiros: "
007. leia n_tiros
008. escreva "Digite a distancia: "
009. leia distancia
010. vida_inimigo < vida_inimigo(n_tiros*10)/distancia
011. se vida_inimigo < 0
012. então escreva "Voce ganhou!"
013. senão escreva "Voce perdeu!"
014. fim se
015. fim algoritmo
20
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Algumas vezes, simplesmente queremos que o programa escolha um caminho
dependendo do resultado e outro caminho no caso contrário. Para isso serve o “if”. Veja o
trecho de algoritmo: (você já viu “se...fim se” em algoritmo !!)
repita
se x > 9
então interrompa
senão
escreva x
fim se
x < x + 1
fim repita
Repare que o se está dividido em duas partes: “então” e “senão”. Ou seja, “se
alguma coisa” então faça isso, senão faça aquilo. Essa é a idéia do if. Agora vamos ao
que interessa. A sintaxe básica é essa:
if(condição)
{
//ações
}
else
{
//mais ações
}
A parte “if(condição){}” equivale a “se condição então”. Já “else{}” equivale a
“senão”. Veja o primeiro exemplo do capítulo traduzido para o c++ (sem o repita por
enquanto ok?).
//alguma estrutura de repetição
if(x > 9){
break;
}
else{
cout << x;
}
x = x + 1;
//fim da estrutura de repetição
Não há segredos como você pôde ver. Assim funciona a estrutura if. Antes de
continuarmos, vejamos a tabela de operadores:
21
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Esses são os operadores de comparação. Useos nas estruturas que necessitarem
de algum tipo de comparação como o if.
Há uma técnica chamado “aninhamento”, onde você coloca um se dentro de outro,
ou seja: se isso então, se isso, então se isso... faça isso senão isso.... Veja:
se x > 0
então //alguma coisa
senão
se x < 100
então //mais alguma coisa
senão
se x > 51
//mais mais mais alguma coisa
senão
//outra coisa
fim se
//mais coisas XD
fim se
//finalmente faça isso !!
fim se
Não há nada novo nesse código. É só uma técnica. Veja como fica em c++:
if(x > 0){
//alguma coisa
}
else{
if(x < 100){
//mais alguma coisa
}
else{
if(x > 51){
//mais mais mais alguma coisa
}
else{
//outra coisa
}
22
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
//mais coisas
}
//finalmente faça isso !!
}
Eu sei, eu sei é meio confuso mas você se acostuma.
Aplicação:
Você é capaz de traduzir o código do capítulo passado para C++?
001. #include <iostream.h>
002.
003. int main()
004. {
005. int n_tiros = 0;
006. int distancia = 100;
007. int vida_inimigo = 400;
008. cout << "Digite o numero de tiros: ";
009. cin >> n_tiros;
010. cout << "Digite a distancia: ";
011. cin >> distancia;
012. vida_inimigo = vida_inimigo(n_tiros*10)/distancia;
013. if(vida_inimigo < 0)
014. {
015. cout << "Voce ganhou!" << endl;
016. }
017. else
018. {
019. cout << "Você perdeu!" << endl;
020. }
021. return 0;
022. }
23
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Capítulo 4 – Para escolher... use o switch case
{ De um lado Narusegawa san... de outro Mutsumi san... cara isso é tortura
Keitarô Urashima }
Você deve ter ficado um pouco invocado e confuso com o aninhamento. Pois na
hora de usar o aninhamento para apresentar escolhas, use o switch case (aninhamento
sucks). Veja:
Escolha com aninhamento:
if(letra == 'A'){
//alguma coisa
}
else{
if(letra == 'B'){
//mais alguma coisa
}
else{
if(letra == 'C'){
//mais mais mais alguma coisa
}
else{
//outra coisa
}
}
}
Escolha com switch case:
letra = toupper(letra);
switch (letra)
{
case 'A': /*alguma
coisa*/
break;
case 'B': /*mais alguma
coisa*/
break;
case 'C': /* mais mais mais
alguma coisa*/
break;
default: /*outra
coisa*/
}
24
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Acredito que não houve problemas a não ser na palavra “toupper” e “break”.
Toupper apenas converte uma letra para maiúscula caso o usuário utilize
minúscula (c++ é case sensitive). O “break” pára a repetição. Só isso.
Nos jogos:
Nos jogos a utilização do switch case é evidente. Você mostra várias opções ao jogador e
ele escolhe uma. Simples. É claro que se o menu do jogo for desenvolvido em ambiente
gráfico, o switch case ficará esquecido (é só adicionar uma função para cada botão ¬¬).
Existem algumas outras utilizações. Mas essa é óbvia.
A partir de agora, muitas coisas vão mudar. Quando a condicional ou loop
possuir apenas 1 linha não usarei mais chaves ({ }). Na hora de declarar
variáveis vou separar as variáveis de mesmo tipo por vírgula.
Aplicação:
Vamos criar um joguinho bem simples, o "par ou ímpar". Você e um amigo colocam um
número e ele verifica se o número é par e dá a vitória para quem escolheu. O switch case
vai ser usado para escolher entre o jogo desse capítulo e o do capítulo passado.
001. #include <iostream.h>
002.
003. int main()
004. {
005. int escolha_jogador_1, escolha_jogador_2, numero1, numero2,
resultado, n_tiros = 0, distancia = 100, vida_inimigo = 400, menu = 1;
006. cout << "Digite qual jogo deseja jogar:\n1: Tiro\n2: Par ou
impar\n";
007. cin >> menu;
008. switch(menu)
009. {
010. case 1:
011. cout << "Digite o numero de tiros: ";
012. cin >> n_tiros;
013. cout << "Digite a distancia: ";
014. cin >> distancia;
015. vida_inimigo = vida_inimigo(n_tiros*10)/distancia;
016. if(vida_inimigo < 0)
017. cout << "Voce ganhou!";
018. else
019. cout << "Você perdeu!";
020. break;
021. case 2:
022. cout << "Jogador 1, digite sua escolha: \"0 = par e 1 =
impar\" ";
023. cin >> escolha_jogador_1;
024. if(escolha_jogador_1 == 1)
025. escolha_jogador_2 = 0;
026. else
25
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
027. escolha_jogador_2 = 1;
028. cout << "Jogador 1, digite um numero: ";
029. cin >> numero1;
030. system("cls");//para unix use system("clear")
031. cout << "Jogador 2, digite um numero: ";
032. cin >> numero2;
033. resultado = numero1+numero2;
034. resultado = resultado%2;
035. if(escolha_jogador_1 == resultado)
036. cout << "Jogador 1 ganhou...";
037. else
038. cout << "Jogador 2 ganhou...";
039. break;
040. default:
041. cout << "Digite uma opção valida...";
042. }
043. system("pause");
044. return 0;
045. }
Se você verificou todo o código terá dúvida em duas coisas. Primeiro o '\n'. Esse
é um caracter ('\n' representa APENAS UM caracter) e siginifica nova linha.
Segundo é o operador '%'. Ele retorna o resto da divisão de dois valores.
26
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Se você está curioso para saber como se escreve o “repita... fim repita” no c++,
aqui vai um dos métodos:
Algoritmo
declare a, contador numérico
a < 0
contador < 0
leia a
repita
se contador > 2
então interrompa
fim se
a < a*a
contador < contador + 1
fim repita
escreva a
fim algoritmo
Esse algoritmo é bem básico (simule e verá que é bem básico). Ele apenas eleva
um número à quarta.
Agora veja em c++:
#include <iostream.h>
int main()
{
int a = 0;
int contador = 0;
cin >> a;
while(contador < 2)
{
a = a*a;
contador+=1;
}
cout << a;
return 0;
}
While significa enquanto, ou seja, segundo o programa, enquanto “contador” for
menor que 3, faça o que estiver dentro das chaves ( {...} ). JAMAIS se esqueça de
aumentar o contador em um determinado valor a cada iteração. Se você omitir o
27
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
“contador+=1;” (que é exatamente a mesma coisa que “contador = contador + 1;”) o loop
ficará infinito (o nome dessas “repetições” ou “iterações” é loop). Repare agora que no
algoritmo, a condição era (se contador for maior que 2 então interrompa). Já em c++ a
condição é (enquanto contador for menor que 2, faça isso). Isso se deve ao fato de no
algoritmo estarmos usando uma condicional “se” para parar a repetição. É apenas
questão de ponto de vista, acho que você não se assustou com isso... certo ?
Veja agora o seguinte código:
#include <iostream.h>
int main()
{
int a = 0, contador = 0;
cin >> a;
while(++contador < 3)
a = a*a;
cout << a;
return 0;
}
Na verdade ele faz EXATAMENTE a mesma coisa que o código anterior... O nome
“disso” é notação prefixada. Ela funciona assim:
– O contador é aumentado
– A condição é testada
– Se for “true (verdadeiro)”, ele entrará na repetição. Se for “false (falso)” ele irá
interromper
Há ainda a notação posfixada. Ela funciona assim:
– A condição é testada
– O contador é aumentado
– Se a condição (que já foi testada) for “true”, ele entrará na repetição. Se for
“false” ele irá interromper
Veja o mesmo exemplo usando a notação posfixada:
#include <iostream.h>
int main()
{
int a = 0, contador = 0;
cin >> a;
while(contador++ < 2)
a = a*a;
cout << a;
return 0;
}
28
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Não existe operador '**' nem operador '//'. Se você tentar usar vai dar erro de
sintaxe! Tirando que ‘**’ seria elevar ao quadrado e já existe função para
potências. O // sempre retornaria 1.
Agora, vejamos o “do... while”:
#include <iostream.h>
int main()
{
int a = 0, contador = 0;
cin >> a;
do
{
a = a*a;
contador+=1;
}while(contador < 2);
cout << a;
return 0;
}
Você deve estar pensando: Mas não é a mesma coisa ?!?! Não exatamente.
O loop “do while” funciona de um jeito semelhante à notação posfixada. Mas a grande
diferença entre o “while” e o “do while” é que no “do while” os comandos são executados
pelo menos uma vez !! Isso se deve a ele testar a condição somente no final. Resumindo:
“faça ... (ou seja ele fará de qualquer jeito) enquanto algo for verdadeiro”.
Nos jogos:
Existem muitas funcionalidades para loops em jogos. Uma delas é o loop principal. Tudo o
que estiver dentro dele irá ocorrer enquanto você não escolher "sair do jogo".
Aplicação:
29
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Se você ainda não se convenceu dessa idéia de loop principal, veja esse joguinho. Mas
antes de fazermos você precisa saber como funciona o "rand".
Se você quiser obter um número aleatório, você pode usar whiles e ifs até a morte e não
conseguir atingir o objetivo. Ou você pode usar a função rand. Para usar apenas faça:
int a;
a = rand()%y;
'y' pode ser um número da sua escolha ou uma variável, e 'y' é o limite superior do rand (0
o inferior, ou seja, você vai obter aleatoriamente um número de 0 a 9. Porém se você usar
apenas o rand muitas vezes a mesma seqüência de números vai ser obtida. Por isso você
deve primeiro "inicializar o rand". Entre aspas porque a inicialização não é obrigatória.
int a;
srand(time(NULL));
a = rand()%y;
Agora sim podemos começar. Vamos fazer dois joguinhos. Um deles sorteia um número
aleatório e pergunta qual é o número. O outro é simplesmente um jogador de dados.
001. #include <iostream.h>
002. #include <time.h>
003.
004. int main()
005. {
006. int aleatorio, adivinha, sair = 0, escolhe_jogo = 1;
007. srand(time(NULL));
008. do
009. {
010. cout << "Qual jogo deseja jogar?\n\n1: Aleatorio\n2: Dado\n";
011. cin >> escolhe_jogo;
012. switch(escolhe_jogo)
013. {
014. case 1:
015. while(!sair)
016. {
017. aleatorio = rand()%10;
018. cout << "Digite um número de 0 a 10: ";
019. cin >> adivinha;
020. if(adivinha == aleatorio)
021. cout << "Voce ganhou!\n";
022. else
023. cout << "Voce perdeu!\n";
024. cout << "Deseja voltar ao menu principal? 0:
025. nao ";
cin >> sair;
026. }
027. break;
028. case 2:
029. while(!sair)
030. {
031. aleatorio = rand()%5+1;
032. cout << "Jogando dado... o numero e: " <<
aleatorio;
30
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
033. cout << "Deseja voltar ao menu principal? 0:
nao ";
034. cin >> sair;
035. }
036. break;
037. default:
038. cout << "Escolha uma opcao valida...\n";
039. }
040. cout << "Deseja sair? 0: nao ";
041. cin >> sair;
042. }while(!sair);
043. return 0;
}
O loop while e a condição if ocorrem caso o que estiver dentro do parênteses for
verdadeiro. Zero (0) para o C++ é falso e um número inteiro positivo é
verdadeiro. Portanto se 'a' for igual a um, fazer "if(a == 1)" e "if(a)" é a mesma
coisa. Caso o 'a' seja zero, fazer "if(a == 0)" e "if(!a)" é o mesmo. O operador '!'
nega o que estiver na frente.
31
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Capítulo 6 – O loop “ for”
{ One, two, three, four... I love marine corp. Recrutas – Full Metal Jacket }
Depois de ver o loop “while” e “do while”, o loop for parece meio inútil. Quando você
já estiver experiente verá que não é necessariamente inútil. Veja o código:
#include <iostream.h>
int main()
{
int a = 0, contador;
cin >> a;
for(contador = 0; contador == 3; contador++)
a = a*a;
cout << a;
return 0;
}
Veja a enorme semelhança com o loop “while” posfixado. Agora repare que neste
caso a condição funciona de uma maneira diferente. No loop while, a condição
normalmente é modificada (você verá isso mais para frente) dentro das condicionais “if”.
No loop for, há um determinado número de vezes para acontecer a iteração, ou seja, você
tem certeza de quantas vezes “a coisa” irá acontecer. No nosso caso, 3 vezes.
Entenda agora, a sintaxe:
for(contador = 0; contador == 3; contador++)
for() { ... } // você apenas indicou que é um loop for
contador = 0; // você inicializou a variável
contador == 3; // condição (repita 3 vezes)
contador++ // incremente a variável contador
#include <iostream.h>
int main()
{
int contador;
for(contador = 0; contador < 9; contador++)
cout << contador << endl; //endl pula uma linha
return 0;
}
O programa irá mostrar os números de 0 a 8
Ou você também pode decrementar a variável:
int main()
{
32
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
int contador;
for(contador = 9; contador > 0; contador)
cout << contador << endl; //endl pula uma linha
return 0;
}
O programa irá mostrar os números de 9 a 1 XD.
Aplicação:
Vamos fazer um joguinho que simplesmente explode uma bomba caso você erre o
número. A bomba vai explodir em 5 segundos caso você erre. Antes de começar preste
atenção no nosso joguinho do capítulo passado.
srand(time(NULL);
Nós incluimos time.h e usamos uma função time. Essa função nos dá o tempo de
execução em milisegundos. Vamos agora fazela contar o tempo da explosão!
001. #include <iostream.h>
002. #include <time.h>
003.
004. int main()
005. {
006. int tempo_inicial, tempo_final, aleatorio, numero;
007. srand(time(NULL));
008. aleatorio = rand()%9;
009. cout << "Digite um numero de 0 a 9: ";
010. cin >> numero;
011. if(numero != aleatorio)
012. for(int conta = 5; conta >= 0; conta)
013. {
014. tempo_inicial = time(NULL);
015. do
016. tempo_final = time(NULL);
017. while(tempo_final tempo_inicial < 1);
018. cout << conta << '\n';
019. }
020. else
021. cout << "=/ voce ganhou =/\n";
022. return 0;
023. }
Como eu já disse no começo simule todos os joguinhos de final de capítulo.
Repare como a sua evolução já é visível. Antes o primeiro jogo era o máximo que
você podia fazer. Agora você já sabe criar até um contador de tempo!
33
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
7 Lógica BÁSICA
{ Você verá uma coisa nova. Duas coisas. E eu as chamo Coisa Um e Coisa Dois. – Dr. Theodor Seuss
Geisel }
Quando uma pessoa fala algo óbvio a gente logo retruca (pô mas isso é lógico). Então o
que é lógico? Vamos ver algumas frases lógicas:
Premissas:
Se você estudar você tira 100% na prova.
Se você não ficar doente você vai estudar.
Conclusão:
Portanto podemos dizer que se você não ficar doente você tira 100% na prova.
Premissas:
Se você for para a cachoeira você não vai almoçar em casa.
Ação:
Você foi para a cachoeira.
Conclusão:
Você não almoçou em casa.
Todas as conclusões são óbvias. Você parte de algumas idéias básicas e dependendo da
sua ação você chega a uma conclusão. Podemos montar tabelas verdade para analisar
essas situações. Mesmo assim, antes disso, aprenderemos mais sobre operadores
lógicos.
7.1 Operador E
O operador E (ou "conjunção") mostra que uma proposição depende diretamente da
outra, sendo que se você anular uma, a frase inteira fica incorreta. Veja:
Se você estudar você tira 100% na prova.
Se você não ficar doente você vai estudar.
Resumindo, podemos dizer que se você estudar E não ficar doente você vai bem na
prova. O operador E na lógica é '۸'. Há também linguagens que usam vígula ',' como
operador E. Assim:
Estudar = a; Ficar doente = b.
34
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
a b a ۸ b
Verdadeiro Verdadeiro Verdadeiro
Verdadeiro Falso Falso
Falso Verdadeiro Falso
Falso Falso Falso
Ou seja:
Se você não ficar doente e estudar você vai bem na prova;
Se você não ficar doente e não estudar você não vai bem na prova;
Se você ficar doente e estudar você não vai bem na prova;
Se você ficar doente e não estudar você não vai bem na prova.
7.2 Operador OU
O operador OU (ou "disjunção") é aquele que uma proposição não anula a outra. O
operador de disjunção é '۷'. Há linguagens que usam ponto e vírgula ';' como operador
OU. Veja:
Se você for para o parque você vai se divertir;
Se você for para o shopping você vai se divertir.
Portanto podemos dizer que se você for para o parque OU for para o shopping você vai
se divertir. Montando a tabela verdade:
Ir ao parque = a; Ir ao shopping = b.
a b a۷ b
Verdadeiro Verdadeiro Verdadeiro
Verdadeiro Falso Verdadeiro
Falso Verdadeiro Verdadeiro
Falso Falso Falso
Em outras palavras:
Se você for ao parque e ao shopping você vai se divertir;
Se você for ao parque e não for ao shopping você vai se divertir;
Se você não for ao parque e for ao shopping você vai se divertir;
Se você não for ao parque e não for ao shopping você não vai se divertir.
7.3 Negação
O operador negação serve para negar (óóóóó) o que vem na frente. Seu funcionamento é
35
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
igual à distributiva da matemática e o símbolo é '~'. Veja:
Se você for para o parque você vai se divertir;
Se você for para o shopping você vai se divertir.
a b a۷ b ~(a۷ b)
Verdadeiro Verdadeiro Verdadeiro Falso
Verdadeiro Falso Verdadeiro Falso
Falso Verdadeiro Verdadeiro Falso
Falso Falso Falso Verdadeiro
Ele apenas inverte o resultado. Se "a = falso", "~a = verdadeiro" e viceversa.
Existe muito mais material sobre lógica e eles vão bem além disso. Procure em
livros e na internet.
36
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Capítulo 8 – Algo sobre variáveis básicas...
{ Veja esta técnica pela última vez..
o melhor presente de adeus que eu podia lhe dar... Dohko }
Lembra do primeiro capítulo, onde as variáveis podiam ter “formas” diferentes? Vou
explicar direito nesse capítulo.
Para começar, a variável (sem viagens agora) é um espaço reservado na memória
de seu computador. Ao declarar uma variável, você está reservando um espaço que
somente o seu programa pode ter acesso e estará disponível até a finalização do
programa. As variáveis podem ser de vários tipos básicos. São eles:
Você deve ter notado a “incrível” semelhança entre os nomes long int e short int e
mais ainda entre unsigned (short int e long int). Na verdade as variáveis são somente int e
short. Repare nos valores numéricos da “unsigned short” e “short” e da “unsigned int” e
“int”. Some o módulo dos dois valores da short int:
|32.768 + 32.767| = 65.535. O resultado ser igual ao valor da “unsigned short” não é
coincidência. Unsigned significa sem sinal, ou seja, ao dizer “sem sinal” você não
aumentou o valor da variável. Você apenas afirmou que o resultado dela é positivo com
certeza e com isso aumentou (no campo dos números positivos) o intervalo de valores
sem utilizar o dobro de memória. O mesmo vale para a “unsigned int” que é igual à soma
dos módulos dos dois valores da “int”. |2.147.483.648| + |2.147.483.647| =
4.294.967.295. Para entender melhor (se você ainda não entendeu, veja o esquema:
32.768 32.767
65.535
Quando eu fiz esse capítulo pela primeira vez eu tinha parado aqui. Mas agora pretendo ir
37
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
adiante e mostrar o "jeitão" das variáveis dentro da sua memória. Como todos sabemos
(nós sabemos não é?) o computador na verdade funciona com "ligado e desligado" (os
famosos 1s e 0s). Se o computador funciona dessa maneira então as variáveis são nada
mais que 1s e 0s.
8.1 Binários
Como já dito, binários são 0s e 1s (por isso o nome BInários). Usando a tabela anterior,
podemos dizer que:
a b a b ~(a b)
1 1 1 0
1 0 1 0
0 1 1 0
0 0 0 1
Porém, binários são muito mais que isso. Você pode usar um número binário para
representar um numero real, inteiro, caractere...
8.1.1 Inteiros
Pegue uma calculadora científica. Procure "bin" na calculadora. Digite 7. Agora aperte
"bin". O número retornado foi "111". Esse número não é "cento e onze" e sim um, um, um.
Vamos entender tudo isso, mas antes vamos construir uma tabela verdade com 1
variável:
a a
1 1
0 0
Veja que o número de linhas é 2 para uma variável. Agora uma tabela verdade com 2
variáveis:
a b a b
1 1 1
1 0 1
0 1 1
0 0 0
Agora para 2 variáveis tivemos 4 linhas. Agora 3 variáveis:
a b c (a b) c
38
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
a b c (a b) c
1 1 1 1
1 1 0 1
1 0 1 1
1 0 0 1
0 1 1 1
0 1 0 1
0 0 1 1
0 0 0 0
Para 3 variáveis, tivemos 8 linhas. Epa! 2 1 = 2, 2² = 4 e 2³ = 8. Portanto para um número
'n' de variáveis, teremos 2 n linhas. Agora voltemos ao número 7. Ele possui 3 valores
lógicos (1, 1, 1). Portanto ele possui verdadeiro em todas as posições. Ora, se para 0
posições, obtemos 1 linha (ou é verdadeiro ou é falso), para 1 posição temos 2 linhas e
para 2 posições temos 4 linhas, podemos dizer que o número 7 possui verdadeiro em 2 0 ,
verdadeiro em 2 1 e verdadeiro em 2², portanto a soma desses valores deve dar 7!
2 0 = 1
2 1 = 2
2² = 4
1+2+4 = 7!
Esse é o método para representar um binário em inteiro. Para representar um inteiro em
binário, faça a subtração, sendo que número negativo representa FALSO, assim:
Representando o número 7:
A primeira potência de 2 menor ou igual a 7 é 4 (8 é maior que 7... lógico)
74 = 3, portanto podemos subtrair 4 de 7;
32 = 1, portanto podemos subtrair 2 de 3;
11 = 0, portanto podemos subtrair 1 de 1.
V V V é o número 7. Portanto 111.
Representando o número 8:
A primeira potência de 2 menor ou igual a 8 é 8
88 = 0, portanto podemos subtrair 8 de 8;
04 = 4, portanto NÃO podemos subtrair 4 de 8;
42 = 6, portanto NÃO podemos subtrair 2 de 4;
61 = 7, portanto NÃO podemos subtrair 1 de 6.
V F F F é o número 8. Portanto 1000.
39
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Agora vamos somar inteiros por uma visão binária õO.
Antes vamos pegar uma tabela com alguns binários já calculados:
1 = 1 5 = 101
2 = 10 6 = 110
3 = 11 7 = 111
4 = 100 8 = 1000
Se você somar 1 com 2, obterá 3 (óóóóóóó), portanto 1+10=11;
Se você somar 1 com 3, obterá 4, portanto 1+11 = 100;
Se você somar 3 com 5, obterá 8, portanto 11+101 = 1000.
A primeira soma é muito óbvia, veja:
10
+1
11
A segunda soma já é estranha pois:
11
+1
100
Vamos entender isso então. Se estamos somando em binários, primeiro vamos
transformar em potência de 2:
2 1 +2 0
+ 2 0
2²+2 1 +2 0
Repare que 2 0 +2 0 =2 1 , 2 1 +2 1 =2²,... portanto se temos uma soma de 2 0 , é a mesma coisa
que 2 1 , portanto vamos "pular" para a próxima casa (em aritmética você "pula" uma casa
quando a soma intera 10 ou mais). Pular uma casa significa mandar o 2 (2 0 +2 0 =2) para a
próxima posição. Na próxima posição já temos um 2 1 . Portanto vamos pular para a
próxima casa. A próxima casa não existe (zero à esquerda), portanto podemos criar e
teremos nosso número completo. 11+1 = 100.
A idéia básica é essa. Só atente para uma coisa: para o seu computador, o binário 100 é
na verdade 00000100.
Para representar um número negativo existem 2 formas, das quais vou falar sobre apenas
uma. A notação de complemento de um nega o seu binário, ou seja, o que é zero vira um
e o que é um vira zero! o binário 100 poderia simplesmente ser negado e passaria a
representar o número 4.
40
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
4 = 11111011
Note que sua calculadora não mostra um binário em complemento de um!
Você é capaz de dividir binários? Tente!
8.1.2 Reais
Pegue um número real qualquer. Eu escolhi o 1,23456. Esse número pode ser escrito em
potência de 10 da seguinte forma:
123456x10 5
Acredito que você sabe disso mas nunca é ruim reforçar. Repare que no exemplo
escrevemos que 1,23456 é o mesmo que 123456x0,00001. Já que isso é verdade,
podemos dizer que todo número real pode ser escrito por um número inteiro vezes dez
elevado a um número inteiro positivo ou negativo. Os números reais são representados
assim no seu computador. Veja:
1,23 = 123x10 2
Isso em binário equivale a:
01111011 x decimal(10) elevado a 11111101
No entando esse número SEMPRE é representado por 32 bits, sendo uma cadeia de 24
bits representando um inteiro e outra de 8 bits representando a potência. Assim, nosso
123 na verdade seria representado por:
000000000000000001111011
e a potência 2 seria representada por:
11111101
Dando uma cadeia de 32 bits representada por:
00000000000000000111101111111101
8.1.3 Memória
Agora que eu já disse todas essas coisas malucas, está na hora de enxergar como
funciona sua memória (mais ou menos >.<””).
41
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Suponha que sua memória seja assim (cada quadrado é 1 byte e não 1 bit):
Você liga seu computador e abre alguns programas. Logo depois sua memória fica assim:
Cada grupo de quadrados representa o espaço reservado para um tipo de dado. Cada
programa tem acesso somente ao espaço reservado por si próprio mas pode pegar
sujeira de memória eventualmente. Sujeira de memória é qualquer área acessada que
não pertence ao programa em questão. Pegar sujeira de memória é “crime” dentro do seu
computador (no windows gera a famosa frase “Esse programa executou uma operação
ilegal e será fechado”, no unix gera um core dumped). Veja que cada espaço reservado
pode ter um endereço. Para simplificar vamos usar o sistema de um tabuleiro de batalha
naval. Sendo assim o endereço de cada variável será a primeira posição ocupada por
essa variável seguida de um qualificador de tipo. Esse tipo de endereçamento foi
inventado agora e patenteado (óóóóó). Já que é assim, as cinco primeiras variáveis
teriam endereço:
0, 0, 1 (linha 0, coluna 0, variável do tipo 1)
0, 4, 1 (linha 0, coluna 4, variável do tipo 1)
0, 9, 2 (linha 0, coluna 9, variável do tipo 2)
0, 10, 2 (linha 0, coluna 10, variável do tipo 2)
42
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
0, 11, 2 (linha 0, coluna 11, variável do tipo 2)
O computador ao ler isso (o meu computador inventado e patenteado ^^”) entenderia que
na posição (0, 0) começaria uma variável do tipo 1, portanto ele deveria reservar quatro
espaços à frente inclusive a posição (0, 0) e assim por diante.
Nos jogos:
Economize memória SEMPRE.
43
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Aplicação:
Vamos usar uma variável bool para fechar nosso joguinho anterior!
001. #include <iostream.h>
002. #include <time.h>
003.
004. int main()
005. {
006. int tempo_inicial, tempo_final, aleatorio, numero;
007. bool sair = false;
008. srand(time(NULL));
009. while(!sair)
010. {
011. aleatorio = rand()%9;
012. cout << "Digite um numero de 0 a 9: ";
013. cin >> numero;
014. if(numero != aleatorio)
015. for(int conta = 5; conta >= 0; conta)
016. {
017. tempo_inicial = time(NULL);
018. do
019. tempo_final = time(NULL);
020. while(tempo_final tempo_inicial < 1);
021. cout << conta << '\n';
022. }
023. else
024. cout << "=/ voce ganhou =/\n";
025. cout << "Deseja sair? 0: nao, 1: sim ";
026. cin >> sair;
027. }
028. return 0;
029. }
44
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Capítulo 9 – Variáveis compostas homogêneas
unidimensionais
{ Rozan HyakuryuHa – Cólera dos 100 dragões – Dohko }
Imagine a seguinte situação: Você precisa acessar, somar, multiplicar... centenas
de valores um pelo outro. Então você pega e declara 400 variáveis inteiras (int) e 1000
variáveis com ponto flutuante(float) ¬¬. CLARO QUE NÃO!!. Para isso existem arrays ou
variáveis estruturadas ou variáveis compostas. Veja o seguinte algoritmo:
Algoritmo
declare s, i, a, b numérico
declare A[1:10] numérico
i < 1
s < 0
repita
se i > 10
então interrompa
fim se
escreva “Digite um valor:”
leia a
leia b
A[i] < a + b
s < s+A[i]
i < i+1
fim repita
escreva s
fim algoritmo
Repare que não foi necessário declarar 10 variáveis A (A1, A2, A3, A4...). Foi
necessário apenas digitar A[1:10] para o computador entender que na verdade não é uma
são 10 !!!. Repare também que eu usei uma outra variável para acessala. Isso foi feito
para que o programa “percorresse o vetor”. A cada iteração o valor de “i” aumenta em
uma unidade. Portanto a cada repetição, o programa acessa uma posição diferente do
vetor. Veja o esquema:
A[1] A[2] A[3] A[4] A[5] A[6] A[7] A[8] A[9] A[10]
Suponha agora que o usuário digitou os 3 primeiros valores de a e b: a(1, 3, 5) e
b(2, 4, 6). Veja o resultado final:
A[1] A[2] A[3] A[4] A[5] A[6] A[7] A[8] A[9] A[10]
3 7 11
Acho que a imagem fala por si só. O valor dentro dos colchetes (A[1], B[2]...), é o
45
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
valor de i.
Veja agora o algoritmo traduzido:
#include <iostream.h>
int main()
{
int s = 0, i = 0, a = 0, b = 0, A[9];
//estrutura de repetição
if(i > 10)
break;
cout << endl << “Digite um valor:”;
cin >> a;
cin >> b;
A[i] = a + b;
s = s+A[i];
i = i+1;
//fim da estrutura de repetição
cout << “\n” << s;
return 0;
}
Repare que eu coloquei A[9] ao invés de A[10]. Isso se deve ao fato de o limite inferior do
C++ ser 0 por padrão. De 0 a 9, temos 10 números. Não se preocupe com limite inferior
por enquanto (mesmo porque no C++ não precisa se preocupar com isso). Uma
“bitolinha” é sempre subtrair um do tamanho de seu vetor.
Nos jogos:
Na programação de jogos com OpengGL ou DirectX, você ainda vai ouvir muito falar de
arrays... Não tenha dúvidas.
Aplicação:
Quem nunca brincou de jogo da forca? O funcionamento desse jogo é bem simples
quando se conhece o método de se fazer uma variável composta homogênea (vetor).
Simplesmente crie um vetor de char (string) e atribua palavras aleatórias depois comece o
jogo. Veja:
Pense em 10 palavras
Faça um rand de 0 a 9 e armazene seu valor. Faça um switch case sendo que para cada
valor ele atribua um valor para nossa matriz.
Faça um contador que some 1 para cada erro.
Quando a quantidade de erros for igual a 5 ele da a mensagem que você perdeu e sai.
Mas... poxa, para atribuir uma palavra eu terei que atribuir letra por letra!? Não
necessariamente se você usar a biblioteca "string.h".
46
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
9.1 Um pouco sobre Strings
Aqui eu vou mostrar algumas poucas funções para manipulação de strings (vetor de
char). A primeira função interessante para mecher com strings é a de cópia. Para copiar
uma palavra para um vetor você poderia atribuir posição por posição (veja que para digitar
uma string é fácil. O operador >> do cin aceita vetor de char) ou poderia simplesmente
usar a função de cópia
strcpy(vetor, palavra ou vetor);
Portanto ao invés de fazer:
char palavra[10];
palavra = "jogosemcpp";
Você deve fazer:
char palavra[10];
strcpy(palavra, "jogosemcpp");
Há também a função de comparação cujo funcionamento é o mesmo só que ao invés de
comparar, vai copiar:
strcmp(palavra, "jogosemcpp");
Mas essa função retorna um valor lógico, portanto:
bool testa = false;
char palavra[10];
testa = strcmp(palavra, "jogosemcpp");
Agora que já sabemos um pouquinho sobre a string.h, é hora de ir para nosso jogo!
001. #include <iostream.h>
002. #include <string.h>
003. #include <time.h>
004.
005. int main()
006. {
007. char palavra[12] = {}, letra = 'a', mostra[12] = {};
008. bool sair = false;
009. srand(time(NULL));
010. int sorteia = rand()%10;
011. switch(sorteia)
012. {
013. case 0:
014. strcpy(palavra, "Jogos");
47
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
015. break;
016. case 1:
017. strcpy(palavra, "Computador");
018. break;
019. case 2:
020. strcpy(palavra, "Matematica");
021. break;
022. case 3:
023. strcpy(palavra, "Fisica");
024. break;
025. case 4:
026. strcpy(palavra, "Inteligencia");
027. break;
028. case 5:
029. strcpy(palavra, "Artificial");
030. break;
031. case 6:
032. strcpy(palavra, "Linux");
033. break;
034. case 7:
035. strcpy(palavra, "Computacao");
036. break;
037. case 8:
038. strcpy(palavra, "Grafica");
039. break;
040. case 9:
041. strcpy(palavra, "PQP");
042. break;
043. }
044. while(!sair)
045. {
046. for(int j = 0; j < 5;)
047. {
048. cout << "Digite uma letra: ";
049. cin >> letra;
050. for(int procura = 0; palavra[procura]; procura++)
051. if(palavra[procura] == letra)
052. mostra[procura] = palavra[procura];
053. else
054. {
055. cout << "Voce errou...\n";
056. j++;
057. }
058. cout << mostra << endl;
059. }
060. cout << "Deseja sair? 0: nao ";
061. cin >> sair;
062. }
063. return 0;
064. }
O loop for não necessita de TODOS os parâmetros. Adicione somente aqueles
que te interessar. Para inicializar um vetor apenas separe os valores por vírgula
e coloqueos entre chaves!
48
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Capítulo 10 – Variáveis compostas homogêneas
bidimensionais
{ Do you know: What It Is ? The Matrix is everywhere – Morpheus }
Arrays bidimensionais são apenas os arrays comuns (unidimensionais) com várias
linhas. Pense como se fossem matrizes. Matriz pode ser M1x2, A1x4 quando só possui
colunas e M22x2, B5x8, ou genéricamente MATRIZixj, sendo i = linhas e j = colunas. Veja
uma matriz 1x4:
Agora veja uma matriz 3x5
Resumindo: Arrays bidimensionais servem para armazenar dados que contenham
uma relação próxima entre si, por exemplo: Você quer armazenar todas as notas dos
alunos de uma sala (sala com 20 alunos) e o número do aluno, portando uma matriz que
tenha 2 colunas e 20 linhas ou 2 linhas e 20 colunas.
Número do aluno Nota do aluno
1 7
2 6
3 8
4 2
. .
. .
. .
20 9
A forma de representar em algoritmo é Matriz[1:2, 1:20]. Veja um exemplo simples
para armazenar os dados:
Algoritmo
declare s, i numérico
declare A[1:2, 1:20] numérico
i < 1
s < 0
repita
se i > 20
49
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
então interrompa
fim se
escreva “Digite a nota do aluno:”
A[i][1] < i
leia A[i][2]
s < s+A[i][2]
i < i+1
fim repita
escreva s/20
fim algoritmo
O algoritmo apenas lê as notas e a cada repetição adiciona o número
correspondente ao aluno. Veja a simulação:
Número do aluno Nota do aluno
1 7
//repetição
Número do aluno Nota do aluno
1 7
2 6
//acho que você entendeu !!!
Agora, como de praxe, veja o algoritmo traduzido:
#include <iostream.h>
int main()
{
int s, i, A[2][19];// olha que legal... o c++ não faz questão de
A[1:2][1:20]
i = 0;
s = 0;
//estrutura de repetição
if(i > 19)
break;
cout << “Digite a nota do aluno:”;
A[i][0] = i;
cin >> A[i][1];
s = s+A[i][1];
i = i+1;
//fim da repetição
cout << s/20;
return 0;
}
Você deve ter se perguntado (por que diabos A[1:2]. Isso se deve a linguagens como
50
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
pascal. O padrão é o seguinte: Matriz[limite inferior:limite superior]. Eu poderia falar muita
coisa e te deixar confuso. Mas a função disso é a seguinte: Veja um exemplo com limite
inferior 1 e limite superior 4 (Matriz[1:4]):
1 2 3 4
Agora veja uma matriz com limite inferior 3 e limite superior 8 (Matriz[3:8])
3 4 5 6 7 8
Mas é só isso?!?!?!?!?!... Sim é só isso ¬¬. Linguagens como o pascal são muito
formais... exigem coisas meio chatas mesmo, vá se acostumando.
Nos jogos: Como dito no capítulo passado, você ainda vai ver muitas arrays na hora de
fazer jogos (3D). Se acostume com isso também.
51
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Capítulo 11 – Variáveis compostas homogêneas
tridimensionais
{ Even now in this very room – Morpheus }
Você já viu arrays unidimensionais (faça relação com uma reta), já viu
bidimensionais (pense em um plano). Agora, veremos tridimensionais (sólido).
Veja a “evolução”:
É só uma continuação intuitiva. É claro que se já temos linhas e colunas, faltam as
páginas (cada página pode estar relacionada a um cliente, as colunas podem ser o salário
e o número de horas de trabalho e as linhas cada mês do ano !!).
Veja:
52
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Simples não ? Agora veja como armazenar esses dados :
Algoritmo
defina fator 100
declare i, conta, k numérico
declare tabela[1:12][1:3][1:20] numérico
i < 1
conta < 0
k < 1
repita
se k > 20
então interrompa
fim se
repita
se i > 12
então interrompa
fim se
escreva “Digite os dados referentes ao mês” i
repita
se conta = 1
então interrompa
fim se
escreva “Digite quantas horas extra” k
“trabalhou:”
leia tabela[i][2][k]
tabela[i][3][k] < (tabela[i][2][k]*fator)+2000
conta < 1
fim repita
conta < 0
i < i+1
fim repita
k < k+1
fim repita
fim algoritmo
Quando você bateu o olho deve ter pensado “NOSSA!!!!!!! que código gigante”.
Acredite. Esse código é muito pequeno e simples de entender. Leia, releia, trileia até
entender tudo. Além disso, uma palavra nova apareceu “defina”. Isso significa que você
definiu uma constante, ou seja, um espaço na memória que não pode ser modificado e
tem um valor determinado (ao contrário de uma variável que pode ser modificada a hora
que você quiser).
53
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Em c++:
#include <iostream.h>
#define fator 100
int main()
{
int i = 0, k = 0, tabela[11][2][19];
while(k < 20)
{
while(i < 12)
{
cout << “Digite os dados referentes ao mês” << i;
for(conta = 0; conta < 1; conta++)
{
cout << “Digite quantas horas extra” << k <<
“trabalhou:”;
cin >> tabela[i][1][k];
tabela[i][2][k] = (tabela[i][1][k]*fator)+2000;
}
i += 1;
}
k += 1;
}
return 0;
}
Só repare em duas coisas:
Primeiro: A constante foi declarada antes do main()
Segundo: Usei o “loop for”
54
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Capítulo 12 – Funções
{ bool Ficar(char *Cantada);. – Piada estúpida feita por mim }
Agora que você já tem uma boa noção de criação de algoritmos, vamos começar a brincar
pra valer!
Quando você lê “funções”, espero que você tenha pensado no “f(x)” da matemática. Em
uma função na matemática, você fornece um valor para “x” e espera um valor de “f(x)”.
f(x) = x²+2x+1
x = 1
f(x) = 4
Essa é a idéia de uma função em C++. Vamos por partes:
int x = 1, fx = x*x+2*x+1;
cout << fx;
A tela exibirá 4, com certeza. Mas agora você quer ver quando vale “fx” para “x” valendo
2:
int x = 1, fx = x*x+2*x+1;
cout << fx;
x = 2;
fx = x*x+2*x+1;
cout << fx;
Não preciso falar onde isso vai nos levar não é verdade? É para isso que foi criada a idéia
de função. Se você pudesse fazer uma chamada simples para usar “fx” isso iria lhe
poupar trabalho. Veja que simples:
int f(int x)
{
return x*x+2*x+1;
}
int main()
{
int x = 1;
cout << f(x);
x = 2;
cout << f(x);
return 0;
}
Como essa função é muito simples, parece que o trabalho poupado não foi tanto assim.
Agora pense em uma função com algumas linhas de código a mais. Como essa:
55
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
int MostraMaior(int a, int b, int c)
{
int maior = a;
if(b > maior)
maior = b;
if(c > maior)
maior = c;
return maior;
}
int main()
{
int a, b, c;
cin >> a >> b >> c;
cout << MostraMaior(a, b, c);
cin >> a >> b >> c;
cout << MostraMaior(a, b, c);
return 0;
}
Agora sim parece vantajoso não é verdade? Vamos à sintaxe:
int MostraMaior(int a, int b, int c)
{
//operações
return x;
}
Assim como nossa função “main” essa função é do tipo inteiro. Uma função do tipo inteiro
é nada mais que uma função que realiza cálculos internos (qualquer cálculo envolvendo
qualquer tipo de dado) e deve retornar um valor inteiro (NECESSARIAMENTE). Se ela
fosse “bool” deveria retornar “true” ou “false”. Agora as regras:
O que está entre os parênteses são os parâmetros.
Você pode colocar quantos parâmetros desejar contanto que não esqueça do
qualificador de tipo (int, bool, char,...) e separando por virgulas.
Você pode realizar as mesmas operações, escrever o código que quiser e usar quantas
variáveis quiser assim como na função “main”.
Uma função só pode acessar as variáveis próprias. Isso quer dizer que você pode ter
duas variáveis chamadas “bolinho_de_arroz” sendo cada uma em uma função diferente
que uma não tem acesso à outra e não haverá problema de “nome repetido”.
Retorne sempre o valor especificado no tipo da função.
Funções do tipo “void” como “void Apaga()” não retornam nada.
Funções não precisam ter parâmetros.
Ufa! Quantas regras. Mas agora que já sabemos tudo sobre funções vamos escrever
algumas...
56
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
É sempre legal criar uma função para algum código que se repete. Mesmo que
este apareça apenas 2 vezes... só não crie funções... err... sem função ^^
bool EMaior(int a, int b)//a é maior que b?
{
if(a > b)
return true;
else
return false;
}
int main()
{
int a, b;
char sair = 'n';
while(sair == 'n')
{
cin >> a >> b;
cout << "Será que a é maior que b?\n";
if(EMaior(a, b))
cout << "SIM\n";
else
cout << "NÃO\n";
cout << “Deseja sair? S/N ”;
cin >> sair;
}
return 0;
}
Epa! O que você testou dentro do “if(EMaior(a, b))”? Simples. O funcionamento de um “if”
(“while” e o segundo parâmetro do “for”) é o seguinte: Se uma condição C for verdadeira
então faça... portanto se você retornou true e true é verdadeiro então o teste deu
verdadeiro, simples ^^
Agora que você já pode escrever muitas funções, vamos às funções recursivas.
Voltando à matemática:
Seja f(x) = 2x+1, então, f(f(x)) = 4x+3.
Ou seja a função da própria função. Em C++ também existe isso, veja:
void Loop(int a)
{
if(a < 2)
{
cout << a << endl;
Loop(++a);
57
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
}
cout << a;
}
int main()
{
int a = 0;
Loop(a);
return 0;
}
vamos entender isso direito:
void Loop(0)
{
if(0 < 2)//verdade
{
cout << 0 << endl;
Loop(0+1);
void Loop(1)
{
if(1 < 2)//verdade
{
cout << 1 << endl;
Loop(1+1);
void Loop(2)
{
if(2 < 2)//mentira
}
cout << 2;//aqui, "a" vale 2
}
cout << 1;//aqui, "a" vale 1
}
}
cout << 0;//aqui, "a" vale 0
}
Se você pode fazer um loop “while” você pode criar uma função recursiva.
Pense nisso.
Veja que ao voltar, o "a" passa a ter o valor original!
Exercícios:
1) Faça uma calculadora de inteiros com operações “+, , / e *”
58
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Capítulo 13 – Ponteiros
{ Comece pelo começo… e vá até o fim; então, pare. – Lewis Carroll }
Esse capítulo é doloroso. Para mim, pois devo explicar e para você que se
entender de primeira demonstrará que ou você é muito inteligente (mais provável) ou que
esse tut é muito bom (menos provável).
Quando você cria uma variável, um espaço na memória é reservado para um
determinado tipo de dado (lembre do primeiro exemplo de variáveis no primeiro capítulo...
do brinquedinho). Pense agora que cada buraco possui uma posição fixa para serem
inseridos os bloquinhos. Use sua imaginação e pense em uma outra pessoa qualquer do
seu lado enquanto você brinca no tal brinquedinho (a pessoa deve possuir uma mão e um
dedo indicador). Você, sendo uma pessoa muito brincalhona pergunta: “Onde eu coloquei
o bloquinho?” e a pessoa aponta para o local e diz: “Ali!!!” (shit que explicação mais
idiota). É assim que funciona o ponteiro. É uma variável que serve para apontar para o
endereço de uma variável. Veja:
int main()
{
int a, *b;
a = 10;
b = &a;
cout << *b;
return 0;
}
Analisando cada linha:
Duas variáveis inteiras foram declaradas: Uma inteira comum e um ponteiro para inteiro
A variável “a” recebeu o valor 10
A variável “b” recebeu o endereço da variável “a”
Mostre o que tem no local onde “b” aponta
O caractere reservado “*” serve para indicar que a variável é ponteiro
O caractere reservado “&” serve para indicar que você quer o endereço da variável e não
o valor dela (a posição do bloco no brinquedinho e não o bloco em si)
Vejamos agora como funciona um vetor:
Uma área na memória é criada para ele, sendo que este possui a primeira posição e as
“n1” outras que seguem (óóóó genial)
Sendo que sempre é assim, é óbvio que o programa só indica a primeira posição e
59
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
quantas são aquelas que se seguem (as n1 posições). Isso quer dizer que um vetor é um
ponteiro! E melhor ainda, é possível criar funções que retornem um vetor de respostas!
Veja:
int *Ordena(int *Vetor)
{
int temp = Vetor[0], j1 = 0, j2 = 0;
while(Vetor[j1+1])
{
while(temp < Vetor[j2])
j2++;
if(temp > Vetor[j2])
{
Vetor[j1] = Vetor[j2];
Vetor[j2] = temp;
}
j1++;
temp = Vetor[j];
}
return Vetor;
}
int main()
{
int Vetor[5] = {5, 10, 7, 8, 40};
Ordena(Vetor);
for(int i = 0; i < 5; i++)
cout << Vetor[i];
return 0;
}
Esse código compila e executa perfeitamente! Mas espera. Se o vetor nada mais é que
um ponteiro que aponta para a primeira posição do vetor, se você modificou o vetor, você
modificou o local onde o ponteiro está apontando, ou seja, o vetor original! Isso quer dizer
que a função, nesse caso, poderia ser “void” e teríamos o mesmo resultado.
void Ordena(int *Vetor)
{
int temp = Vetor[0], j1 = 0, j2 = 0;
while(Vetor[j1+1])
{
while(temp < Vetor[j2])
j2++;
if(temp > Vetor[j2])
{
Vetor[j1] = Vetor[j2];
Vetor[j2] = temp;
}
60
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
j1++;
temp = Vetor[j];
}
}
Se você ainda está assustado com a palavra “ponteiro” volte e leia tudo denovo.
Lembrese que ponteiro aponta para um endereço na memória e o símbolo de
referência “&” mostra o endereço da variável na memória e não a variável.
Ótimo, agora vamos pensar em variáveis que não são ponteiros. Como fazer para
modificar o valor delas sem precisar retornar um valor (odeio essa parte, tira a
semelhança das funções matemáticas =( )? Muito simples, apenas passe como parâmetro
da função o endereço da variável e não a variável.
void SomaUm(int &a)
{
a++;
}
int main()
{
int a = 0;
SomaUm(a);
cout << a;
return 0;
}
Ponteiros duplos
É intuitivo que você leve a regra de passar o vetor como parâmetro a níveis mais altos e
querer passar matrizes, ou seja, passe um ponteiro duplo (**a) e pronto. Apesar de existir
ponteiros de quantos ponteiros você quiser não é possível passar uma matriz como
parâmetro com ponteiro duplo simplesmente. Para fazer isso você precisa especificar
quantas linhas a matriz possui. Só isso.
void FazNada(int a[][2])
{
a[0][0] = 1;
}
int main()
{
int a[2][2];
FazNada(a);
cout << a[0][0] << endl;
system("pause");//use system("pause") somente no win
return 0;
}
61
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Agora sim a regra é geral. Faça isso com matrizes tridimensionais e tudo dará certo.
Exercícios
1) Faça um programa com funções separadas para incluir e excluir um nome de
uma matriz
2) Faça uma função que verifique se um número já faz parte de um vetor
62
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Capítulo 14 Estrutura
Imagine que você quer criar um sistema de cadastro de alunos. Nesse cadastro os
campos são:
Nome
Telefone
Sala
Número
Você pode criar matrizes para armazenar cada dado, mas isso é muito cansativo. Ao
invés disso, seria legal se fosse possível criar uma só variável chamada Aluno que
aceitasse todos esses tipos de dados. E isso é possível com o comando “struct”.
struct aluno
{
char Nome[30];
char Telefone[8];
short int Sala;
short int Numero;
};
Veja estrutura como um formulário. Um formulário precisa de muitos dados mas
ele é um objeto único.
Sintaxe:
O comando “struct” deve ser seguido do nome da estrutura. Todos os tipos de dados
devem ficar dentro de “{ ... };” e NUNCA ESQUEÇA O PONTO E VÍRGULA.
Acessando:
void Insere(aluno *novos, int conta)
{
cout << "Nome: ";
cin >> novos[conta].Nome;
cout << "Telefone: ";
cin >> novos[conta].Telefone;
cout << "Sala: ";
cin >> novos[conta].Sala;
cout << "Numero: ";
cin >> novos[conta].Numero;
}
int main()
{
aluno novos[50];
63
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
for(int i = 0; i < 50; i++)
Insere(novos, i);
return 0;
}
Repare que nossa função acessa os dados da estrutura com a notação ponto e “aluno” é
um novo tipo de dado. Para criar a estrutura “aluno” faça como você já fazia com os
outros tipos de dados. Para acessar a posição do nosso vetor de “aluno”, use os
colchetes depois do nome da variável e não depois do nome do campo.
14.1 – Ponteiro para Estruturas
Assim como você criou ponteiros para variáveis (e não parecia lá a coisa mais útil do
mundo XD) você pode criar para estruturas já que estruturas são tipos de dados. E agora
vamos com calma. O C++ trouxe muitas facilidades mas com as facilidades vieram alguns
problemas de falta de entendimento (como tudo na vida você tem que escolher entre o
trabalhoso ou o complicado). No C, para transformar um ponteiro (de char por exemplo)
eu uma variável do tipo char você usava “calloc”. Esse comando alocava um espaço para
uma variável do tipo char e entregava esse espaço para o ponteiro. Você podia realocar
(ou seja, criar um novo espaço e entregar o novo endereço para o ponteiro) sendo que o
espaço reservado anteriormente continuava lá só que o ponteiro que apontava para ele
estava apontando para outro espaço õO. No C++ só existe o new e ele funciona como
calloc e realoc. Agora que você já sabe ficou fácil e menos trabalhoso. Eu não citei nada
assim antes porque as utilidades de um “new” para um programador iniciante são quase
0. Assim sendo podemos fazer:
struct aluno
{
char Nome[30];
char Telefone[8];
short int Sala;
short int Numero;
};
void Insere(aluno *&novo, int conta)
{
novo = new aluno;
cin >> novo>Nome;
cin >> novo>Telefone;
cin >> novo>Sala;
cin >> novo>Numero;
}
int main()
{
aluno *novo;
for(int i = 0; i < 50; i++)
Insere(novo, i);
return 0;
64
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
}
Antes de tentar entender vamos à sintaxe:
“>” é o mesmo que “.” em “novos.Nome” com uma pequena diferença. A seta “>” você
usa para VARIÁVEIS ALOCADAS DINÂMICAMENTE (ou seja com o comando “new”) e
“.” você usa para as estáticas.
Agora analisando o código ele parece legal. Você cria um novo registro a cada chamada e
nenhum deles é apagado o que permite a criação de muitos (tantos quanto sua memória
agüentar) registros. Mas... como eu saberei o endereço de cada um?! Hmmm vamos para
o próximo capítulo ^^.
Exercícios
1) Implemente o deleta aluno e o busca aluno
65
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Capítulo 15 Introdução à estrutura de dados
Antes de começar esse capítulo eu pensei se não seria interessante criar outro tut
separado só pra ele. Mas achei melhor não. Esse capítulo será dividido em muitos sub
capítulos e será a parte mais difícil pois utiliza os conceitos mais avançados em C. Não
deixe passar nada porque aqui que a parte REALMENTE importante começa. Sem
estrutura de dados na ponta da língua você continuará sendo um leigo. Também entenda
melhor ponteiros (*) e estruturas (structs) caso tenha restado alguma dúvida, já que
estrutura de dados usa somente estruturas e ponteiros (e depois objetos).
A primeira pergunta pertinente que posso fazer é: O que é informação? Pensando nessa
pergunta podemos listar tudo o que contém informação:
Jornal
Revista
Banco de dados
Artigo
Livro
Equação
etc etc etc
Talvez informação seja um dado ou um conjunto de dados úteis para alguma pessoa.
Responda então o que é dado. Dado é... é... é aquilo que em conjunto ou sozinho pode
significar informação. Esse tipo de pensamento já foi discutido em muitas aulas e você
talvez não lembre. Pense em quando seu professor definiu ENERGIA: "Energia é aquilo
necessário para realizar trabalho" e "Trabalho é aquilo que acontece quando se gasta
energia". A mesma idéia surgiu em geometria quando o problema era definir ponto, linha,
plano: "Ponto é uma unidade sem tamanho e massa", "linha é um conjunto de pontos" e
"plano é um conjunto de linhas", porém é difícil definir exatamente o que é cada um deles.
Já que é assim, considere um "dado" como aquilo que sozinho ou em conjunto produz
uma informação e como algo que você pode manipular. Leia "manipular um dado" como
"inserir, remover, modificar ou buscar um dado". Veja alguns exemplos:
Exemplo 1 Um livro é um conjunto de páginas. Uma página é um conjunto de
parágrafos. Um parágrafo é um conjunto de frases. Uma frase é um conjunto de palavras.
Uma palavra é um conjunto de letras. Se você pensar em um arquivo de um livro em seu
computador você pode remover um dado (apagar uma página, por exemplo), inserir um
dado (colocar mais alguma informação), corrigir um erro (modificar um dado) e procurar
por um determinado tópico.
Exemplo 2 O cruzamento de um ser vivo com outro ser vivo gera um número X de
possibilidades de filhotes que cruzando entre si podem gerar um número Y de filhotes.
Cada nascimento é uma inserção de descendentes, cada morte é uma remoção, o
crescimento pode ser uma modificação e uma busca pode ser localizar aquele que
contém determinadas características únicas.
Exemplo 3 Em um jogo da velha programado usando um determinado algoritmo
(veremos isso mais adiante) o computador SEMPRE ganha. Isso porque ele analisa todas
as possibilidades de jogos e escolhe sempre a melhor delas.
66
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
No exemplo 1, temos um conjunto de páginas (um livro). Cada página é a continuação da
anterior, ou seja, cada página está ligada com a outra e "depende" da outra. Portanto é
um conjunto continuo onde a útlima página depende de todas as páginas anteriores.
No exemplo 2, temos uma família (avós, pais e netos). Cada filho está ligado diretamente
com seu pai e sua mãe mas não está diretamente ligados com seus irmãos, ou seja, a
existência de um não afeta a existência do outro já que todos nasceram de um pai e uma
mãe comum.
No exemplo 3, a partir de um passo do jogo o computador definiu todos os 'n' próximos
passos sendo que cada possibilidade de um mesmo nível não está ligada com outra e a
escolha de uma anula a outra.
Como montar esses "conjuntos" e manipulálos é o que faremos daqui até o final.
67
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Capítulo 16 Listas
O último exemplo do capítulo anterior mostrava um vetor de struct. Um vetor de alunos é
uma lista de alunos. Aquela lista era uma lista estática pois sempre terá 50 espaços para
alunos e é encadeada já que é possível acessar qualquer aluno pelo seu índice. Portanto
era uma lista estática encadeada (LEE). Uma LEE é uma estrutura de dados e acho que a
essa altura esse nome não te assusta. O que vamos estudar agora é como criar essa
estrutura bem como todas as funções que são primitivas a ela, ou seja, todas as funções
que se relacionam diretamente com ela. Pense em uma pilha de livros. Você possui vários
livros cada um com número de páginas, editora, autor, edição... Dessa pilha, você pode
tirar um livro, acrescentar um livro (caso haja espaço no armário) e procurar um livro. A
pilha é uma estrutura de dados que estudaremos mais adiante. Inserção, Remoção e
Busca são as funções primitivas à essa estrutura. Vejamos mais afundo a LEE.
16.1 Lista Estática Encadeada
Como já foi explicado uma LEE possui esse nome pois é uma lista, não muda de tamanho
e possui ligação entre os membros. Para criar uma LEE, bem como qualquer outra
estrutura de dados, precisaremos primeiramente de uma estrutura.
struct aluno
{
char Nome[30];
char Telefone[8];
short int Sala;
short int Numero;
};
Agora que já temos a estrutura, vamos implementar a primitiva de Inserção:
void Insere(aluno *novos, int conta)
{
cout << “Nome: ”;
cin >> novos[conta].Nome;
cout << “Telefone: “;
cin >> novos[conta].Telefone;
cout << “Sala: “;
cin >> novos[conta].Sala;
cout << “Numero: “;
cin >> novos[conta].Numero;
}
Se você já inseriu, é hora de buscar algum aluno:
int Busca(aluno *novos, int numero)
{
int j;
for(j = 0; j < 50 || numero != novos[j].Numero; j++);
68
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
if(numero == novos[j].Numero)
return j;
else
return 0;
}
Essa função diz que enquanto não acabar o vetor, ou, enquanto não achar o numero
procurado, a variável “j” será incrementada. Se achar, retorne o número do aluno. Senão
retorne “0”.
Agora que você já sabe quem é, vamos apagar:
bool Deleta(aluno *novos, int j)
{
for(; j < 49 || novos[j].Nome; j++)
{
strcpy(novos[j].Nome, novos[j+1].Nome);
strcpy(novos[j].Telefone, novos[j+1].Telefone);
novos[j].Sala = novos[j+1].Sala;
novos[j].Numero = novos[j+1].Numero;
}
if(j == 50)
{
strcpy(novos[j].Nome, "");
strcpy(novos[j].Telefone, "");
novos[j].Sala = 1;
novos[j].Numero = 1;
return true;
}
}
Enquanto não chegar na última posição ou enquanto houver alunos, o próximo aluno volta
uma posição no vetor. Caso o aluno seja o aluno 50, apenas apague os dados.
Agora nossa função de chamada:
int main()
{
aluno novos[50];
int j, numero;
for(int i = 0; i < 50; i++)
Insere(novos, i);
cin >> numero;
j = Busca(novos, numero);
Deleta(novos, j);
return 0;
}
69
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Apesar de todas serem listas cada uma tem sua particularidade (pontos fortes e
fracos). A LEE nunca pode ter mais do que X valores e isso é muito ruim.
Mesmo assim usea sempre que for possível.
Essa é a LEE. LEE esse é o leitor.
Veja que ela possui um problema. Nem sempre existem 50 alunos e isso pode ser um
pouco chato as vezes.
16.2 Listas Dinâmicas
Pensando em corrigir o problema das LEE, vieram as listas dinâmicas. Elas são de 2 tipos
principais e alguns outros tipos que veremos depois. Os tipos principais são:
Listas Dinâmicas Simplesmente Encadeadas (LDSE)
Listas Dinâmicas Duplamente Encadeadas (LDDE)
16.2.1 Listas Dinâmicas Simplesmente Encadeadas
As LDSE resolvem o problema da nossa LEE. Para fazer uma LDSE iremos usar uma
“struct” com um campo de ponteiro.
struct aluno
{
char Nome[30];
char Telefone[8];
short int Sala;
short int Numero;
aluno *proximo;
};
Veja que interessante. O campo “proximo” é um ponteiro de aluno.
Cada ponteiro de “aluno” irá apontar para o próximo aluno. Isso é inspirador. O nome
LDSE significa que é uma lista, não possui tamanho fixo, é encadeada e só indica o
próximo elemento.
Para iniciar uma estrutura desse tipo, devemos declarala como ponteiro.
int main()
{
aluno *novos;
return 0;
}
No entanto, acessar um ponteiro é estranho quando este não aponta pra nada (Figura
16.2.1.1). Para isso devemos inicializar nossa estrutura:
70
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
void Inicializa(aluno *&novos)
{
novos = new aluno;
novos>próximo = NULL;
}
Figura 16.2.1.2, lista inicializada:
Veja que temos quatro coisas legais aqui. Primeiro é o endereço do ponteiro. Segundo é a
“>”, a terceira é “NULL” e a quarta é o “new”. Acho que endereço de ponteiro já não o
assusta tanto assim. É simplesmente o endereço de um ponteiro. A seta é como
acessamos campos de estruturas declaradas como ponteiro. O “NULL” significa que
“próximo” não aponta para nada. O “new” já foi discutido e não quero falar sobre isso
denovo ^^.
Agora que já inicializamos, vamos adicionar alunos.
void Adiciona(aluno *&novos, char *nome, char *telefone, short int
sala, short int numero)
{
strcpy(novos>Nome, nome);
strcpy(novos>Telefone, telefone);
novos>Sala = sala;
novos>Numero = numero;
novos>proximo = new aluno;
novos = novos>proximo;
novos>proximo = NULL;
}
Aqui tempos comando novo e uma idéia ainda não explicada. O comando “strcpy()”; serve
para copiar uma string para outra. Quando terminamos de adicionar, criamos um novo
registro no lugar onde “proximo” apontava e fomos até ele. O ponteiro que indica o
“proximo” nesse novo registro recebe NULL. A seqüência de figuras falam por elas
mesmas.
Figura 16.2.1.3, modificando um “nó”:
71
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Figura 16.2.1.4, criando outro nó (nova chamada de Adiciona()):
Figura 16.2.1.5, modificando o novo nó criado:
Agora para buscar. Como fazer para ir ao registro anterior? Epa vamos voltar um pouco:
int main()
{
72
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
aluno *novos, *primeiro;
Inicializa(novos);
primeiro = novos;
return 0;
}
Crie um ponteiro para a primeira posição. Inicialize a nossa lista e indique ao primeiro
quem é o primeiro. Agora sim.
void Busca(aluno *novos, aluno *primeiro, short int numero)
{
novos = primeiro;
while(numero != novos>Numero || novos>proximo)
novos = novos>proximo;
cout << novos>Nome
}
A lista voltou à primeira posição e começou a busca. Enquanto não achar o número ou
enquanto houver lista, vá ao próximo elemento. Quando achar, imprima.
Agora que achamos, vamos deletar.
void Deleta(aluno *novos, aluno *primeiro, short int numero)
{
alunos *descarta;
novos = primeiro;
while(numero != novos>proximo>Numero || novos>proximo)
novos = novos>proximo;
if(novos>Numero == numero)
{
descarta = novos>proximo;
novos>proximo = novos>proximo>proximo;
delete descarta;
}
}
Em alguns casos não é preciso saber que é o elemento anterior. Use as LDSE
nesses casos.
Quando achar, verifique se é o correto. Se for memorize a posição, refaça as ligações e
apague o local memorizado.
Bom. Se você for um cara (ou uma) enjoado(a) deve achar uma gambiarra ter que criar
uma variável do tipo ponteiro para indicar o primeiro nó. Se você voltar um pouco vai se
lembrar que nas funções recursivas, após o término, a variável volta ao seu estado
original e é isso que vamos fazer (finalmente o algoritmo correto).
Inserção:
73
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
void Adiciona(alunos *&aluno, alunos *novo)
{
if(aluno>proximo>numero < novo>numero)
Adiciona(aluno>proximo, novo);
else if(aluno>proximo>numero >= novo>numero)
{
if(aluno)
{
novo>proximo = aluno>proximo;
aluno>proximo = novo;
}
else
novo>proximo = aluno;
}
}
Nossa, mais simples. Agora chegou a hora da busca.
bool Busca(alunos *aluno, short int &numero)
{
if(aluno>numero != numero && aluno>proximo)
Busca(aluno>proximo, numero);
else if(aluno>numero == numero)
{
cout << aluno>Nome;
return true;
}
else
return false;
}
Removendo:
bool Remove(alunos *&aluno, short int numero)
{
if(aluno>proximo>numero != numero && aluno>proximo)
Remove(aluno>proximo, numero);
else if(aluno>proximo>numero == numero)
{
alunos *remover = aluno>proximo;
aluno>proximo = aluno>proximo>proximo;
delete alunos;
return true;
}
else
return false;
}
74
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Fica como sugestão criar listas organizadas segundo um critério (ordem crescente de
número, por exemplo) já que essa é a idéia de uma lista. ORGANIZAÇÃO.
16.2.2 Listas Dinâmicas Duplamente Encadeadas
Talvez você concorde que a lista anterior parece meio idiota. Porém é possível criar um
ponteiro para o nó anterior também (hehe... lógico) para isso existem as LDDE. Sua
estrutura é quase a mesma só possui um ponteiro a mais.
struct aluno
{
aluno *anterior;
char Nome[30];
char Telefone[8];
short int Sala;
short int Numero;
aluno *proximo;
};
Declarando um novo ponteiro na main (Figura 16.2.2.1):
int main()
{
aluno *novos;
return 0;
}
Sua declaração é idêntica á LDSE e sua inicialização é semelhante (Figura 16.2.2.2).
void Inicializa(aluno *&novos)
{
novos = new aluno;
novos>proximo = novos>anterior = NULL;
}
Agora que já a declaramos e inicializamos, é hora de inserir elementos.
75
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
void Adiciona(aluno *&novos, char *nome, char *telefone, short int
sala, short int numero)
{
if(novos == NULL)
{
Inicializa(novos);
}
else
{
novos>proximo = new aluno;
novos>proximo>anterior = novos;
novos = novos>proximo;
novos>proximo = NULL;
}
strcpy(novos>Nome, nome);
strcpy(novos>Telefone, telefone);
novos>Sala = sala;
novos>Numero = numero;
}
A única diferença dessa primitiva de inserção para a das LDSE é uma linha. Essa linha
diz que o ponteiro para o registro anterior do próximo registro deve apontar para o registro
atual õO. Veja a seqüência de imagens:
Figura 16.2.2.3, modificando o primeiro nó:
76
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Figura 16.2.2.4, criando outro nó:
Figura 16.2.2.5, modificando o segundo nó:
O próximo registro (o que vem depois de Rodrigo) é o registro Samuel. O ponteiro
“proximo” do registro Rodrigo aponta para o Samuel. O ponteiro “anterior” do Samuel
deve apontar para Rodrigo. É só isso.
Veja que as LDDE são bem úteis. Então vamos tornálas mais úteis ainda. Quando você
adicionou um novo elemento, não houve a preocupação com uma SEQUÊNCIA, portanto
vamos modificar o algoritmo de inserção para inserir na seqüência. Além disso, vamos
agora passar uma estrutura como argumento. Se inventaram a estrutura para armazenar
nossos dados e uma estrutura é um tipo de dado, é possível passala como argumento.
bool Adiciona(aluno *&novos, aluno *&insere)
{
if(novos == NULL)
{
Inicializa(novos);
}
else
{
aluno *aux = new aluno;
while(novos>proximo && novos>Numero <= insere>Numero)
novos = novos>proximo;
while(novos>anterior && novos>Numero > insere>Numero)
novos = novos>anterior;
aux>proximo = novos>proximo;
aux>anterior = novos>anterior;
77
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
novos>proximo = new aluno;
novos>proximo>anterior = novos;
novos = novos>proximo;
novos>proximo = aux>proximo;
if(aux>anterior)
aux>anterior = novos;
}
strcpy(novos>Nome, insere>Nome);
strcpy(novos>Telefone, insere>Telefone);
novos>Sala = insere>Sala;
novos>Numero = insere>Numero;
}
Foi preciso implementar um método de busca. Vamos criar uma função para isso.
bool Busca(aluno *&alunos, short int numero)
{
while(alunos>proximo && alunos>numero < numero)
alunos = alunos>proximo;
while(alunos>anterior && alunos>numero > numero)
alunos = alunos>anterior;
if(alunos>numero == numero)
{
cout << alunos>Nome;
return true;
}
else
return false;
}
Caso você queira remover, basta saber se o número é maior ou menor que o número
atual.
bool Remove(aluno *&alunos, short int numero)
{
if(Busca(alunos, numero))
{
alunos>anterior>proximo = alunos>proximo;
alunos>proximo>anterior = alunos>anterior;
return true;
}
return false;
}
As LDDE são ótimas para todos os casos. Usea em primeiro caso. Caso o
conhecimento do elemento anterior seja inútil faça dela uma LDSE. Se o número
de elementos for constante use uma LEE.
78
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
16.2.2.1 Listas Circulares
Para resolver o problema das LDDE, foi criado um tipo particular chamado Lista Circular
(LC). Uma LC é uma LDDE, com a diferença que o ponteiro “proximo” do ultimo registro
aponta para o primeiro registro e o ponteiro “anterior” do primeiro registro aponta para o
ultimo registro (Figura 16.2.2.1.1).
A estrutura é igual. Mas vamos criar um ponteiro que indica o primeiro elemento:
struct aluno
{
aluno *anterior;
char Nome[30];
char Telefone[8];
short int Sala;
short int Numero;
aluno *proximo;
} *primeiro;
79
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Inicialização:
void Inicializa(aluno *&novos)
{
novos = new aluno;
primeiro = novos;
novos>proximo = novos>anterior = novos;
}
Agora o algoritmo Adiciona:
void Adiciona(aluno *&novos, char *nome, char *telefone, short int
sala, short int numero)
{
if(novos == NULL)
{
Inicializa(novos);
}
else
{
novos>proximo = new aluno;
novos>proximo>anterior = novos;
novos = novos>proximo;
novos>proximo = NULL;
novos>proximo = primeiro;
primeiro>anterior = novos;
}
strcpy(novos>Nome, nome);
strcpy(novos>Telefone, telefone);
novos>Sala = sala;
novos>Numero = numero;
}
Agora precisamos criar um registro a parte e copiar os dados para ele. O ponteiro
“proximo” do novo registro deve apontar para onde o “proximo” do registro atual aponta. O
ponteiro “anterior” do próximo registro deve apontar para o novo registro. O ponteiro
“proximo” do registro atual deve apontar para novo e finalmente o ponteiro “anterior” do
novo registro deve apontar para o registro atual.
LC é uma LDDE. Nunca se esqueça disso!
Exercícios
1) Implemente as primitivas de inicialização, remoção e busca.
80
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Capítulo 17 Árvores
A parte de árvores é um pouco mais complicada pois utiliza funções recursivas. Se você
não entendeu funções recursivas E LD’s você deve voltar e estudar mais!
Árvores tem tudo a ver com I.A. (inteligência artificial) e I.A. tem tudo a ver com
jogos. Se você quer ser um programador de jogos entenda árvores. Não pare
nesse livro, faça algumas pesquisas na internet e compre livros mais específicos
sobre estrutura de dados
17.1 Árvores binárias de busca
A primeira coisa para se entender de uma árvore é que ela serve para melhorar o sistema
de busca, tanto para o programador quanto para o usuário. Imagine a seguinte situação:
"Partindo de uma lista com 'n' valores busque um valor 'x'". Você teria que saber a
posição e se o número fosse maior que o atual você percorreria à direita e se fosse menor
à esquerda. Se você estivesse na posição 1, teria que percorrer todas as 'n' posições
caso ele fosse o último elemento. Uma árvore binária de busca é aquela em que à
esquerda de cada nó existe um valor menor que este e à direita um número maior. Para
entender melhor analise esta árvore:
47
54
39
Se você quisesse buscar o elemento maior dessa árvore, percorreria direita direita e
encontraria o 70. Se isso fosse uma lista e você estivesse no primeiro nó, você teria que
percorrer 6 posições até encontrar o maior. A árvore é binária pois possui apenas 1 nó à
direita e 1 à esquerda. Para percorrêla usaremos uma função que chama o nó direita
caso o valor desejado seja maior e o nó esquerda caso seja menor. Vamos primeiramente
fazer uma montagem esquemática da nossa função.
Não tenha vergonha de fazer algoritmos. Tudo o que você aprendeu é útil até o
final da sua carreira.
Procure o elemento 51.
51 é maior que 47? Sim! então percorra à direita.
51 é maior que 54? Não!. 51 é menor que 54? Sim!, então percorra à esquerda.
51 é maior que 51? Não!. 51 é menor que 51? Não!, se 51 for igual ao dado do nó,
imprima "51", senão imprima "elemento não encontrado".
81
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Veja que precisaremos de 3 variáveis (no mínimo). Um valor e dois ponteiros. Portanto
vamos definilos:
struct ArvoreBB
{
ArvoreBB *esquerda;
int valor;
ArvoreBB *direita;
};
Agora vamos à primitiva de busca:
void BuscaABB(int valor, ArvoreBB *arvore)
{
if(valor > arvore>valor)
BuscaABB(valor, arvore>direita);
else if(valor < arvore>valor)
BuscaABB(valor, arvore>esquerda);
else if(valor == arvore>valor)
cout << arvore>valor << endl;
else
cout << "elemento não encontrado...\n";
}
82
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Capítulo 17 Objetos
O que é um objeto no mundo real para você? Para mim é "alguma coisa" que eu possa
tocar, ver, sentir, etc. Pensando assim, podemos dizer que um objeto possue atributos
como tamanho (para que você possa tocar), cores (para que você possa ver), cheiro (para
que você possa sentir) e por aí vai. Pensando assim, podemos caracterizar uma bola
(eita! exemplo criativo ~~). Uma bola deve possuir:
Cores (ou ausência delas)
Raio
Utilidade (futebol, volei, etc)
Material (borracha, couro, etc)
e tudo que sua mente for capaz de identificar em uma bola
Falando em termos de programação, tudo o que é objeto no mundo real possuí
caracteristicas que variam de um para outro mas mesmo assim possuem muito em
comum (você tem um computador que talvez seja diferente do computador da sua escola
mas ambos são computadores). Uma bola também deve possuir uma posição x, y, z no
espaço, pode estar no chão ou não, possuem coeficiente elástico e outras características
que podem variar com o tempo. Essas características precisam de uma função para
modificalas o tempo todo (se alguém chuta uma bola ela entra em movimento). Porém, o
movimento da bola é algo próprio da bola e a partir do chute ela é responsável pelo
próprio movimento. Pensando nisso ela precisaria de uma função que modificasse suas
coordenadas. Pronto! Temos uma bola quase completa! Vamos agora criar o código:
class CBola
{
};
A partir desse momento nós temos a "idéia" bola. Esse código criou uma classe chamada
CBola (o C em CBola é opcional... mas ajuda a organizar o código). Criar uma classe
significa criar a idéia bola. Criar a idéia bola não é criar a bola (todos sabemos como é
uma bola mas nem todos possuímos fábrica de bolas). Quando só temos a idéia
chamamos de classe. Quanto criamos de fato chamamos de objeto. Agora vamos aos
atributos da bola:
class CBola
{
private:
int m_cor;
double m_raio;
int m_utilidade;
int m_material;
float y;
float x;
float z;
float m_aceleracao;
float m_velocidade;
bool m_chao;
83
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
double m_coeficiente_elastico;
public:
};
Agora caso você queira criar uma bola você já... pode? Repare em duas palavras novas:
"private" e "public". Existem outras palavras reservadas mas por enquanto vamos nos
concentrar nessas duas. Tudo o que estiver depois de "private" e antes de "public" será
privado à classe, ou seja, é um atributo dela que NÃO PODE SER MODIFICADO
DIRETAMENTE como em uma struct (aha! finalmente uma diferença). Isso quer dizer que
NÃO TEM COMO você tratar uma variável privada escrevendo: "CBola.x = 10.0f". Para
isso vamos usar funções que NORMALMENTE e NÃO OBRIGATORIAMENTE ficam na
área pública. A área pública significa que você pode acessar. Primeiramente vamos criar
a função de construção da bola. Essa função será chamada automaticamente quando
você instanciar (errr... tipo declarar variável) a classe. Instanciar a classe é declarala
dentro de uma função como a "int main".
class CBola
{
private:
int m_cor;
double m_raio;
int m_utilidade;
int m_material;
float y;
float x;
float z;
float m_aceleracao;
float m_velocidade;
bool m_chao;
double m_coeficiente_elastico;
public:
CBola();
};
CBola::CBola()
{
m_cor = 0;
m_raio = 0;
m_utilidade = 0;
m_material = 0;
y = 0;
x = 0;
z = 0;
m_aceleracao = 0;
m_velocidade = 0;
m_chao = false;
m_coeficiente_elastico = 0;
}
84
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
O que eu acabei de criar se chama “construtor”. Um construtor pode ser padrão ou não.
Podemos ter vários construtores NÃO padrões na mesma classe. Esse negócio nada
mais é que uma função que, como eu já disse, será chamada automaticamente quando
você instanciala. Vamos agora instanciar:
int main()
{
CBola bola;
return 0;
}
Uau! Emocionante! Agora você fabricou uma bola. O construtor padrão já foi chamado e
todas as variáveis valem o que foi previamente definido. Outra função muito útil que você
pode querer implementar em uma classe é a função imprime. Essa função vai mostrar o
estado de todas as variáveis da classe.
...
public:
CBola();
void imprime();
};
void CBola::imprime()
{
cout << “Cor: “ << m_cor << “\nRaio: “ << m_raio <<
“\nUtilidade: “ << m_utilidade << “\nMaterial: “ << m_material <<
“\nX: “ << x << “\nY: “ << y << “\nZ: “ << z << “\nAceleração: “
<< m_aceleracao << “\nVelocidade: “ << m_velocidade << “\nEstá no
chão? “ << m_chao << “\nCoeficiente elástico: “ <<
m_coeficiente_elastico;
}
Se você for fazer um jogo você precisará de algo a mais do que a velocidade e
aceleração em um só eixo. Mas isso é apenas um exemplo e colocar
“m_aceleracaox, m_aceleracaoy, m_aceleracaoz” seria perda de tempo
Se você quiser imprimir os dados do seu objeto basta chamar a função:
int main()
{
CBola bola;
bola.imprime();
return 0;
}
Agora, vamos supor que seu personagem chutou a bola. Você vai precisar mudar a
aceleração da bola! Não precisa pensar muito (se você leu todos os capítulos anteriores)
para chegar à conclusão que basta criar uma função com um parâmetro para receber um
valor e modificar a variável membro da classe.
85
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
...
public:
CBola();
void imprime();
void foiChutada(float);
};
Repare que até agora, dentro da classe só criamos protótipos das funções. Criar um
protótipo não é obrigatório, ou seja, você pode criar a função dentro da classe e já
implementala. Como somos programadores bons e organizados vamos implementar
sempre fora da classe.
void CBola::foiChutada(float nova_accel)
{
m_aceleracao = nova_accel;
}
int main()
{
CBola bola;
bola.imprime();
bola.foiChutada(4.8);
return 0;
}
Se você tiver a curiosidade de chamar imprime novamente verá que a bola acelerou!
Acredite. É exatamente assim que funciona em um jogo normal. O resultado é o
que você vê na tela. Internamente é isso aí. O que acontece é que temos um
loop principal que executa tudo o tempo todo. Enquanto a bola tiver aceleração
ele atualiza as posições e mostra na tela. Emocionante?
Para criarmos bolas diferentes, não é muito prático criar uma bola comum e depois
modificala. Vamos então criar construtores diferentes.
...
public:
CBola();
CBola(int, double);
void imprime();
void foiChutada(float);
};
Não deve ser novidade para você duas funções com nomes iguais mas o número de
argumentos e seus tipos diferindo.
CBola::CBola(int material, double coeficiente_elastico)
{
m_cor = 0;
86
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
m_raio = 0;
m_utilidade = 0;
m_material = material;
y = 0;
x = 0;
z = 0;
m_aceleracao = 0;
m_velocidade = 0;
m_chao = false;
m_coeficiente_elastico = coeficiente_elastico;
}
Esse construtor atribui valores diferentes ao coeficiente e ao material. Para chamalo:
int main()
{
CBola bola(2, 3.1);
bola.imprime();
bola.foiChutada(4.8);
return 0;
}
Agora nós já podemos criar a bola de dois jeitos diferentes, mostrala e chutala. Agora
falta destruir a bola. Esse destrutor/destruidor pode desalocar áreas de memória que você
criou para esse objeto ou simplesmente destruir o objeto e pronto. Para mostrar como
desalocar uma área na memória vamos criar uma nova variável do tipo ponteiro.
private:
int m_cor;
int *m_ponteiro_teste;
...
Agora, no construtor não padrão vamos alocar essa variável.
CBola::CBola(int material, double coeficiente_elastico)
{
m_cor = 0;
m_raio = 0;
m_utilidade = 0;
m_material = material;
y = 0;
x = 0;
z = 0;
m_aceleracao = 0;
m_velocidade = 0;
m_chao = false;
m_coeficiente_elastico = coeficiente_elastico;
m_ponteiro_teste = new int[10];
}
87
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
Depois disso é hora de criar a função do tipo destrutor. Ela possui sintaxe semelhante à
construtora com a diferença de ter um “til” antes de seu nome.
...
public:
CBola();
CBola(int, double);
~CBola();
void imprime();
void foiChutada(float);
};
Essa função é padrão e só existe o destrutor padrão. Você não pode ter vários
destrutores já que ele sempre é chamado automaticamente. Implementandoo.
CBola::~CBola()
{
cout << “Destruindo o objeto...\n”;
delete [] m_ponteiro_teste;
}
Para você ver que funciona e acreditar que o destrutor é chamado automaticamente, crie
uma função que crie o objeto dentro de seu escopo somente. O código ficaria mais ou
menos assim:
88
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
001. #include <iostream>
002. #include <conio.h>
003.
004. using namespace std;
005.
006. class CBola
007. {
008. private:
009. int m_cor;
010. int *m_ponteiro_teste;
011. double m_raio;
012. int m_utilidade;
013. int m_material;
014. float y;
015. float x;
016. float z;
017. float m_aceleracao;
018. float m_velocidade;
019. bool m_chao;
020. double m_coeficiente_elastico;
021. public:
022. CBola();
023. CBola(int, double);
024. ~CBola();
025. void imprime();
026. void foiChutada(float);
027. };
028.
029. CBola::CBola(int material, double coeficiente_elastico)
030. {
031. m_cor = 0;
032. m_raio = 0;
033. m_utilidade = 0;
034. m_material = material;
035. y = 0;
036. x = 0;
037. z = 0;
038. m_aceleracao = 0;
039. m_velocidade = 0;
040. m_chao = false;
041. m_coeficiente_elastico = coeficiente_elastico;
042. m_ponteiro_teste = new int[10];
043. }
89
Curso C++ para criação de jogos (Básico) – Por: Rodrigo da Silva Vaz
044.
045. CBola::CBola()
046. {
047. m_cor = 0;
048. m_raio = 0;
049. m_utilidade = 0;
050. m_material = 0;
051. y = 0;
052. x = 0;
053. z = 0;
054. m_aceleracao = 0;
055. m_velocidade = 0;
056. m_chao = false;
057. m_coeficiente_elastico = 0;
058. }
059.
060. void CBola::foiChutada(float nova_accel)
061. {
062. m_aceleracao = nova_accel;
063. }
064.
065. void CBola::imprime()
066. {
067. cout << “Cor: “ << m_cor << “\nRaio: “ << m_raio << “\nUtilidade: “
068. << m_utilidade << “\nMaterial: “ << m_material << “\nX: “ << x << “\nY: “
069. << y << “\nZ: “ << z << “\nAceleração: “ << m_aceleracao <<
070. “\nVelocidade: “ << m_velocidade << “\nEstá no chão? “ << m_chao <<
071. “\nCoeficiente elástico: “ << m_coeficiente_elastico;
072. }
073.
074. CBola::~CBola()
075. {
076. cout << “Destruindo o objeto...\n”;
077. delete [] m_ponteiro_teste;
078. }
079.
080. void funcao()
081. {
082. CBola bola(1, 1.2);
083. cout << “Saindo do escopo da função...\n”;
084. }
085.
086. int main()
087. {
088. funcao();
089. getch();
090. return 0;
091. }
092.
093.
094.
095.
90