132
UNIVERSIDADE FEDERAL DE SANTA CATARINA DESENVOLVIMENTO DE UM MOTOR PARA JOGOS 2D Ewerton Conceição Florianópolis - Santa Catarina 15 de maio de 2012

UNIVERSIDADE FEDERAL DE SANTA CATARINAF3rio%20de%20TCC%20II.pdf · 3 Sumário Lista de Figuras ... como acontece no popular Age of Empires; Estratégia baseada em turnos: as batalhas

  • Upload
    lythien

  • View
    214

  • Download
    0

Embed Size (px)

Citation preview

UNIVERSIDADE FEDERAL DE SANTA CATARINA

DESENVOLVIMENTO DE UM MOTOR PARA JOGOS 2D

Ewerton Conceição

Florianópolis - Santa Catarina

15 de maio de 2012

1

UNIVERSIDADE FEDERAL DE SANTA CATARINA

DEPARTAMENTO DE INFORMÁTICA E ESTATÍSTICA

CURSO DE CIÊNCIAS DA COMPUTAÇÃO

DESENVOLVIMENTO DE UM MOTOR PARA JOGOS 2D

Ewerton Conceição

Trabalho de Conclusão de Curso apresentado como parte

dos requisitos para a obtenção do grau de Bacharel em

Ciências da Computação.

Florianópolis - Santa Catarina

15 de maio de 2012

2

Ewerton Conceição

DESENVOLVIMENTO DE UM MOTOR PARA JOGOS 2D

Trabalho de Conclusão de Curso apresentado como parte dos requisitos para a

obtenção do grau de Bacharel em Ciências da Computação.

Orientador: Prof. Raul Sidnei Wazlawick

Banca Examinadora

Prof. Antônio Carlos Mariani

Prof. Mauro Roisenberg

3

Sumário

Lista de Figuras

Lista de Acrônimos

Resumo

Abstract

1 Introdução ........................................................................................................... 9

1.1 Objetivos ............................................................................................................................... 10

1.1.1 Objetivo Geral ...................................................................................................................... 10

1.1.2 Objetivos Específicos ............................................................................................................ 10

1.2 Metodologia .......................................................................................................................... 10

1.3 Organização do Trabalho ...................................................................................................... 11

2 Fundamentos .................................................................................................... 12

2.1 Estilos de Jogos ...................................................................................................................... 12

2.2 Funcionamento Básico de um Jogo ....................................................................................... 12

2.3 Organização das Entidades do Jogo ...................................................................................... 13

3 Módulo de Desenho .......................................................................................... 17

3.1 Posicionando os Objetos no Mapa ........................................................................................ 17

3.2 Animações ............................................................................................................................. 18

4 Módulo de Física ............................................................................................... 21

4.1 Detecção de Colisão a Posteriori ........................................................................................... 21

4.2 Detecção de Colisão a Priori .................................................................................................. 22

4.3 Etapas da detecção de colisão .............................................................................................. 22

4.3.1 Etapa Ampla .................................................................................................................. 22

4.3.2 Etapa Específica ............................................................................................................. 23

4.4 Coerência Temporal e Coerência Espacial ............................................................................ 23

4.5 Técnicas de Particionamento do Espaço ............................................................................... 24

4.5.1 Quadtree ....................................................................................................................... 24

4.6 Implementação da Detecção de Colisões no Motor ............................................................. 25

4.7 Implementação do Tratamento de Colisões ......................................................................... 25

4.8 Implementação da Simulação de Física ................................................................................ 26

5 Módulo de Áudio ............................................................................................... 28

4

6 Módulo de Inteligência Artificial ...................................................................... 30

6.1 Comportamento de Agentes Baseado em Objetivos ............................................................ 30

6.1.1 Definições ...................................................................................................................... 30

6.1.2 Uso e Implementação ................................................................................................... 31

6.2 Lógica Difusa.......................................................................................................................... 34

6.2.1 Definições ...................................................................................................................... 34

6.2.2 Uso e Implementação ................................................................................................... 34

7 Considerações Finais ....................................................................................... 38

8 Referências Bibliográficas ............................................................................... 39

Apêndice A – Código Fonte .................................................................................... 40

5

Lista de Figuras

Figura 2.1 Hierarquia orientada a objetos ................................................................. 14

Figura 3.1 Arquivo texto, contendo as posições dos tiles no mapa ........................... 17

Figura 3.2 Mapa de tiles carregado ........................................................................... 18

Figura 3.3 Sprite usada para fazer o Bomberman andar .......................................... 18

Figura 3.4 Principais classes envolvidas em uma animação .................................... 19

Figura 4.1 Volumes envolventes (Kimmerle, 2005) ................................................... 23

Figura 4.2 Quadtree com profundidade igual a 2 ...................................................... 24

Figura 4.3 Componente de física .............................................................................. 27

Figura 5.1 Diagrama de classes do módulo de sons ................................................. 28

Figura 6.1Diagrama de classes da técnica de comportamento de agentes baseados

em objetivos .............................................................................................................. 32

Figura 6.2 Grau de desejo de se executar um objetivo ............................................. 35

Figura 6.3 Quantidade de pontos de vida ................................................................. 35

Figura 6.4 Distância a um item .................................................................................. 36

6

Lista de Acrônimos

2D Duas dimensões

3D Três dimensões

AABB Axis-aligned Bounding Box

CPU Central Processing Unit

DOP Discrete Oriented Polytope

IA Inteligência Artificial

OBB Oriented Bounding Box

RAM Random Access Memory

RPG Role-playing Game

SPU Synergistic Processing Unit

7

RESUMO

No desenvolvimento de jogos eletrônicos, é interessante reutilizar código-fonte, para que o esforço na criação de novos jogos seja diminuído. Os motores de jogos têm esse propósito. No entanto, não há um motor adotado como padrão. Neste trabalho, foi desenvolvido um motor para a família de jogos com jogabilidade 2D. Para isso, foram desenvolvidos pequenos jogos, verificando-se partes do código-fonte que possam ser extraídas para módulos do motor e serem reutilizadas. Foram criados módulos que tratam da parte gráfica, detecção de colisões, sons e inteligência artificial. Neste trabalho, são discutidos conceitos envolvendo os módulos citados anteriormente bem como conceitos básicos sobre programação de jogos.

8

ABSTRACT

In the development of electronic games, it is interesting to reuse source code, so that the effort to create new games is reduced. The game engines have this purpose. However, there is no standard engine. In this work, it was developed an engine for the family of games with 2D gameplay. Thus, it was developed small games, verifying parts of the source code that can be extracted for modules of the engine and reuse. Modules were created that deal with graphics, collision checking, sound and artificial intelligence. In this work, it is discussed concepts involving the modules mentioned above as well as basic concepts of game programming.

9

1 Introdução

Simplesmente mentalizando o termo genérico “jogo”, podemos pensar em

vários tipos de jogos, como jogos de cartas, esportes e jogos eletrônicos. Quando

usado no contexto de jogos eletrônicos, normalmente o termo jogo vem associado a

mundos 2D ou 3D, contendo um avatar, que pode ser um humano, um animal ou

um veículo, como personagem principal sob controle de um jogador (Gregory, 2009).

Segundo (Sanches, 2009) um jogo é um aplicativo multimídia que funciona

em tempo real, provendo uma resposta da forma mais rápida possível ao jogador.

Este tem a impressão que o jogo avança como um filme ou uma cena da vida real,

apesar de, na prática, o funcionamento ser diferente.

O termo “motor de jogos” começou a ser usado na década de 1990, fazendo

referência à uma conexão com alguns jogos 3D, como de tiro em primeira pessoa,

como o conhecido Doom, da empresa id Software. A arquitetura do jogo Doom tinha

uma separação bem definida entre os componentes principais do software (como

sistema de renderização de gráficos 3D, sistema de detecção de colisão, sistema de

áudio) e as artes, os mundos, e as regras do jogo (Gregory, 2009).

O valor dessa separação se tornou evidente quando os desenvolvedores

notaram que bastaria criar novas armas, cenários e personagens sem precisar fazer

grandes modificações no motor do jogo. Assim, grande parte do código-fonte de um

jogo poderia ser utilizado novamente em vários outros, como bibliotecas para vários

módulos, fazendo com que o trabalho dos desenvolvedores de jogos fosse

diminuído.

Como durante a criação dos primeiros jogos eletrônicos da história existiam

muitas limitações de hardware, o código-fonte tinha que ser muito bem otimizado.

Não havia separação entre partes do jogo, como um subsistema para renderizar os

gráficos e outro para simular fenômenos físicos.

Hoje em dia, existem vários motores para jogos. Alguns disponibilizam uma

biblioteca de classes, outros um editor que permite construir um jogo a partir de uma

interface gráfica, e também existem os que disponibilizam as duas formas. Como

exemplo de motores de jogos, podemos citar: Unreal Engine 3, CryENGINE 3, Unity

3D, Torque 3D e RPG Maker.

10

No entanto, nenhum motor é adotado como padrão para desenvolvimento de

jogos. Geralmente, ou é escolhido um motor que seja fácil de usar, do ponto de vista

dos desenvolvedores, e que atenda aos requisitos do jogo que se quer fazer ou cria-

se um novo motor, muitas vezes sendo responsável por apenas um módulo do jogo,

como um módulo de física, por exemplo.

1.1 Objetivos

Procurando limitar e também nortear o escopo do trabalho, foram definidos

objetivos para o desenvolvimento do motor. Esses objetivos serão apresentados a

seguir, de forma geral e específica.

1.1.1 Objetivo Geral

Desenvolver um motor para jogos 2D, com foco na família dos jogos de RPG,

contendo uma biblioteca desenvolvida em C++ que facilite a criação de jogos.$

1.1.2 Objetivos Específicos

1. Desenvolver uma biblioteca que deve conter módulos que tratem da parte

gráfica, de detecção de colisões, de sons e de inteligência artificial;

2. Comparar técnicas de detecção de colisão em duas dimensões para

otimizar o detector de colisões;

3. Verificar na prática os desafios envolvidos na criação de um motor de

jogos.

1.2 Metodologia

Cada família de jogos tem certos requisitos específicos que, muitas vezes, só

são descobertos quando são desenvolvidos pequenos protótipos. Alguns requisitos

são comuns a alguns tipos de jogos, como detecção de colisão e animações,

podendo ser diferente a forma como deve ser implementado (colisão em 2D e em

3D, por exemplo).

11

Um problema na criação de motores de jogos é tentar fazer o maior motor

possível, adicionando coisas que nunca serão usadas. No artigo Starting out on

Game Programming do site Lazy Foo Productions, o autor conta que queria fazer

uma IA para um jogo da velha que fosse imbatível. Ele a fez de uma forma que

pudesse vencer cada armadilha possível. Para isso, ele precisou de quarenta mil

linhas de código, sendo que a maioria do código foi copiado e colado, gastando um

mês do seu tempo. Depois que ele descobriu o algoritmo Minimax, viu que poderia

ter feito de uma forma melhor e com muito menos código.

Adicionar funcionalidades que nunca serão usadas pelo tipo de jogo que se

deseja construir com o motor é um problema. Para evitá-lo, o desenvolvimento do

motor foi feito com base em protótipos, que são pequenos jogos. Desses jogos,

foram retirados os requisitos do motor. Partes de código que podem reusadas em

outros jogos ajudaram a compor os módulos do motor. O código foi escrito de forma

que fosse flexível e sem adicionar coisas desnecessárias, até o momento em que

realmente seja preciso adicioná-las.

1.3 Organização do Trabalho

O capítulo 2 fala sobre fundamentos dos jogos. O capítulo 3 mostra como foi

feito o módulo de desenho e detalha conceitos sobre animações. O capítulo 4 trata

do módulo de física e explica a detecção de colisões. O capítulo 5 expõe o módulo

de áudio. O capítulo 6 fala sobre o módulo de IA. O capítulo 7 mostra as

considerações finais.

12

2 Fundamentos

2.1 Estilos de Jogos

Existem vários estilos de jogos. De acordo com (Fernandes, 2002), alguns

deles são:

Estratégia em tempo real: normalmente são jogos de batalha, em que o

jogador comanda exércitos, suprimentos, acampamentos e recursos para

superar grupos adversários, como acontece no popular Age of Empires;

Estratégia baseada em turnos: as batalhas ocorrem em tempos

espaçados para que as ações sejam realizadas, o que nos dá tempo para

pensar, mas não diminui a ação. Tornaram-se populares com o

surgimento dos RPGs;

Tiro em primeira pessoa: o jogador controla um personagem, que pode

ser um soldado, por exemplo, e pode usar diversos tipos de armas para

matar os inimigos, como no jogo Battlefield;

Plataformas: conta com rolagem lateral, em que o personagem principal

passa por fases, podendo encontrar um chefe final em cada fase. Como

exemplo podemos citar vários jogos da franquia Super Mario;

Simulação: são jogos que imitam a realidade, simulando corridas de

carros, futebol, voo, entre outros;

RPGs: são jogos que podem conter um ou mais de outros estilos citados

acima. Sua principal característica está no fato de os personagens

possuírem um nível, que aumenta conforme vão ganhando experiência.

Como exemplo, podemos citar jogos da franquia Final Fantasy.

2.2 Funcionamento Básico de um Jogo

Para que um jogador tenha a sensação de que o jogo está fluindo

naturalmente, o processamento dos comandos do jogador, da lógica do jogo e a

atualização da tela tem que ser feitos de forma rápida e sincronizada. Para fazer

isso, um jogo possui o laço principal, responsável pela atualização do jogo, seguindo

quatro passos:

13

1. Ler entradas do mouse e teclado;

2. Atualizar os objetos do jogo;

3. Desenhar a cena a partir dos objetos atualizados;

4. Voltar para o passo 1 caso o jogo não tenha sido finalizado.

O jogo é atualizado de forma discreta e a velocidade de atualização dos

objetos é fixa, de acordo com a taxa de quadros por segundo informada. Devido aos

monitores trabalharem, normalmente, com uma taxa de atualização de 60 quadros

por segundo, é comum essa taxa ser usada para atualizar um jogo. Se a taxa de

atualização for maior que a suportada pelo monitor, alguns quadros serão

descartados, mesmo tendo feito todo o processamento necessário para gerá-los.

Assim, com o intuito de evitar gerar quadros que serão descartados, é interessante

limitar a taxa de atualização, o que tende a diminuir o consumo de CPU.

2.3 Organização das Entidades do Jogo

Cada jogo possui diferentes tipos de entidades, cada uma responsável por

certas tarefas. Normalmente, as entidades são visíveis ao jogador, ocupando algum

lugar no mundo e algumas podem se locomover. Pensando num jogo de aventura,

podemos imaginar algumas possíveis entidades:

Herói;

Inimigo;

Espada;

Arma de fogo;

Carro;

Submarino;

Barco.

A forma tradicional de representar as entidades de um jogo como esse é fazer

uma decomposição orientada a objetos das classes necessárias para o

funcionamento das entidades. Isso geralmente começa com boas intenções, mas é

frequentemente modificado com o progresso de desenvolvimento do jogo,

principalmente se um motor de jogo é usado novamente para criar um jogo diferente

(West, 2007).

14

A figura 2.1 mostra uma possível representação de uma hierarquia orientada

a objetos do jogo de aventura mostrado anteriormente:

Figura 2.1 Hierarquia orientada a objetos

É comum, nos jogos, haver uma variação grande dos requisitos, pois ideias

de como melhorar o jogo podem surgir no meio do desenvolvimento do mesmo.

Suponhamos que, no exemplo do jogo de aventura apresentado anteriormente,

alguém teve a ideia de criar um veículo anfíbio. A hierarquia envolvendo o veículo

anfíbio pode ser vista na figura 2.2, que é uma forma clássica de ilustrar um

problema da herança múltipla, quando uma classe estende a outra duas vezes. A

classe que representa um veículo anfíbio estende a classe que representa um

veículo duas vezes.

Esse tipo de hierarquia (mostrada na figura 2.1), com uma profunda cadeia de

heranças, tende a possuir uma classe na raiz e nas folhas as classes representando

entidades mais concretas, que são, comumente, visíveis ao jogador. Num jogo real,

a hierarquia tende a ser bem mais profunda e complexa, envolvendo animações,

corpos rígidos e outros componentes, deixando, possivelmente, as classes das

folhas com muitos métodos que foram herdados e que nunca serão chamados, ou

seja, são funcionalidades desnecessárias em certas entidades, mas que estão numa

determinada classe devido à profunda cadeia de heranças.

Profundas hierarquias de heranças tendem a criar problemas de

desempenho. Eles ocorrem quando é necessário chamar o construtor de uma classe

15

derivada, tendo que invocar o construtor das classes bases, o que faz o uso de

recursos pesados, como alocação de memória para classe base, além da derivada.

(Flynt; Salem, 2004).

Figura 2.2 Herança Múltipla.

Outra abordagem para representar os objetos do jogo é uma agregação de

componentes. Uma forma de fazer isso é considerar uma entidade como sendo uma

soma de componentes. Cada entidade poderia ter como componentes um corpo

rígido para realizar simulações de física e um componente de animação para cuidar

da sua representação visual, por exemplo. Esse é um modelo simples, porém não é

o mais adequado e pode ser melhorado (Flynt; Salem, 2004).

Para explicar melhor o modelo de componentes, o passo 2 do laço principal

será detalhado agora. Primeiro, consideremos que o jogo possui um conjunto de

entidades, cada uma com seus componentes. O jogo pode ser atualizado conforme

o procedimento abaixo, em pseudocódigo:

Jogo::atualizar(real tempo)

início

1. Para cada e ϵ jogo.entidades faça

a. Para cada c ϵ e.componentes

i. c.atualizar(tempo)

fim

16

O procedimento acima é bastante simples, mas existe outra forma para usar o

modelo de componentes e que é superior à primeira: a atualização em lotes. Nesse

modelo, ao invés das entidades atualizarem todos os seus componentes, cada

conjunto de um dado tipo de componente deve ser atualizado de uma vez só. Uma

classe gerenciadora pode ser criada para cada tipo de componente para realizar a

tarefa de atualização.

Considerando que certo jogo possui apenas os componentes de física e de

animação, o passo 2 do laço principal (método “atualizar” na classe “Jogo”) chamaria

o método “atualizar” no objeto que gerencia a simulação de física e no objeto que

gerencia as animações. O método “atualizar” na classe que gerencia os

componentes apenas faz uma iteração sobre todos os componentes que possui,

chamando o método “atualizar” de cada componente.

A atualização em lotes traz vários benefícios. De acordo com (Gregory,

2009), alguns deles são:

Máxima coerência na memória cache: os dados de um subsistema do

motor são mantidos internamente e podem ser arranjados numa região

única e contígua da memória RAM, o que leva a máxima coerência

possível na memória cache;

Pipeline eficiente: recursos de hardware especializados podem ser

usados de forma mais eficiente, ganhando mais uma otimização. Como

exemplo, podemos citar o Playstation 3, que possui um conjunto de

microprocessadores de alta velocidade, chamados de SPUs, cada um

com sua área de memória de alta velocidade. Enquanto se processa um

lote de animações, a pose de um personagem pode ser calculada e, ao

mesmo tempo, os dados do próximo personagem são acessados

diretamente na memória da SPU. Processando cada entidade

isoladamente não seria atingido esse nível de paralelismo;

Alguns subsistemas não funcionam adequadamente isolados:

uma solução satisfatória para resolver se vários corpos rígidos estão

colidindo não pode ser achada, geralmente, se os objetos forem tratados

isoladamente. As colisões entre os objetos devem ser resolvidas em

grupo, através de uma abordagem iterativa ou resolvendo um sistema de

equações lineares, por exemplo.

17

3 Módulo de Desenho

O motor de jogos desenvolvido contém um módulo responsável por desenhar

os objetos visíveis ao jogador e também realizar animações. Para tornar o módulo

de desenho mais completo, foi usada a biblioteca Guichan, que contém

componentes mais básicos de desenho como áreas de texto, rótulos, botões, entre

outros.

3.1 Posicionando os Objetos no Mapa

Nos jogos 2D, duas formas comuns de jogabilidade são a rolagem lateral e a

perspectiva top-down. Na primeira, a câmera fica posicionada lateralmente e os

personagens se movem de um lado para o outro. Na segunda, também conhecida

como bird’s-eye view, a câmera mostra o jogador e a área em torno dele vistos de

cima. Para que se atenda a essas duas formas de jogabilidade, os objetos que

podem ser desenhados possuem como posição um ponto no mundo.

Outra forma possível para representar a posição dos objetos é usar um mapa

de ladrilhos. Segundo (Fernandes, 2001), um ladrilho é um desenho pequeno, que

se caracteriza por formar uma única imagem, sem cortes, quando colocam-se vários

desenhos iguais, lado a lado. São ideais para representar um chão, florestas ou

elementos estáticos de um jogo.

Figura 3.1 Arquivo texto, contendo as posições dos tiles no mapa

No protótipo do jogo Bomberman, foi implementado um mapa de tiles. Para

formar um mapa de tiles, geralmente, usa-se um arquivo que mostra as posições

dos tiles no mapa (como na figura 3.1). Cada número representa um tile, que será

18

associado com uma imagem na hora de carregar o mapa. O resultado do

carregamento de um mapa 15x13 pode ser observado na figura 3.2.

Figura 3.2 Mapa de tiles carregado

Por ser mais simples de gerenciar todos os objetos de desenho fazendo com

que eles tenham uma posição no mapa, o mapa de tiles não foi incluído no módulo

de desenho.

3.2 Animações

O processo para fazer uma animação em duas dimensões é simples. Pode-se

pegar um conjunto de imagens que determinam certa movimentação e exibi-las,

sendo que uma nova imagem é exibida de acordo com a taxa de quadros por

segundo.

Figura 3.3 Sprite usada para fazer o Bomberman andar

Por se tratar apenas de exibir imagens com certo “atraso” entre uma e outra

exibição, esse processo pode ser facilmente automatizado para qualquer animação

2D. Só é preciso definir quais são as imagens, a ordem em que serão exibidas e a

taxa de quadros por segundo.

19

No jogo Bomberman, para fazer o personagem se movimentar é usada uma

imagem que possui as imagens da movimentação do mesmo (figura 3.3). Elas são

agrupadas todas num único arquivo e são as imagens dos objetos do jogo,

desenhadas em todos os seus quadros de animação (Fernandes, 2002). Quando

são mostrados sequencialmente, dão uma sensação de movimento. Quanto mais

quadros tiver uma sequência de movimentação, mais perfeito fica o movimento.

Figura 3.4 Principais classes envolvidas em uma animação

O módulo de desenho armazena um conjunto de visões das entidades do

jogo. Cada visão pode ter vários objetos desenháveis associados, mas apenas um

será exibido. Um objeto desenhável representa o desenho uma ação do

personagem, que pode ser andar, correr, saltar, entre outras. Cada objeto

desenhável pode ter um componente associado. Esse componente possui um

20

método responsável por atualizar o objeto. No caso das animações, há uma classe

chamada Animation, que implementa a interface Component.

A classe Animation faz uma animação sempre da esquerda para a direita. No

entanto, caso um usuário do motor queira fazer a animação de outra maneira ou

criar outro tipo componente, basta que implemente a interface Component do jeito

que desejar. O diagrama da figura 3.4 dá uma visão geral do módulo de desenho.

21

4 Módulo de Física

O módulo de física foi feito principalmente para atender ao objetivo de tratar

colisões entre corpos. Além disso, em quase todos os jogos, objetos se movem ou

realizam alguma ação em que uma simulação de física é necessária. Assim, foi

definida uma arquitetura que permite a criação de componentes para simular ações

de física, a qual será detalhada no final do capítulo.

Cada entidade pode possuir um corpo rígido associado, para tratar das

ações de física e da detecção de colisão. Segundo (Gregory, 2009), corpos rígidos

são interessantes de serem usados em jogos porque são perfeitamente sólidos e

não são deformáveis. Isso simplifica a matemática envolvida para simular a dinâmica

dos objetos.

Ações que ocorrem em um jogo podem, normalmente, ser representadas por

colisões entre dois ou mais corpos. A detecção de colisões é um fator crítico nos

jogos, devido a três grandes problemas: o alto custo computacional, pois quando se

precisa testar todos os objetos contra todos a complexidade é O(n²); objetos que

podem mover-se muito rapidamente, como projéteis, por exemplo; e objetos que

possuem geometria complexa, como personagens (Ferreira; Ferreira, 2009). A

seguir, serão detalhadas formas de detecção de colisão e métodos para fazê-la de

forma eficiente.

4.1 Detecção de Colisão a Posteriori

A detecção de colisão a posteriori testa se a colisão já ocorreu. A cada passo

da simulação é testado se um objeto tem intersecção com outro. Se tiver, há uma

colisão. É um teste discreto, pois testa apenas um momento no tempo.

Esse teste é mais comum, porém é menos preciso. Um problema dele pode

se manifestar quando dados dois corpos, pelo menos um corpo tem uma velocidade

muito alta e pelo menos um deles seja um corpo pequeno. Dependendo da

velocidade e do tamanho do corpo, como o teste é discreto, pode acontecer de um

corpo atravessar o outro e o teste vai falhar. A solução para o problema acima é

usar uma restrição. A velocidade máxima de um objeto multiplicada pelo passo de

22

tempo de atualização deve ser menor que o tamanho do menor objeto. Para garantir

isso, deve-se reduzir a velocidade máxima ou o passo do tempo de atualização.

Outro problema dessa técnica é permitir sobreposições dos objetos. Quando

uma colisão é detectada, a posição dos objetos deve ser corrigida. Corrigir a posição

dos objetos a fim de tornar a simulação real pode ser complicado. Um método

simples é apenas retornar os objetos para as posições que tinham no passo de

atualização anterior. Esse método funciona bem se os corpos não tiverem

velocidade alta e se o intervalo de atualização não for muito alto. Assim, apesar de

fisicamente incorreto, o tratamento de colisão é imperceptível ao jogador, pois a

posição anterior dos corpos vai ser muito próxima da posição atual.

4.2 Detecção de Colisão a Priori

Uma técnica de detecção de colisão a priori prevê quando uma colisão vai

ocorrer. Com uma técnica a priori, a simulação é avançada até o tempo em que a

colisão vai ocorrer, impedindo que os objetos se sobreponham. Ela é mais realista e

resolve os problemas das técnicas a posteriori, no entanto tem implementação mais

complexa e, geralmente, é usada em jogos em que não se pode ter uma restrição de

velocidade ou do passo máximo do tempo de atualização.

4.3 Etapas da detecção de colisão

Normalmente, as técnicas de detecção de colisão dividem-se em duas

etapas. Numa etapa inicial, os objetos são testados aos pares com um método

menos preciso, determinando se há uma chance de estarem colidindo. A segunda

etapa pega os pares da etapa inicial e testa, usando um algoritmo preciso, se há

colisão. As duas etapas serão detalhadas a seguir.

4.3.1 Etapa Ampla

Como foi dito anteriormente, nesta etapa é usado um método menos preciso.

Podem ser usados volumes envolventes para otimizar o teste. A figura 4.1 mostra

tipos de volumes envolventes. Quanto mais para a direita, mais precisa é a

aproximação, porém os custos de atualização, de construção e de realização do

23

teste de colisão se tornam maiores. Quanto menor a precisão do teste, maior é a

chance dele retornar falsos positivos, porém nos casos em que os volumes

envolventes dos objetos não colidem nessa etapa não é preciso usar o teste com

precisão máxima. É aí que está o grande benefício dessa etapa, que é não gastar

tempo usando um teste totalmente preciso (que é computacionalmente caro)

verificando se há colisão com objetos que estão muito distantes entre si.

Figura 4.1 Volumes envolventes (Kimmerle, 2005)

4.3.2 Etapa Específica

A etapa específica recebe os pares candidatos a estarem colidindo obtidos

na etapa ampla. Nessa etapa, é usado um algoritmo preciso para verificar se os

objetos estão realmente colidindo. Aqui, dependendo da forma do objeto, um teste

computacionalmente caro pode ser necessário, mas considerando que o número de

pares testados será muito menor do que a complexidade O(n²), é possível usar o

teste sem causar um grande impacto negativo na fluência do jogo.

4.4 Coerência Temporal e Coerência Espacial

Uma técnica de detecção de colisão que leve em consideração a coerência

espacial vai buscar melhorias de desempenho a partir do princípio de que objetos

distantes entre si não colidem, logo o número de pares de objetos colidindo num

certo instante de tempo tende a ser menor do que o máximo de colisões possíveis.

Já a coerência espacial leva em conta que os objetos se movem relativamente

pouco entre os períodos de atualização. Dessa forma, um objeto que está colidindo

tende a estar colidindo na próxima atualização e os que não estavam tendem a não

estar (Rocha, 2010).

24

4.5 Técnicas de Particionamento do Espaço

A fim de tirar proveito da coerência espacial, o espaço pode ser particionado

em várias células. Ele é dividido recursivamente para se criar uma estrutura

hierárquica que minimize a quantidade de testes. Consequentemente, só é

necessário aplicar o teste de colisão numa mesma célula (Ferreira; Ferreira, 2009).

Várias são as formas de particionamento do espaço. Entre elas, podemos destacar

octrees, árvores BSP, kd-tree e árvores esféricas (Gregory, 2009).

Dividir um espaço em quadrantes é uma forma de particionar o espaço. Num

mundo 3D, se usaria uma octree, num mundo 2D uma quadtree. Como o escopo

deste trabalho se limita a ambientes 2D, a técnica de quadtree será abordada a

seguir.

4.5.1 Quadtree

O termo quadtree é comumente usado para se referir a uma classe de

estruturas hierárquicas com uma propriedade em comum, que é a decomposição

recursiva do espaço (Ferreira; Ferreira, 2009). Elas usam o conceito de estrutura de

dados em árvore, sendo que cada nodo pode ter até quatro filhos, dependendo da

implementação. Existem variações de quadtree em que cada nodo tem exatamente

quatro filhos, com exceção das folhas da árvore.

Figura 4.2 Quadtree com profundidade igual a 2

Uma quadtree divide o espaço em quatro partes iguais, recursivamente, até

que se alcance o tamanho desejado. A figura 4.2 mostra uma quadtree com

profundidade igual a dois. Os objetos são adicionados no quadrante em que

25

estiverem contidos. Se um objeto for maior que o menor quadrante da quadtree,

tenta-se colocá-lo num nodo pai que o contenha. A única exceção é o nodo raiz, em

que basta que ele tenha intersecção com objeto, evitando que o mesmo fique fora

do espaço.

Além da coerência espacial, esta estrutura também tira proveito da coerência

temporal, pois como os objetos tendem a estar próximos das suas posições atuais

nas atualizações seguintes, a atualização dos objetos na quadtree é facilitada,

bastando perguntar se o objeto está contido no nodo atual. Apenas se ele não

estiver contido terá de ser removido e colocado em outro nodo, o que é uma

operação custosa, se tiver de ser aplicada em muitos objetos a cada atualização.

4.6 Implementação da Detecção de Colisões no Motor

A gerência da física é feita pelo gerente de física. Ele possui uma instância

do mundo e este último contém um espaço. O espaço está representado na forma

de uma interface que contém métodos para adicionar, remover e atualizar corpos

rígidos e obter os objetos que estão em estado de colisão. Dessa forma, uma

técnica a priori ou a posteriori pode ser anexada ao módulo de física, simplesmente

implementando a interface Space.

Foram implementados dois espaços, ambos usando-se de técnicas a

posteriori: um genérico, que tem de fazer n² testes (considerando n o número de

corpos) para saber se os corpos estão colidindo, com a única melhoria de possuir

uma etapa ampla e uma específica; e outro que representa uma quadtree, conforme

foi descrita na seção 4.5 e considerando cada nodo sempre com quatro filhos, com

exceção das folhas.

4.7 Implementação do Tratamento de Colisões

Quando colisões ocorrem nos jogos, é provável que algo aconteça com as

entidades envolvidas na colisão. Se um personagem colidiu com uma flecha,

possivelmente ele deve perder certa quantidade de vida, dependendo do jogo. Se

um personagem empurrou uma pedra de um penhasco, ela pode receber a força

aplicada pelo personagem e, de acordo com sua massa, ganhar certa aceleração e

26

começar a se mover. Logo, as entidades envolvidas nas colisões devem ser

avisadas para que certas ações sejam tomadas.

Para fazer isso, o mundo recebe todas as colisões calculadas pelo seu

espaço e avisa ao gerente de entidades que uma colisão ocorreu. Foi permitido que

um corpo rígido não pertencesse a uma entidade, sendo usado apenas no módulo

de física. O gerente de entidades verifica se os dois corpos rígidos envolvidos numa

colisão pertencem a uma entidade. Se ambos são parte de entidades, elas são

avisadas que a colisão ocorreu, através do envio de uma mensagem, feito pelo

gerente de mensagens, e tomam as ações que forem necessárias, inclusive se

forem apenas ações físicas, como no caso de empurrar uma pedra, citado

anteriormente. Se apenas um dos corpos pertencer a uma entidade, a entidade é

avisada sobre com qual corpo ela colidiu. Se ambos os corpos não pertencerem a

uma entidade, apenas é tratada a sobreposição dos corpos, no caso de se fazer uso

de uma técnica a posteriori.

4.8 Implementação da Simulação de Física

Apesar do foco do motor de física neste trabalho ser a detecção de colisões,

é comum que jogos possuam elementos que se encaixem numa simulação de física.

Para se adequar a essa exigência dos jogos, foi implementada uma estrutura que

permite que ações físicas, como simulação de movimentos dos corpos, fossem

representadas.

Foi criada uma classe abstrata a qual representa um componente de física

que pode ser especializado em alguma ação de física sobre certo conjunto de

objetos do jogo. Uma ação física que foi predefinida no módulo de física foi um

movimento.

Há uma classe chamada Movable que estende o componente de física e

possui uma instância de uma classe que implemente a interface Motion, que

representa um movimento. No método update da classe Movable, todos os corpos

rígidos que possuam um mesmo tipo de movimento terão suas posições atualizadas,

a cada quadro do jogo. A figura 4.3 mostra o diagrama de classes para o

componente Movable.

27

Figura 4.3 Componente de física

28

5 Módulo de Áudio

O módulo de áudio foi implementado usando a biblioteca SDL_Mixer. Seu

uso é bastante simples, sendo que o usuário precisa conhecer alguns conceitos

sobre áudio, para que possa saber como passar os parâmetros de inicialização. Os

parâmetros são detalhados a seguir:

Figura 5.1 Diagrama de classes do módulo de sons

Frequência: frequência de saída das amostras, em Hertz. Segundo a própria

documentação da biblioteca, o valor de 22050 Hz é adequado para jogos,

embora se possa querer outro valor, como 44100 Hz, que dá a qualidade de

CD ao som, mas que exige muito mais poder de processamento;

29

Formato: define a quantidade de bits de saída das amostras de som. São

permitidos os formatos de 8 bits e 16 bits;

Canais: define a quantidade de canais da saída de som, sendo que 2 canais

fazem o som ser estéreo e 1 faz o som ser mono;

Tamanho do buffer: tamanho de cada amostra mixada, em bytes. Deixando

este valor muito pequeno, o som poderá ter cortes. Se for grande demais, o

som poderá ter atrasos. 1024 bytes costuma ser um bom valor quando muitos

sons precisarem ser mixados;

Canais de mixagem: define a quantidade de canais usados para a mixagem.

Quanto mais canais, mais processamento será preciso para mixar todos os

sons.

Para carregar um som basta instanciar um objeto da classe Sound,

informando o nome do arquivo que contém o som. O gerente de áudio vai carregar o

som, se o mesmo ainda não tiver sido carregado. Isso impede que o mesmo som

seja carregado várias vezes. A classe Sound contém métodos para tocar um som,

pausar um som, continuar um som, parar um som e também definir o volume de um

som. A figura 6.1 mostra o diagrama de classes do módulo de som.

30

6 Módulo de Inteligência Artificial

O projeto da parte de inteligência artificial de um jogo é um ponto delicado.

Um jogo com uma IA que sempre vence vai deixar o jogador frustrado. Porém, se o

jogador sempre vencer facilmente, o jogo perderá a graça logo. Para (Buckland,

2005), a IA de um jogo deve entreter o jogador, oferecendo boas batalhas, fazendo

com que o jogador vença mais vezes do que a máquina e sinta-se poderoso, astuto,

inteligente e esperto.

As ações de cada personagem podem ser bem específicas de acordo com

cada jogo, tornando-se difíceis de serem reusadas. Foram isolados, para compor o

módulo de IA, duas técnicas que podem ser usadas para simular a inteligência de

um personagem. Uma é a de comportamento de agentes baseado em objetivos e

outra é lógica difusa. Ambas as técnicas serão detalhadas a seguir, bem como suas

respectivas implementações no módulo de IA.

6.1 Comportamento de Agentes Baseado em Objetivos

6.1.1 Definições

Não há um consenso na definição do que é um agente. (Wooldridge, 2002)

define um agente como um sistema computacional situado em um ambiente e que é

capaz de realizar ações autônomas nesse ambiente a fim de atingir seus objetivos.

Outra definição, que muitos pesquisadores de agentes consideram aceitável,

conforme (Bradshaw, 1997), diz que um agente é uma entidade de software

funcionando de forma autônoma e contínua em um determinado ambiente, o qual

pode conter outros agentes, e que seja capaz de interferir no seu ambiente de uma

maneira flexível e inteligente, sem precisar de orientação humana constante.

O comportamento dos agentes pode ser definido por uma coleção de

objetivos hierárquicos, que podem ser compostos ou atômicos. Um objetivo atômico

é a representação de uma tarefa, ação, ou comportamento simples, enquanto que

um objetivo composto representa uma tarefa mais complexa e é feito de vários

outros objetivos, que por sua vez podem ser compostos ou atômicos, definindo uma

31

hierarquia aninhada. Além disso, os objetivos devem ter a capacidade de monitorar

seu estado e fazer um replanejamento caso falhem (Buckland, 2005).

Um agente com comportamento dirigido por objetivos tenta imitar o

comportamento humano, examinando estratégias de alto nível (objetivos compostos)

e selecionando a que mais se adequar a certa situação. No caso dos jogos, um

personagem pode possuir uma série de objetivos, sendo que ele vai selecionar o

que for mais desejável para ele, baseado no estado atual do jogo. Normalmente,

“ser mais desejável” implica em vencer o jogo. O objetivo de alto nível escolhido será

decomposto em outros, que serão satisfeitos um de cada vez. O objetivo pode falhar

ou pode ser satisfeito e o estado do jogo pode mudar. Tais acontecimentos podem

fazer o personagem repensar seus objetivos e alterar sua estratégia.

6.1.2 Uso e Implementação

A figura 6.1 mostra o diagrama de classes para a técnica de comportamento

de agentes baseados em objetivos que foi implementada no módulo de IA. Um

objetivo composto pode ser criado especializando a classe CompositeGoal. Criar um

objetivo atômico é feito simplesmente especializando a classe Goal. Cada objetivo

contém um método abstrato para obter o quão desejável é de ele ser ativado num

dado momento do jogo. A escolha de qual objetivo ativar é feita pela classe Brain.

Cada entidade que deseja ter seu comportamento dirigido por objetivos deve criar e

adicionar uma instância da classe Brain ao gerente de IA, que quando atualizado

pedirá a todos os cérebros das entidades para que façam a seleção do melhor

objetivo a ser perseguido e o execute.

A forma do cálculo do quão desejável é um objetivo ser executado é um

ponto chave na definição dos objetivos. É interessante que todos os valores do

desejo de executar um objetivo estejam padronizados num intervalo, por exemplo,

de 0 a 1, assim como os valores necessários para calcular o desejo. Dessa forma a

comparação entre os valores fica facilitada (Buckland, 2005).

Um exemplo será dado para mostrar o funcionamento da escolha dos

objetivos, considerando um jogo de aventura em que guerreiros lutam entre si para

ver quem é o mais forte. O intervalo de possíveis valores para o desejo de se

executar um objetivo será [0,1]. Os guerreiros possuem duas armas: uma espada e

um arco-e-flecha. Eles possuem também uma quantidade de pontos de vida e uma

32

quantidade de flechas. No cenário, podem surgir itens que aumentam a quantidade

de pontos de vida ou de flechas. Alguns objetivos possíveis para eles seriam:

Figura 6.1Diagrama de classes da técnica de comportamento de agentes baseados

em objetivos

1. Aumentar pontos de vida: o personagem procura pelo item mais próximo

que aumente seus pontos de vida, planeja um caminho até ele e o segue;

2. Atacar um inimigo com a espada: o personagem se dirige ao inimigo e o

ataca com a espada;

3. Atacar um inimigo com arco-e-flecha: o personagem se posiciona de

forma a acertar uma flechada no inimigo e atira a flecha;

4. Explorar mapa: o personagem escolhe um ponto do mapa, traça um

caminho até ele o segue;

5. Fugir: o personagem procura uma rota de fuga e a segue.

33

Para calcular o quão desejável é do objetivo 1 ser executado, podemos

pensar que procurar por vida é proporcional a quantidade de pontos de vida que o

personagem possui e é inversamente proporcional à distância do item que lhe dará

mais pontos de vida. A fórmula a seguir mostra a relação do desejo de aumentar os

pontos de vida em função da quantidade de pontos de vida atual e da distância do

personagem ao item:

A fórmula acima funciona porque quanto menos pontos de vida o

personagem possuir, maior será o valor do desejo de ir procurar um item que

aumente sua vida. Para a distância, podemos limitar um valor como sendo o máximo

que um personagem irá percorrer para pegar um item. Se a distância entre um

personagem e um item for maior ou igual à distância máxima, então o valor 1 será

usado na fórmula, o que fará o desejo diminuir. Quanto mais próximo de 0 estiver o

valor da distância, maior será o valor do desejo. Tratar a divisão por 0 dependeria

das regras do jogo. Se basta que um personagem toque em um item para pegá-lo, a

distância calculada nunca seria 0, pois o personagem já teria pegado o item. No

entanto, alguns jogos assumem que um comando deve ser dado ao personagem

para que ele pegue o item, mesmo que ambos ocupem a mesma posição. Nesse

caso, apenas seria retornado o valor 1, que representa o maior grau de desejo.

A divisão pode retornar um valor maior que 1. Para que se possam comparar

os graus de desejo da mesma forma, deve ser feito um procedimento que faça com

que o valor volte ao intervalo [0,1]. Esse procedimento depende dos valores

máximos das variáveis que representam os pontos de vida e a distância até um item,

sendo um cuidado a mais que deve ser tomado ao usar esse método.

O grau de desejo dos outros objetivos pode ser obtido de forma análoga ao

que foi feito para se calcular o desejo de ir procurar um item de vida. Para atacar um

inimigo com a espada, pode-se levar em consideração que o ataque será feito com o

personagem muito próximo do inimigo. Logo, quanto menos pontos de vida, mais

arriscada é essa estratégia. Também se pode levar em conta a distância entre os

dois, pois se ela for muito grande pode não valer a pena ir até o inimigo para atacá-

lo. Um ataque com o arco-e-flecha pode levar em consideração apenas a distância,

pois não é necessário se aproximar demais do inimigo e com isso a chance de sofrer

um ataque e perder pontos de vida é menor.

34

Para o objetivo de explorar o mapa, pode-se atribuir um valor constante e

baixo. Assim o personagem só vai explorar o mapa quando não tiver que se

preocupar com outras ações.

O objetivo de fuga pode ser ativado considerando-se a quantidade de pontos

de vida e a distância do item mais próximo que aumente sua vida. Espera-se que o

objetivo escolhido seja aumentar os pontos de vida, mas pode acontecer de não

haver um item disponível ou de ele estar muito longe, o que leva um personagem

que está prestes a morrer a ter que fugir para que possa ter uma chance de

sobreviver.

6.2 Lógica Difusa

6.2.1 Definições

Normalmente, quando as pessoas se comunicam costumam usar termos

linguísticos com algum grau de imprecisão. Apesar disso, as pessoas conseguem se

entender e realizar suas tarefas sem necessitar de uma precisão perfeita e formal.

Fazer uma máquina entender as variáveis linguísticas humanas seria um jeito mais

natural de se fazer a comunicação entre o homem e a máquina.

A lógica difusa foi inventada por Lofti Zadeh e permite a um computador

reagir a regras e termos linguísticos de um jeito similar ao dos humanos. Definições

como “distante”, “fraco” e “ligeiramente” não são representados por intervalos

discretos, mas por conjuntos difusos, permitindo que valores sejam atribuídos a

conjuntos considerando seu grau de pertinência. Esse processo é chamado de

fuzificação. Usando valores difusos, um computador interpreta regras linguísticas e

produz uma saída que ainda permanece difusa, mas que pode ser defuzificada,

como normalmente é usada nos jogos. Esse processo é conhecido como inferência

difusa e é o mais popular uso da lógica difusa (Buckland, 2005).

6.2.2 Uso e Implementação

É possível usar a lógica difusa combinada com o método de comportamento

de agentes baseados em objetivos. Ela poderia ser usada para implementar o

35

método getDesirability, nas classes filhas de Goal e CompositeGoal. Como

determinar o quão desejável é de um objetivo ser executado simplesmente

baseando-se em atributos do jogo e expressões matemáticas não é uma tarefa fácil,

usar a lógica difusa para fazê-lo pode ser um jeito mais intuitivo e natural,

principalmente para desenvolvedores iniciantes, bastando-se apenas que sejam

definidos os conjuntos difusos e as regras para a inferência difusa.

Figura 6.2 Grau de desejo de se executar um objetivo

Figura 6.3 Quantidade de pontos de vida

Considerando o exemplo da seção 6.1.2 e o objetivo 1 lá definido, será

explicado o que seria necessário definir para calcular o grau de desejo usando lógica

difusa. Primeiro, devem ser definidas as variáveis linguísticas difusas. Para este

exemplo, precisa-se de uma variável que represente o grau de desejo de execução

do objetivo, uma que represente a quantidade de pontos de vida do personagem e

outra que represente à distância do personagem ao item mais próximo que aumente

sua vida.

36

Figura 6.4 Distância a um item

Para a variável “grau de desejo”, pode-se ter em mente que ela assume um

valor no intervalo [0, 100]. Podemos definir três termos: desejável, indesejável, muito

desejável. A figura 6.2 ilustra os conjuntos associados aos termos. Para a variável

“pontos de vida”, vamos assumir que um personagem tem, no máximo, 200 pontos

de vida. Ela pode ser definida com os termos “alto”, “normal” e “baixo”, conforme a

figura 6.3. A variável distância pode ser definida pelos termos “perto”, “média” e

“longe”, como foi feito na figura 6.4.

As regras que definem o grau de desejo de se executar o objetivo de

procurar um item de vida são definidas abaixo:

Se a quantidade de pontos de vida é baixa e a distância até o item de vida

mais próximo é baixa, então o objetivo é muito desejável;

Se a quantidade de pontos de vida é baixa e a distância até o item de vida

mais próximo é média, então o objetivo é muito desejável;

Se a quantidade de pontos de vida é baixa e a distância até o item de vida

mais próximo é longe, então o objetivo é desejável;

Se a quantidade de pontos de vida é normal e a distância até o item de

vida mais próximo é baixa, então o objetivo é desejável;

Se a quantidade de pontos de vida é normal e a distância até o item de

vida mais próximo é média, então o objetivo é indesejável;

Se a quantidade de pontos de vida é normal e a distância até o item de

vida mais próximo é longe, então o objetivo é indesejável;

Se a quantidade de pontos de vida é alta e a distância até o item de vida

mais próximo é baixa, então o objetivo é muito indesejável;

37

Se a quantidade de pontos de vida é alta e a distância até o item de vida

mais próximo é média, então o objetivo é indesejável;

Se a quantidade de pontos de vida é alta e a distância até o item de vida

mais próximo é longe, então o objetivo é indesejável;

Como método de defuzzificação, pode ser usado o método “Média das

Máximas”, pois tem precisão próxima a do método do “Centróide”, que é o mais

preciso, e tem custo computacional baixo quando comparado ao “Centróide”.

As regras acima descrevem o grau de desejo de ir procurar um item de vida

de forma natural, como as pessoas fazem no dia-a-dia, o que torna a lógica difusa

uma ferramenta poderosa. Os conjuntos podem ser modificados a fim de se ajustar

o comportamento que se deseja. Por exemplo, o conjunto que define a quantidade

de pontos de vida como baixa poderia ser “esticado” para fazer com que um

personagem fosse mais vezes procurar um item de vida, adquirindo um

comportamento de se preservar antes de pensar em tomar outras atitudes, como ir

atacar. Outras variáveis também poderiam ser exploradas como a força de ataque

de uma arma, cabendo ao desenvolvedor escolher quais variáveis serão levadas em

consideração.

38

7 Considerações Finais

Neste trabalho foram apresentados conceitos básicos de desenvolvimentos

de jogos. Foram discutidos tópicos sobre gráficos, física, áudio e inteligência

artificial.

Foi mostrado que o sistema de detecção de colisões pode ser otimizado,

tirando proveito de técnicas que aproveitam a coerência espacial e a coerência

temporal e também se usando uma etapa ampla e uma etapa específica. Assim, a

complexidade pode ser reduzida de O(n²) para O(log n).

Desenvolver um motor para jogos se mostrou uma tarefa extensa, pois várias

áreas de conhecimento são envolvidas na construção do mesmo, como computação

gráfica, matemática, física e inteligência artificial. Outro desafio na criação de jogos é

fazer o jogo ser divertido por muito tempo, fazendo com o que o jogador não queira

parar de jogá-lo tão facilmente.

Como trabalhos futuros, podem-se criar algumas extensões do motor

desenvolvido:

Adaptar os módulos de física de desenho para suportar jogos 3D;

Criar um módulo para carregar o jogo totalmente de forma parametrizada,

sem possuir parâmetros embutidos no código;

Criar um editor de mapas para os jogos.

39

8 Referências Bibliográficas

Bradshaw, J. M. An introduction to software agents. 1997.

Buckland, M. Programming Game AI by Example. Wordware Publishing, Inc. 2005.

Fernandes M. Programação de Jogos com Delphi usando DirectX. Relativa, 2002.

Fernandes M. Programação de Jogos com Visual Basic usando DirectX. Relativa,

2001.

Ferreira, Rodrigo R.; Ferreia, Rafael R. Algoritmos para Detecção de Colisões em

Ambientes Gráficos Bidimensionais. 29/01/2009. Disponível em:

<http://www.sharpgames.net/F%C3%B3rum/tabid/57/forumid/Artigos/Artigo/tabid/58/

selectmoduleid/376/ArticleID/1603/reftab/54/Default.aspx>. Acesso em: 30 jun. 2010.

Flynt, J.P; Salem, O. Software Engineering for Game Developers. 2004.

Game Engine. 04/11/2010. Disponível em:

<http://en.wikipedia.org/wiki/Game_engine>. Acesso em: 07 nov. 2010.

Gregory, J. Game engine architecture. 2009.

Kimmerle, S. Tutorial: Real-Time Collision Detection for Dynamic Virtual

Environments. 2005.

Rocha, R. S. Algoritmos Rápidos de Detecção de Colisão. 2010.

Sanches, B. C. Funcionamento de um jogo. Disponível em:

<http://www.pontov.com.br/site/arquitetura/51-programacao/112-funcionamento-de-

um-jogo>. Acesso em 07 abr. 2012.

SDL mixer: Tutorials: Playing a WAV Sound File. Disponível em:

<http://content.gpwiki.org/index.php/SDL_mixer:Tutorials:Playing_a_WAV_Sound_Fi

e>. Acesso em: 01 abr. 2012.

Start out on Game Programming. 28/10/2010. Disponível em:

<http://lazyfoo.net/articles/article01/index.php>. Acesso em: 25 nov. 2010.

West, M. Evolve your Hierarchy. 2007. Disponível em:

<http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy>. Acesso em: 08

abr. 2012.

Wooldridge, M. An Introduction to multiagents systems. 2002.

40

Apêndice A – Código Fonte

AbstractEventHandler.h

#ifndef ABSTRACTEVENTHANDLER_H

#define ABSTRACTEVENTHANDLER_H

#include "Event.h"

class AbstractEventHandler

{

public:

AbstractEventHandler(){}

virtual ~AbstractEventHandler(){}

virtual void processEvent(const Event& event) = 0;

private:

AbstractEventHandler(AbstractEventHandler& other);

AbstractEventHandler& operator=(const AbstractEventHandler& other);

};

#endif // ABSTRACTEVENTHANDLER_H

Animation.h

#ifndef ANIMATION_H

#define ANIMATION_H

#include "Sprite.h"

#include "Component.h"

class Animation : public Component

{

public:

41

Animation(int fps, Sprite& sprite, int numberOfFrames);

virtual ~Animation();

virtual void update(float deltaTime);

private:

const float timeInterval;

float time;

Sprite& sprite;

};

#endif // ANIMATION_H

AudioManager.h

#ifndef AUDIOMANAGER_H

#define AUDIOMANAGER_H

#include <string>

#include <SDL.h>

#include "SDL_mixer.h"

#include "ResourceManager.h"

#include "SoundResource.h"

class AudioManager : public ResourceManager<SoundResource>

{

public:

static void createInstance();

static AudioManager& getInstance();

static void destroyInstance();

void init(int frequency, Uint16 format, int channels, int bufferSize, int

numberOfMixingChannels);

void end();

private:

static AudioManager* instance;

AudioManager();

42

virtual ~AudioManager();

};

#endif // AUDIOMANAGER_H

Brain.h

#ifndef BRAIN_H

#define BRAIN_H

#include "Goal.h"

#include <set>

template <class T>

class Brain

{

public:

Brain() : bestGoal(NULL), status(INACTIVE){}

virtual ~Brain(){}

void arbitrate(){

float bestDesirability = 0;

for(auto* goal : goals){

float d = goal->getDesirability();

if(d > bestDesirability){

bestGoal = goal;

bestDesirability = d;

}

}

assert(bestGoal != NULL);

}

GoalStatus process(){

if(status == INACTIVE){

status = ACTIVE;

arbitrate();

43

}

GoalStatus status = bestGoal->process();

if(status == COMPLETED || status == FAILED)

status = INACTIVE;

return status;

}

inline void addGoal(Goal<T>& goal) { goals.insert(&goal); }

private:

std::set<Goal<T>*> goals;

Goal<T>* bestGoal;

GoalStatus status;

};

#endif // BRAIN_H

Camera.h

#ifndef CAMERA_H

#define CAMERA_H

#include <GL/GL.h>

#include "Vector2D.h"

class Camera

{

public:

Camera();

inline void advance(const Vector2D& advance) { position += advance; }

inline void advanceX(float x) { position.addX(x); }

inline void advanceY(float y) { position.addX(y); }

inline void setPosition(const Vector2D& position) { this->position = position; }

inline void goForward() { glTranslatef(- position.getX(), - position.getY(), 0); }

inline void goBack() { glTranslatef(position.getX(), position.getY(), 0); }

virtual ~Camera();

44

private:

Vector2D position;

};

#endif // CAMERA_H

Clock.h

#ifndef CLOCK_H

#define CLOCK_H

#include <ctime>

class Clock

{

public:

Clock();

virtual ~Clock();

void start();

void pause();

void restart();

clock_t getTime();

inline float getTimeInScale(){ return static_cast<float>(getTime()) / scale; }

clock_t getDeltaTime();

inline float getDeltaTimeInScale(){ return getDeltaTime() / scale; }

inline void setScale(float newScale) { scale = newScale; }

inline float getScale() const { return scale; }

private:

clock_t lastTime, time, absoluteTime;

float scale;

bool paused;

Clock(const Clock& other);

Clock& operator=(const Clock& other);

};

45

#endif // CLOCK_H

CollisionHandler.h

#ifndef COLLISIONHANDLER_H

#define COLLISIONHANDLER_H

#include "RigidBody.h"

class CollisionHandler

{

public:

CollisionHandler(){}

virtual ~CollisionHandler(){}

virtual void onCollision(RigidBody& rigidBody1, RigidBody& rigidBody2) = 0;

};

#endif // COLLISIONHANDLER_H

Color.h

#ifndef COLOR_H

#define COLOR_H

class Color

{

public:

Color();

Color(float red, float green, float blue);

virtual ~Color();

Color(const Color& other);

Color& operator=(const Color& other);

46

inline void setRed(float red) { r = red; }

inline void setGreen(float green) { g = green; }

inline void setBlue(float blue) { b = blue; }

inline float getRed() const { return r; }

inline float getGreen() const { return g; }

inline float getBlue() const { return b; }

private:

float r, g, b;

};

#endif // COLOR_H

Component.h

#ifndef COMPONENT_H

#define COMPONENT_H

class Component

{

public:

Component();

virtual ~Component();

virtual void update(float deltaTime) = 0;

};

#endif // COMPONENT_H

CompositeGoal.h

#ifndef COMPOSITEGOAL_H

#define COMPOSITEGOAL_H

#include <list>

#include "Goal.h"

47

template <class T>

class CompositeGoal : public Goal<T>

{

public:

CompositeGoal(T& t) : Goal<T>(t){}

virtual ~CompositeGoal(){}

virtual void activate() = 0;

virtual GoalStatus process() = 0;

virtual void terminate() = 0;

void addSubGoal(Goal<T>* goal) {

subGoals.push_front(goal);

}

void processSubGoals() {

while(!subGoals.empty() and (subGoals.front()->isCompleted() ||

subGoals.front()->hasFailed())){

subGoals.front()->Terminate();

delete subGoals.front();

subGoals.pop_front();

}

if (!subGoals.empty()){

GoalStatus s = subGoals.front()->process();

if(s == COMPLETED && subGoals.size() > 1)

return ACTIVE;

return s;

} else {

return COMPLETED;

}

}

void removeAllSubGoals(){

for(Goal<T>* goal : subGoals){

goal->terminate();

delete goal;

}

48

subGoals.clear();

}

virtual float getDesirability(T& t) const = 0;

protected:

std::list<Goal<T>*> subGoals;

};

#endif // COMPOSITEGOAL_H

Drawable.h

#ifndef DRAWABLE_H

#define DRAWABLE_H

#include <string>

#include "Vector2D.h"

#include "Graphics.h"

class Drawable

{

public:

Drawable();

virtual ~Drawable();

virtual void draw(Graphics& graphics) const = 0;

inline void setPosition(const Vector2D& position) { this->position = position; }

protected:

Vector2D position;

};

#endif // DRAWABLE_H

DrawManager.h

#ifndef DRAWMANAGER_H

49

#define DRAWMANAGER_H

#include <set>

#include "View.h"

#include "ResourceManager.h"

#include "Texture.h"

#include "Graphics.h"

#include "Camera.h"

#include "Component.h"

class View;

class DrawManager

{

public:

static void createInstance();

static DrawManager& getInstance();

static void destroyInstance();

void addView(View& view);

void removeView(View& view);

void removeAllViews();

void draw();

inline Camera& getCamera() { return camera; }

void end();

void update(float deltaTime);

void removeComponents(const std::set<Component*>& components);

void removeComponent(Component& component);

void insertComponents(const std::set<Component*>& components);

void insertComponent(Component& component);

private:

typedef std::set<View*> ViewSet;

typedef std::set<Component*> UpdateSet;

static DrawManager* instance;

ViewSet viewSet;

50

UpdateSet updateSet;

DrawManager();

DrawManager(const DrawManager& other);

DrawManager& operator=(const DrawManager& other);

virtual ~DrawManager();

Graphics graphics;

Camera camera;

};

#endif // DRAWMANAGER_H

Entity.h

#ifndef ENTITY_H

#define ENTITY_H

#include "View.h"

#include "RigidBody.h"

class Message;

class Entity

{

public:

Entity();

virtual ~Entity();

inline RigidBody& getRigidBody() const { return *rigidBody; }

virtual void handleMessage(const Message& message)= 0;

inline bool hasRigidBody() const { return rigidBody != NULL; }

private:

std::string name;

View* view;

RigidBody* rigidBody;

};

51

#endif // ENTITY_H

EntityManager.h

#ifndef ENTITYMANAGER_H

#define ENTITYMANAGER_H

#include <map>

#include <set>

#include "RigidBody.h"

#include "Entity.h"

class EntityManager

{

public:

static void createInstance();

static EntityManager& getInstance();

static void destroyInstance();

void addEntity(Entity& entity);

void removeEntity(Entity& entity);

void removeAllEntities();

void sendCollision(RigidBody& rigidBody1, RigidBody& rigidBody2);

private:

typedef std::map<RigidBody*, Entity*> MappingEntityRigidBody;

typedef std::set<Entity*> Entities;

MappingEntityRigidBody entityRigidBody;

Entities entities;

static EntityManager* instance;

EntityManager();

virtual ~EntityManager();

EntityManager(const EntityManager& other);

EntityManager& operator=(const EntityManager& other);

};

52

#endif // ENTITYMANAGER_H

Event.h

#ifndef EVENT_H

#define EVENT_H

#include <string>

class Event

{

public:

Event(const std::string& name);

virtual ~Event();

inline bool operator<(const Event& other) const { return name < other.name;}

private:

std::string name;

};

#endif // EVENT_H

EventHandler.h

#ifndef EVENTHANDLER_H

#define EVENTHANDLER_H

#include "AbstractEventHandler.h"

#include "GameEventManager.h"

#include <map>

template <class T>

class EventHandler : public AbstractEventHandler

{

53

private:

typedef void (T::*MemberFunction)(void);

T* t;

std::map<Event, MemberFunction> bindings;

EventHandler(const EventHandler& other);

EventHandler& operator=(const EventHandler& other);

public:

EventHandler(T* _t) : t(_t){}

virtual ~EventHandler();

void bind(const Event& event, const MemberFunction& memberFunction) {

bindings.insert(make_pair(event, memberFunction));

GameEventManager::getInstance().bind(event, this);

}

virtual void processEvent(const Event& event){

auto it = bindings.find(event);

if(it != bindings.end()){

MemberFunction memberFunction = bindings.find(event)->second;

(t->*memberFunction)();

}

}

};

#endif // EVENTHANDLER_H

Exception.h

#ifndef EXCEPTION_H

#define EXCEPTION_H

#include <exception>

#include <string>

namespace util{

54

class Exception

{

public:

Exception();

Exception(const std::string& message);

virtual ~Exception();

inline std::string getMessage() const { return message; }

inline std::string getFile() const { return file; }

inline unsigned int getLine() const { return line; }

private:

std::string message;

std::string file;

unsigned int line;

};

}

#endif // EXCEPTION_H

Frame.h

#ifndef FRAME_H

#define FRAME_H

#include "Vector2D.h"

class Frame

{

public:

Frame(const Vector2D& position, const Vector2D& dimension);

Frame(const Vector2D& dimension);

virtual ~Frame();

Frame(const Frame& other);

Frame& operator=(const Frame& other);

55

void advance();

void back();

inline void setPosition(const Vector2D& position) { this->position = position; }

inline void setDimension(const Vector2D& dimension) { this->dimension =

dimension; }

inline void setWidth(float width) { this->dimension.setX(width); }

inline void setHeight(float height) { this->dimension.setY(height); }

inline Vector2D getPosition() const { return position; }

inline float getWidth() const { return dimension.getX(); }

inline float getHeight() const { return dimension.getY(); }

inline void setMaxX(int max) { maxX = max;}

inline void setMaxY(int max) { maxX = max;}

private:

Vector2D position, dimension;

int maxX, maxY;

};

#endif // FRAME_H

Game.h

#ifndef GAME_H

#define GAME_H

#include "GameWindow.h"

class Game

{

public:

Game();

virtual ~Game();

virtual void init(unsigned int fps) = 0;

virtual void start() = 0;

virtual void end() = 0;

56

virtual void setRunning(bool running) = 0;

virtual void setGameWindow(GameWindow* gameWindow) = 0;

};

#endif // GAME_H

GameEventManager.h

#ifndef GAMEEVENTMANAGER_H

#define GAMEEVENTMANAGER_H

#include "AbstractEventHandler.h"

#include "Game.h"

#include "Event.h"

#include <map>

#include <set>

#include <queue>

#include <vector>

#include <SDL.h>

class GameEventManager

{

public:

static void createInstance();

static GameEventManager& getInstance();

static void destroyInstance();

void bind(Event event, AbstractEventHandler* handler);

void receive(Event event);

void dispatch();

std::vector<SDL_Event> getSDL_Events() const;

void init(Game* game);

void end();

protected:

GameEventManager();

57

virtual ~GameEventManager();

private:

typedef std::set<AbstractEventHandler*> AbstractEventHandlerSet;

typedef std::map<Event, AbstractEventHandlerSet*>

MappingEventForEventHandlerSet;

typedef std::queue<Event> EventQueue;

MappingEventForEventHandlerSet mappingEventForEventHandlerSet;

EventQueue eventQueue;

static GameEventManager* instance;

Game* game;

GameEventManager(const GameEventManager& other);

GameEventManager& operator=(const GameEventManager& other);

};

#endif // GAMEEVENTMANAGER_H

GameWindow.h

#ifndef GAMEWINDOW_H

#define GAMEWINDOW_H

#include <vector>

#include <string>

#include <SDL.h>

#include <guichan/keylistener.hpp>

#include <guichan/mouselistener.hpp>

class GameWindow

{

public:

GameWindow();

virtual ~GameWindow();

virtual void init(int width, int height, int bpp, bool fulScreen, const std::string&

caption) = 0;

58

virtual void receiveEvents(const std::vector<SDL_Event>& events) = 0;

virtual void processLogic() = 0;

virtual void draw() const = 0;

virtual void end() = 0;

virtual void swapBuffers() const = 0;

virtual void addGlobalKeyListener(gcn::KeyListener& keyListener) = 0;

virtual void addMouseListener(gcn::MouseListener& mouseListener) = 0;

};

#endif // GAMEWINDOW_H

GeneralSpace.h

#ifndef GENERALSPACE_H

#define GENERALSPACE_H

#include <set>

#include <map>

#include "Space.h"

class GeneralSpace : public Space

{

public:

GeneralSpace();

virtual ~GeneralSpace();

virtual void add(RigidBody& rigidBody);

virtual void remove(RigidBody& rigidBody);

virtual void update(RigidBody& rigidBody);

virtual std::map<RigidBody*, std::set<RigidBody*> > * getCollisions() const;

private:

std::set<RigidBody*> rigidBodies;

void addCollision(std::map<RigidBody*, std::set<RigidBody*> >& collisions,

RigidBody& rigidBody1, RigidBody& rigidBody2) const;

};

59

#endif // GENERALSPACE_H

Goal.h

#ifndef GOAL_H

#define GOAL_H

#include "GoalStatus.h"

#include "Exception.h"

template <class T>

class Goal

{

public:

Goal(T& t) : entity(t) {}

virtual ~Goal(){}

inline bool isActive() const { return status == ACTIVE; }

inline bool isInactive() const { return status == INACTIVE; }

inline bool isCompleted() const { return status == COMPLETED; }

inline bool hasFailed() const { return status == FAILED; }

virtual void activate() = 0;

virtual GoalStatus process() = 0;

virtual void terminate() = 0;

virtual void addSubGoal(Goal<T>& goal) { throw util::Exception("This goal is not

compososite!"); }

virtual float getDesirability(T& t) const = 0;

protected:

T& entity;

GoalStatus status;

};

#endif // GOAL_H

60

GoalStatus.h

#ifndef GOALSTATUS_H

#define GOALSTATUS_H

enum GoalStatus { ACTIVE, INACTIVE, COMPLETED, FAILED };

#endif // GOALSTATUS_H

Graphics.h

#ifndef GRAPHICS_H

#define GRAPHICS_H

#include "Vector2D.h"

#include "Texture.h"

#include "Color.h"

#include "Rectangle.h"

class Graphics

{

public:

Graphics();

virtual ~Graphics();

void saveContext() const { glPushMatrix(); }

void restoreContext() const { glPopMatrix(); }

void drawImage(const Texture* texture, float x, float y) const ;

void drawImage(const Texture* texture, const Vector2D& position, float srcX,

float srcY, int width, int height) const;

void drawPoint(const Vector2D& point);

void drawLine(const Vector2D& p1, const Vector2D& p2);

void drawRectangle(const math::Rectangle& rectangle);

void setColor(const Color& color);

private:

61

Graphics(const Graphics& other);

Graphics& operator=(const Graphics& other);

};

#endif // GRAPHICS_H

Icon.h

#ifndef ICON_H

#define ICON_H

#include "Texture.h"

#include "Drawable.h"

#include "DrawManager.h"

class Icon : public Drawable

{

public:

Icon(const std::string& fileName);

virtual ~Icon();

virtual void draw(Graphics& graphics) const;

private:

const Texture* image;

};

#endif // ICON_H

MainWindow.h

#ifndef MAINWINDOW_H

#define MAINWINDOW_H

#include <guichan.hpp>

#include <guichan/opengl.hpp>

62

#include <guichan/opengl/openglsdlimageloader.hpp>

#include <guichan/sdl/sdlinput.hpp>

#include <vector>

#include <string>

#include "GameWindow.h"

class MainWindow : public GameWindow

{

public:

MainWindow();

virtual ~MainWindow();

void init(int width, int height, int bpp, bool fulScreen, const std::string& caption);

void receiveEvents(const std::vector<SDL_Event>& events);

void processLogic();

void draw() const;

void end();

void swapBuffers() const;

void addGlobalKeyListener(gcn::KeyListener& keyListener);

void addMouseListener(gcn::MouseListener& mouseListener);

private:

SDL_Surface* screen;

gcn::Gui* gui;

gcn::Container* top;

gcn::OpenGLGraphics* openGLGraphics;

gcn::OpenGLSDLImageLoader* imageLoader;

gcn::SDLInput* input;

};

#endif // MAINWINDOW_H

Message.h

#ifndef MESSAGE_H_INCLUDED

#define MESSAGE_H_INCLUDED

63

#include "MessageType.h"

class Entity;

//Usar ID ?

class Message

{

public:

Message(Entity& receiver, MessageType type);

Message(Entity& receiver, MessageType type, void* extraInfo);

Message(Entity& sender, Entity& receiver, MessageType type);

Message(Entity& sender, Entity& receiver, MessageType type, void*

extraInfo);

virtual ~Message();

inline Entity* getSender() const { return sender; }

inline Entity* getReceiver() const { return receiver; }

inline MessageType getType() const { return type; }

inline void* getExtraInfo() const { return extraInfo; }

Message(const Message& other);

Message& operator=(const Message& other);

private:

Entity* sender;

Entity* receiver;

MessageType type;

void* extraInfo;

};

#endif // MESSAGE_H_INCLUDED

MessageManager.h

#ifndef MESSAGEMANAGER_H

64

#define MESSAGEMANAGER_H

#include "Message.h"

class MessageManager

{

public:

static void createInstance();

static MessageManager& getInstance();

static void destroyInstance();

void sendMessage(const Message& message);

private:

static MessageManager* instance;

MessageManager();

virtual ~MessageManager();

};

#endif // MESSAGEMANAGER_H

MessageType.h

#ifndef MESSAGETYPE_H

#define MESSAGETYPE_H

enum MessageType : unsigned int { COLLISION };

#endif // MESSAGETYPE_H

Motion.h

#ifndef MOTION_H

#define MOTION_H

#include "Vector2D.h"

65

#include "RigidBody.h"

class RigidBody;

class Motion

{

public:

Motion();

virtual ~Motion();

virtual Vector2D getDisplacement(float deltaTime) const = 0;

virtual void setParameters(const RigidBody& rigidBody) = 0;

private:

Motion(const Motion& other);

Motion& operator=(const Motion& other);

};

#endif // MOTION_H

Movable.h

#ifndef MOVABLE_H

#define MOVABLE_H

#include "PhysicsComponent.h"

#include "RigidBody.h"

#include "Motion.h"

class Movable : public PhysicsComponent

{

public:

Movable(Motion& motion);

virtual ~Movable();

virtual void update(float deltaTime);

private:

66

Motion& motion;

};

#endif // MOVABLE_H

MyGame.h

#ifndef MYGAME_H

#define MYGAME_H

#include "Game.h"

#include "Clock.h"

#include "GameEventManager.h"

#include "AudioManager.h"

#include "DrawManager.h"

#include "PhysicsManager.h"

#include "MessageManager.h"

#include "EntityManager.h"

class MyGame : public Game

{

public:

MyGame();

virtual ~MyGame();

void init(unsigned int fps);

void start();

void end();

void setRunning(bool running);

void setGameWindow(GameWindow* gameWindow);

private:

bool isRunning;

unsigned int fps;

GameWindow* window;

Clock gameClock;

67

void update(float time);

};

#endif // MYGAME_H

PhysicsComponent.h

#ifndef PHYSICSCOMPONENT_H

#define PHYSICSCOMPONENT_H

#include <set>

#include "Component.h"

#include "RigidBody.h"

class PhysicsComponent : public Component

{

public:

PhysicsComponent();

virtual ~PhysicsComponent();

virtual void update(float deltaTime) = 0;

inline void addRigidBody(RigidBody& rigidBody) {

rigidBodies.insert(&rigidBody); }

inline void removeRigidBody(RigidBody& rigidBody) {

rigidBodies.erase(&rigidBody); }

protected:

std::set<RigidBody*> rigidBodies;

};

#endif // PHYSICSCOMPONENT_H

PhysicsManager.h

#ifndef PHYSICSMANAGER_H

#define PHYSICSMANAGER_H

68

#include <set>

#include "PhysicsComponent.h"

#include "World.h"

#include "CollisionHandler.h"

class PhysicsManager

{

public:

static void createInstance();

static PhysicsManager& getInstance();

static void destroyInstance();

void init(Space* space, CollisionHandler* handler);

void end();

inline void addRigidBody(RigidBody& rigidBody) { world->add(rigidBody); }

void removeRigidBody(RigidBody& rigidBody);

inline void onCollision(RigidBody& rigidBody1, RigidBody& rigidBody2) {

handler->onCollision(rigidBody1, rigidBody2); }

void update(float deltaTime);

inline void update(RigidBody& rigidBody) { world->update(rigidBody); }

void remove(PhysicsComponent& component);

void insert(PhysicsComponent& component);

private:

PhysicsManager();

virtual ~PhysicsManager();

static PhysicsManager* instance;

World* world;

CollisionHandler* handler;

std::set<PhysicsComponent*> components;

};

#endif // PHYSICSMANAGER_H

QuadTree.h

69

#ifndef QUADTREE_H

#define QUADTREE_H

#include "QuadTreeNode.h"

#include "RigidBody.h"

#include "Rectangle.h"

#include "Space.h"

#include <set>

#include <map>

class QuadTree : public Space

{

public:

QuadTree(const math::Rectangle& rectangle, int depth);

virtual ~QuadTree();

void add(RigidBody& rigidBody);

//bool intersects(RigidBody& rigidBody1, RigidBody& rigidBody2);

std::map<RigidBody*, std::set<RigidBody*> > * getCollisions() const;

void update(RigidBody& rigidBody);

void remove(RigidBody& rigidBody);

private:

typedef std::set<QuadTreeNode*> QuadTreeNodeSet;

typedef std::map<RigidBody*, QuadTreeNode*> RigidBodies;

RigidBodies rigidBodies;

QuadTreeNode* root;

QuadTree(const QuadTree& other);

QuadTree& operator=(const QuadTree& other);

QuadTreeNode* find(RigidBody& rigidBody) const;

};

#endif // QUADTREE_H

QuadTreeNode.h

70

#ifndef QUADTREENODE_H

#define QUADTREENODE_H

#include <iostream>

#include <set>

#include <map>

#include <vector>

#include "Rectangle.h"

#include "Shape.h"

#include "RigidBody.h"

class QuadTreeNode

{

public:

QuadTreeNode(const math::Rectangle& _rectangle);

virtual ~QuadTreeNode();

inline bool intersects(RigidBody& rigidBody) const { return

rectangle.intersects(rigidBody.getBoundBox()); }

bool isLeaf() const;

void subDivide(int depth);

QuadTreeNode* add(RigidBody& rigidBody);

inline void remove(RigidBody& rigidBody) { objects.erase(&rigidBody); }

inline const QuadTreeNode* getChild(int i) const { return nodes[i]; }

void getCollisions(std::map<RigidBody*, std::set<RigidBody*> >&) const;

inline bool contains(const RigidBody& rigidBody) { return

rectangle.contains(rigidBody.getBoundBox()); }

private:

const static unsigned int NW = 0, NE = 1, SW = 2, SE = 3;

math::Rectangle rectangle;

QuadTreeNode* nodes[4];

std::set<RigidBody*> objects;

QuadTreeNode(const QuadTreeNode& other);

QuadTreeNode& operator=(const QuadTreeNode& other);

71

void getCollisions(std::map<RigidBody*, std::set<RigidBody*> >& collisions,

RigidBody& rigidBody) const;

void addCollision(std::map<RigidBody*, std::set<RigidBody*> >& collisions,

RigidBody& rigidBody1, RigidBody& rigidBody2) const;

};

#endif // QUADTREENODE_H

Rectangle.h

#ifndef RECTANGLE_H

#define RECTANGLE_H

#include "Vector2D.h"

#include <iostream>

#include "Shape.h"

//class math::Shape;

namespace math{

class Rectangle : public math::Shape

{

public:

Rectangle(const Vector2D& position, const Vector2D& dimension);

Rectangle(const Vector2D& position, float width, float height);

Rectangle(float x, float y, float width, float height);

virtual ~Rectangle();

Rectangle(const Rectangle& other);

Rectangle& operator=(const Rectangle& other);

inline void setDimension(const Vector2D& dimension) { this->dimension =

dimension; }

inline void setWidth(float width) { this->dimension.setX(width); }

inline void setHeight(float height) { this->dimension.setY(height); }

72

inline float getWidth() const { return dimension.getX(); }

inline float getHeight() const { return dimension.getY(); }

virtual Vector2D getCenter() const;

virtual Rectangle getBoundBox() const;

virtual bool intersects(const Shape& other) const;

virtual bool intersects(const Rectangle& other) const;

bool contains(const Rectangle& other) const;

private:

Vector2D dimension;

};

}

std::ostream& operator<<(std::ostream& output, const math::Rectangle& rectangle);

#endif // RECTANGLE_H

Resource.h

#ifndef RESOURCE_H

#define RESOURCE_H

#include <string>

class Resource

{

public:

inline std::string getName() const { return name; }

protected:

Resource(const std::string& _name);

virtual ~Resource();

private:

Resource(const Resource& other);

Resource& operator=(const Resource& other);

std::string name;

73

};

#endif // RESOURCE_H

ResourceManager.h

#ifndef RESOURCEMANAGER_H

#define RESOURCEMANAGER_H

#include <map>

#include <string>

template<class R>

class ResourceManager

{

public:

static ResourceManager<R>& getInstance() {

static ResourceManager<R> instance;

return instance;

}

R* load(const std::string& fileName){

typename std::map<std::string, R*>::iterator i = resources.find(fileName);

if(i==resources.end()){

R* r = new R(fileName);

resources.insert(make_pair(fileName, r));

return r;

} else {

return i->second;

}

}

protected:

ResourceManager(){};

virtual ~ResourceManager(){

74

for(typename ResourceMap::iterator i = resources.begin(); i !=

resources.end(); i++)

delete i->second;

resources.clear();

};

private:

typedef std::map<std::string, R*> ResourceMap;

ResourceMap resources;

static ResourceManager<R>* instance;

ResourceManager(const ResourceManager& other);

ResourceManager& operator=(const ResourceManager& other);

};

#endif // RESOURCEMANAGER_H

RigidBody.h

#ifndef RIGIDBODY_H

#define RIGIDBODY_H

#include "Vector2D.h"

#include "Shape.h"

#include "Rectangle.h"

class Motion;

class RigidBody

{

public:

RigidBody(math::Shape& shape);

virtual ~RigidBody();

inline void translate(const Vector2D& position) { shape.translate(position); }

inline void setPosition(const Vector2D& position) { shape.setPosition(position); }

inline Vector2D getPosition() const { return shape.getPosition(); }

75

inline void setSpeedVector(const Vector2D& speed) { this->speed = speed; }

inline Vector2D getSpeedVector() const { return speed; }

inline void setAccelerationVector(const Vector2D& acceleration) { this-

>acceleration = acceleration; }

inline Vector2D getAccelerationVector() const { return acceleration; }

inline math::Rectangle getBoundBox() const { return shape.getBoundBox(); }

inline bool collidesWith(const RigidBody& other) { return

shape.intersects(other.shape); }

bool collidesBoundBoxWith(const RigidBody& other) { return

shape.getBoundBox().intersects(other.shape.getBoundBox()); }

inline void saveCurrentPosition() { previousPosition = shape.getPosition(); }

inline void backToPreviousPosition() { shape.setPosition(previousPosition); }

private:

RigidBody(const RigidBody& other);

RigidBody& operator=(const RigidBody& other);

math::Shape& shape;

Vector2D speed, acceleration;

Vector2D previousPosition;

};

#endif // RIGIDBODY_H

Shape.h

#ifndef SHAPE_H

#define SHAPE_H

#include "Vector2D.h"

//class math::Rectangle;

namespace math{

class Rectangle;

76

class Shape

{

public:

Shape(const Vector2D& position);

Shape(float x, float y);

virtual ~Shape();

inline void translate(const Vector2D& position) { this->position += position; }

inline void setPosition(const Vector2D& position) { this->position = position; }

inline Vector2D getPosition() const { return position; }

inline float getX() const { return position.getX(); }

inline float getY() const { return position.getY(); }

virtual Rectangle getBoundBox() const = 0;

virtual Vector2D getCenter() const = 0;

virtual bool intersects(const Shape& other) const = 0;

virtual bool intersects(const Rectangle& other) const = 0;

// bool intersectsBoundBox(const Shape& other) const {

// return getBoundBox().intersects(other.getBoundBox());

// }

protected:

Vector2D position;

};

}

#endif // SHAPE_H

SimpleCollisionHandler.h

#ifndef SIMPLECOLLISIONHANDLER_H

#define SIMPLECOLLISIONHANDLER_H

#include "CollisionHandler.h"

class SimpleCollisionHandler : public CollisionHandler

77

{

public:

SimpleCollisionHandler();

virtual ~SimpleCollisionHandler();

virtual void onCollision(RigidBody& rigidBody1, RigidBody& rigidBody2);

};

#endif // SIMPLECOLLISIONHANDLER_H

Sound.h

#ifndef SOUND_H

#define SOUND_H

#include "SoundResource.h"

class Sound

{

public:

Sound(const std::string& name);

virtual ~Sound();

void setVolume(int volume);

void play();

void play(int channel, int numberOfLoops);

void resume();

void pause();

void stop();

static const int INVALID_CHANNEL = -1;

private:

const SoundResource& soundResource;

int channel;

static void finish(int channel);

};

78

#endif // SOUND_H

SoundResource.h

#ifndef SOUNDRESOURCE_H

#define SOUNDRESOURCE_H

#include "Resource.h"

#include "SDL_Mixer.h"

class SoundResource : public Resource

{

public:

SoundResource(const std::string& name);

virtual ~SoundResource();

inline Mix_Chunk* getChunk() const { return chunk; }

private:

Mix_Chunk* chunk;

Mix_Chunk* loadMixChunk(const std::string& fileName);

};

#endif // SOUNDRESOURCE_H

Space.h

#ifndef SPACE_H

#define SPACE_H

#include <map>

#include <set>

#include "RigidBody.h"

class Space

{

79

public:

Space();

virtual ~Space();

virtual void add(RigidBody& rigidBody) = 0;

virtual void remove(RigidBody& rigidBody) = 0;

virtual void update(RigidBody& rigidBody) = 0;

virtual std::map<RigidBody*, std::set<RigidBody*> > * getCollisions() const = 0;

};

#endif // SPACE_H

Sprite.h

#ifndef SPRITE_H

#define SPRITE_H

#include <string>

#include "Frame.h"

#include "Texture.h"

#include "Drawable.h"

#include "Graphics.h"

class Sprite : public Drawable

{

public:

Sprite(const Frame& frame, const std::string& textureName);

virtual ~Sprite();

Sprite(const Sprite& other);

Sprite& operator=(const Sprite& other);

void draw(Graphics& graphics) const;

inline void advanceFrame() { frame.advance(); }

inline void setFramePosition(const Vector2D& position) {

frame.setPosition(position); }

80

inline int getWidth() const { return texture->getWidth(); }

inline int getHeight() const { return texture->getHeight(); }

inline void setNumberOfFrames(int frames) { frame.setMaxX(frames *

frame.getWidth()); }

private:

Frame frame;

Texture* texture;

};

#endif // SPRITE_H

Texture.h

#ifndef TEXTURE_H

#define TEXTURE_H

#include <string>

#include <GL/gl.h>

#include "Resource.h"

#include <SDL.h>

class Texture : public Resource

{

public:

Texture(const std::string& fileName);

virtual ~Texture();

inline int getWidth() const { return width; };

inline int getHeight() const{ return height; };

inline void bind() const { glBindTexture(GL_TEXTURE_2D, id); }

inline void release() const { glBindTexture(GL_TEXTURE_2D, 0); }

private:

Texture(const Texture&);

GLuint id;

81

int width;

int height;

};

#endif // TEXTURE_H

UniformMotion.h

#ifndef UNIFORMMOTION_H

#define UNIFORMMOTION_H

#include "Motion.h"

class UniformMotion : public Motion

{

public:

UniformMotion();

virtual ~UniformMotion();

virtual Vector2D getDisplacement(float deltaTime) const;

virtual void setParameters(const RigidBody& rigidBody);

private:

Vector2D speed;

};

#endif // UNIFORMMOTION_H

Vector2D.h

#ifndef VECTOR2D_H

#define VECTOR2D_H

#include <iostream>

class Vector2D

82

{

public:

Vector2D();

Vector2D(float x, float y);

virtual ~Vector2D();

Vector2D(const Vector2D& other);

bool operator!=(const Vector2D& other);

bool operator==(const Vector2D& other);

Vector2D& operator=(const Vector2D& other);

Vector2D operator+(const Vector2D& other) const;

Vector2D& operator+=(const Vector2D& other);

Vector2D operator-(const Vector2D& other) const;

Vector2D& operator-=(const Vector2D& other);

Vector2D operator*(float scalar) const;

Vector2D& operator*=(float scalar);

Vector2D operator/(float scalar) const;

Vector2D& operator/=(float scalar);

Vector2D operator-() const;

inline float getX() const { return x; };

inline float getY() const { return y; };

inline void setX(float x) { this->x = x; };

inline void setY(float y) { this->y = y; };

inline void addX(float value) { x+=value; };

inline void addY(float value) { y+=value; };

private:

float x, y;

};

std::ostream& operator<<(std::ostream& output, const Vector2D& rectangle);

#endif // VECTOR2D_H

View.h

83

#ifndef VIEW_H

#define VIEW_H

#include <set>

#include <map>

#include "Drawable.h"

#include "DrawManager.h"

#include "Graphics.h"

#include "Component.h"

class View

{

public:

View();

virtual ~View();

virtual void draw(Graphics& graphics) const;

void setCurrentDrawable(Drawable& drawable);

void addDrawable(Drawable& drawable);

void removeDrawable(Drawable& drawable);

void removeAllDrawables();

void bind(Drawable& drawable, Component& component);

private:

typedef std::set<Drawable*> DrawableSet;

typedef std::map<Drawable*, std::set<Component*> > DrawableMap;

DrawableSet drawableSet;

DrawableMap drawableMap;

Drawable* currentDrawable;

};

#endif // VIEW_H

World.h

#ifndef WORLD_H

84

#define WORLD_H

#include "RigidBody.h"

#include "Space.h"

#include "Entity.h"

class World

{

public:

World(Space* space);

virtual ~World();

inline void add(RigidBody& rigidBody) { space->add(rigidBody); }

inline void remove(RigidBody& rigidBody) { space->remove(rigidBody); }

inline void update(RigidBody& rigidBody) { space->update(rigidBody); }

void checkCollisions() const;

private:

World(const World& other);

World& operator=(const World& other);

Space* space;

};

#endif // WORLD_H

AbstractEventHandler.cpp

#include "AbstractEventHandler.h"

Animation.cpp

#include "Animation.h"

#include "DrawManager.h"

#include <iostream>

using namespace std;

85

Animation::Animation(int fps, Sprite& _sprite, int numberOfFrames) :

timeInterval(1.0f/fps), time(0), sprite(_sprite){

sprite.setNumberOfFrames(numberOfFrames);

}

Animation::~Animation(){}

void Animation::update(float deltaTime){

time += deltaTime;

if(time > timeInterval){

sprite.advanceFrame();

time -= timeInterval;

}

}

AudioManager.cpp

#include "AudioManager.h"

#include "Exception.h"

AudioManager* AudioManager::instance = NULL;

AudioManager::AudioManager(){}

AudioManager::~AudioManager(){}

void AudioManager::createInstance(){

instance = new AudioManager();

}

AudioManager& AudioManager::getInstance(){

return *instance;

}

86

void AudioManager::destroyInstance(){

delete instance;

}

void AudioManager::init(int frequency, Uint16 format, int channels, int bufferSize, int

numberOfMixingChannels){

if(SDL_InitSubSystem(SDL_INIT_AUDIO) == -1){

throw util::Exception("Unable to start SDL Audio subsystem!");

}

if(Mix_OpenAudio(frequency, format, channels, bufferSize) == -1){

throw util::Exception("Unable to start SDL_Mixer subsystem!");

}

Mix_AllocateChannels(numberOfMixingChannels);

}

void AudioManager::end(){

SDL_QuitSubSystem(SDL_INIT_AUDIO);

Mix_CloseAudio();

}

Brain.cpp

#include "Brain.h"

Camera.cpp

#include "Camera.h"

Camera::Camera(){}

Camera::~Camera(){}

Clock.cpp

87

#include "Clock.h"

Clock::Clock() : lastTime(0), time(0), absoluteTime(0), scale(CLOCKS_PER_SEC),

paused(true){}

Clock::~Clock(){}

void Clock::start(){

lastTime = 0;

time = 0;

absoluteTime = clock();

paused = false;

}

clock_t Clock::getTime(){

if(!paused){

lastTime = time;

clock_t t = clock();

time += t - absoluteTime;

absoluteTime = t;

}

return time;

}

clock_t Clock::getDeltaTime(){

if(!paused){

lastTime = time;

clock_t t = clock();

time += t - absoluteTime;

absoluteTime = t;

return time - lastTime;

}

return 0;

88

}

void Clock::pause(){

paused = true;

}

void Clock::restart(){

paused = false;

}

CollisionHandler.cpp

#include "CollisionHandler.h"

Color.cpp

#include "Color.h"

Color::Color() : r(0), g(0), b(0) {}

Color::Color(float red, float green, float blue) : r(red), g(green), b(blue) {}

Color::~Color(){}

Color::Color(const Color& other) : r(other.r), g(other.g), b(other.b){}

Color& Color::operator=(const Color& other)

{

r = other.r;

g = other.g;

b = other.b;

return *this;

}

89

Component.cpp

#include "Component.h"

Component::Component(){}

Component::~Component(){}

CompositeGoal.cpp

#include "CompositeGoal.h"

Drawable.cpp

#include "Drawable.h"

Drawable::Drawable(){}

Drawable::~Drawable(){}

DrawManager.cpp

#include "DrawManager.h"

#include <cassert>

DrawManager* DrawManager::instance = NULL;

void DrawManager::createInstance(){

instance = new DrawManager();

}

DrawManager& DrawManager::getInstance(){

return *instance;

}

90

void DrawManager::destroyInstance(){

delete instance;

}

DrawManager::DrawManager(){}

DrawManager::~DrawManager(){}

void DrawManager::addView(View& view){

ViewSet::iterator i = viewSet.find(&view);

assert(i!=viewSet.end());

viewSet.insert(&view);

}

void DrawManager::removeView(View& view){

ViewSet::iterator i = viewSet.find(&view);

assert(i!=viewSet.end());

view.removeAllDrawables();

delete &view;

viewSet.erase(i);

}

void DrawManager::removeAllViews(){

for(View* view : viewSet){

view->removeAllDrawables();

delete view;

}

viewSet.clear();

}

void DrawManager::draw(){

camera.goForward();

for(View* view : viewSet)

91

view->draw(graphics);

camera.goBack();

}

void DrawManager::end(){

removeAllViews();

}

void DrawManager::update(float deltaTime){

for(Component* component : updateSet)

component->update(deltaTime);

}

void DrawManager::removeComponents(const std::set<Component*>&

components){

for(Component* c: components)

updateSet.erase(c);

}

void DrawManager::removeComponent(Component& component){

updateSet.erase(&component);

}

void DrawManager::insertComponents(const std::set<Component*>& components){

for(Component* c: components)

updateSet.insert(c);

}

void DrawManager::insertComponent(Component& component){

updateSet.insert(&component);

}

Entity.cpp

92

#include "Entity.h"

Entity::Entity(){}

Entity::~Entity(){}

EntityManager.cpp

#include "EntityManager.h"

#include "MessageManager.h"

#include "PhysicsManager.h"

#include "Message.h"

#include <cassert>

using namespace std;

EntityManager::EntityManager(){}

EntityManager::~EntityManager(){}

EntityManager* EntityManager::instance = NULL;

void EntityManager::createInstance() {

instance = new EntityManager();

}

EntityManager& EntityManager::getInstance(){

return *instance;

}

void EntityManager::destroyInstance() {

delete instance;

}

93

void EntityManager::addEntity(Entity& entity){

if(entity.hasRigidBody())

entityRigidBody.insert(make_pair(&entity.getRigidBody(), &entity));

entities.insert(&entity);

}

void EntityManager::removeEntity(Entity& entity){

if(entity.hasRigidBody())

entityRigidBody.erase(&entity.getRigidBody());

entities.erase(&entity);

}

void EntityManager::removeAllEntities(){

for(Entity* e : entities){

if(e->hasRigidBody())

entityRigidBody.erase(&e->getRigidBody());

entities.erase(e);

}

}

void EntityManager::sendCollision(RigidBody& rigidBody1, RigidBody& rigidBody2){

MappingEntityRigidBody::iterator i = entityRigidBody.find(& rigidBody1);

MappingEntityRigidBody::iterator j = entityRigidBody.find(& rigidBody2);

if(i != entityRigidBody.end() and j == entityRigidBody.end())

MessageManager::getInstance().sendMessage(Message(*i->second,

MessageType::COLLISION, &rigidBody2));

else if(i == entityRigidBody.end() and j != entityRigidBody.end())

MessageManager::getInstance().sendMessage(Message(*j->second,

MessageType::COLLISION, &rigidBody1));

else if(i != entityRigidBody.end() and j != entityRigidBody.end())

MessageManager::getInstance().sendMessage(Message(*i->second, *j-

>second, MessageType::COLLISION));

else

PhysicsManager::getInstance().onCollision(rigidBody1, rigidBody2);

94

}

Event.cpp

#include "Event.h"

using namespace std;

Event::Event(const string& _name) : name(_name){}

Event::~Event(){}

EventHandler.cpp

#include "EventHandler.h"

Exception.cpp

#include "Exception.h"

namespace util{

Exception::Exception(const std::string& _message) : message(_message),

file(__FILE__), line(__LINE__) {}

Exception::~Exception(){}

}

Frame.cpp

#include "Frame.h"

95

Frame::Frame(const Vector2D& _position, const Vector2D& _dimension) :

position(_position), dimension(_dimension), maxX(0), maxY(0){}

Frame::Frame(const Vector2D& _dimension) : dimension(_dimension), maxX(0),

maxY(0){}

Frame::~Frame(){}

Frame::Frame(const Frame& other) : position(other.position),

dimension(other.dimension), maxX(0), maxY(0){}

Frame& Frame::operator=(const Frame& other)

{

position = other.position;

dimension = other.dimension;

maxX = other.maxX;

maxY = other.maxY;

return *this;

}

void Frame::advance(){

position.addX(getWidth());

if(position.getX() > maxX - getWidth())

position.setX(0);

}

void Frame::back(){

position.addX(- getWidth());

if(position.getX() < 0)

position.setX(maxX - getWidth());

}

Game.cpp

96

#include "Game.h"

Game::Game(){}

Game::~Game(){}

GameEventManager.cpp

#include "GameEventManager.h"

using namespace std;

GameEventManager* GameEventManager::instance = NULL;

void GameEventManager::createInstance(){

instance = new GameEventManager();

}

GameEventManager& GameEventManager::getInstance(){

return *instance;

}

void GameEventManager::destroyInstance(){

delete instance;

}

GameEventManager::GameEventManager(){}

GameEventManager::~GameEventManager(){}

void GameEventManager::end(){

set<AbstractEventHandlerSet*> handlers;

for(auto i = mappingEventForEventHandlerSet.begin(); i !=

mappingEventForEventHandlerSet.end(); i++)

97

handlers.insert(i->second);

for(auto* i : handlers)

delete i;

}

void GameEventManager::bind(Event event, AbstractEventHandler* handler){

MappingEventForEventHandlerSet::const_iterator i =

mappingEventForEventHandlerSet.find(event);

if(i!=mappingEventForEventHandlerSet.end()){

i->second->insert(handler);

} else {

AbstractEventHandlerSet* set = new AbstractEventHandlerSet();

set->insert(handler);

mappingEventForEventHandlerSet.insert(std::make_pair(event, set));

}

}

void GameEventManager::receive(Event event){

eventQueue.push(event);

}

void GameEventManager::dispatch(){

while(!eventQueue.empty()){

Event& event = eventQueue.front();

MappingEventForEventHandlerSet::const_iterator it =

mappingEventForEventHandlerSet.find(event);

if(it != mappingEventForEventHandlerSet.end()){

AbstractEventHandlerSet* set = it->second;

for(AbstractEventHandlerSet::iterator i = set->begin(); i != set->end(); i++)

(*i)->processEvent(event);

}

eventQueue.pop();

}

}

98

std::vector<SDL_Event> GameEventManager::getSDL_Events() const{

std::vector<SDL_Event> events;

SDL_Event event;

while(SDL_PollEvent(&event))

{

if (event.type == SDL_KEYDOWN and event.key.keysym.sym ==

SDLK_ESCAPE)

game->setRunning(false);

else if(event.type == SDL_QUIT)

game->setRunning(false);

events.push_back(event);

}

return events;

}

void GameEventManager::init(Game* game){

this->game = game;

}

GameWindow.cpp

#include "GameWindow.h"

GameWindow::GameWindow(){}

GameWindow::~GameWindow(){}

GeneralSpace.cpp

#include "GeneralSpace.h"

GeneralSpace::GeneralSpace(){}

99

GeneralSpace::~GeneralSpace(){}

void GeneralSpace::add(RigidBody& rigidBody){

rigidBodies.insert(&rigidBody);

}

void GeneralSpace::remove(RigidBody& rigidBody){

rigidBodies.erase(&rigidBody);

}

void GeneralSpace::update(RigidBody& rigidBody){}

std::map<RigidBody*, std::set<RigidBody*> > * GeneralSpace::getCollisions() const{

std::set<RigidBody*> copyOfRigidBodies = rigidBodies;

std::map<RigidBody*, std::set<RigidBody*> > * collisions = new

std::map<RigidBody*, std::set<RigidBody*> >();

for(auto* rigidBody : rigidBodies){

for(auto* r : copyOfRigidBodies)

if(rigidBody->collidesBoundBoxWith(*r) and rigidBody->collidesWith(*r))

addCollision(*collisions, *rigidBody, *r);

copyOfRigidBodies.erase(rigidBody);

}

return collisions;

}

void GeneralSpace::addCollision(std::map<RigidBody*, std::set<RigidBody*> >&

collisions, RigidBody& rigidBody1, RigidBody& rigidBody2) const{

std::map<RigidBody*, std::set<RigidBody*> >::iterator i =

collisions.find(&rigidBody1);

if(i != collisions.end()){

i->second.insert(&rigidBody2);

} else {

std::set<RigidBody*> c;

100

c.insert(&rigidBody2);

collisions.insert(make_pair(&rigidBody1, c));

}

}

Goal.cpp

#include "Goal.h"

GoalStatus.cpp

#include "GoalStatus.h"

Graphics.cpp

#include "Graphics.h"

#include <GL/glext.h>

#include <GL/gl.h>

Graphics::Graphics(){}

Graphics::~Graphics(){}

void Graphics::drawImage(const Texture* texture, float x, float y) const {

glPushMatrix();

glTranslatef(x, y,0);

texture->bind();

glBegin(GL_QUADS);

glTexCoord2f(1,0);

glVertex2f(texture->getWidth(), texture->getHeight());

glTexCoord2f(0,0);

glVertex2f(0, texture->getHeight());

glTexCoord2f(0,1);

glVertex2f(0, 0);

101

glTexCoord2f(1,1);

glVertex2f(texture->getWidth(), 0);

glEnd();

texture->release();

glPopMatrix();

}

void Graphics::drawImage(const Texture* texture, const Vector2D& position, float

srcX, float srcY, int width, int height) const {

glPushMatrix();

glTranslatef(position.getX(), position.getY(),0);

texture->bind();

glBegin(GL_QUADS);

glTexCoord2f((srcX+width)/texture->getWidth(),srcY/texture->getHeight());

glVertex2f(width, height);

glTexCoord2f(srcX/texture->getWidth(),srcY/texture->getHeight());

glVertex2f(0, height);

glTexCoord2f(srcX/texture->getWidth(), (srcY + height) / texture-

>getHeight());

glVertex2f(0, 0);

glTexCoord2f((srcX+width)/texture->getWidth(), (srcY + height) / texture-

>getHeight());

glVertex2f(width, 0);

glEnd();

texture->release();

glPopMatrix();

}

void Graphics::setColor(const Color& color){

glColor3f(color.getRed(), color.getGreen(), color.getBlue());

}

void Graphics::drawLine(const Vector2D& p1, const Vector2D& p2){

glPushMatrix();

102

glBegin(GL_LINE_STRIP);

glVertex2f(p1.getX(), p1.getY());

glVertex2f(p2.getX(), p2.getY());

glEnd();

glPopMatrix();

}

void Graphics::drawPoint(const Vector2D& point){

glPushMatrix();

glBegin(GL_POINT);

glVertex2f(point.getX(), point.getY());

glEnd();

glPopMatrix();

}

void Graphics::drawRectangle(const math::Rectangle& rectangle){

glPushMatrix();

glTranslatef(rectangle.getPosition().getX(), rectangle.getPosition().getY(), 0);

glBegin(GL_QUADS);

glVertex2f(rectangle.getWidth(), rectangle.getHeight());

glVertex2f(0, rectangle.getHeight());

glVertex2f(0, 0);

glVertex2f(rectangle.getWidth(), 0);

glEnd();

glPopMatrix();

}

Icon.cpp

#include "Icon.h"

Icon::Icon(const std::string& imageName) :

image(ResourceManager<Texture>::getInstance().load(imageName)){}

103

Icon::~Icon(){}

void Icon::draw(Graphics& graphics) const{

graphics.drawImage(image, position.getX(), position.getY());

}

MainWindow.cpp

#include "MainWindow.h"

#include <iostream>

#include <GL/glu.h>

MainWindow::MainWindow() : screen(NULL), gui(NULL), top(NULL),

openGLGraphics(NULL), imageLoader(NULL), input(NULL){}

MainWindow::~MainWindow(){}

void MainWindow::init(int width, int height, int bpp, bool fullScreen, const std::string&

caption){

SDL_Init(SDL_INIT_VIDEO);

SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1 );

SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, bpp);

SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0);

SDL_GL_SetAttribute(SDL_GL_ACCUM_RED_SIZE, 0);

SDL_GL_SetAttribute(SDL_GL_ACCUM_GREEN_SIZE, 0);

SDL_GL_SetAttribute(SDL_GL_ACCUM_BLUE_SIZE, 0);

SDL_GL_SetAttribute(SDL_GL_ACCUM_ALPHA_SIZE, 0);

int flags = SDL_OPENGL | SDL_HWPALETTE;

if (fullScreen)

flags |= SDL_FULLSCREEN;

const SDL_VideoInfo* info = SDL_GetVideoInfo();

if (info->hw_available)

flags |= SDL_HWSURFACE;

else

104

flags |= SDL_SWSURFACE;

if(info->blit_hw)

flags |= SDL_HWACCEL;

screen = SDL_SetVideoMode(width, height, bpp, flags);

SDL_WM_SetCaption(caption.c_str(), NULL);

glViewport(0, 0, width, height);

glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

SDL_EnableUNICODE(1);

SDL_EnableKeyRepeat(0,0);

atexit(SDL_Quit);

imageLoader = new gcn::OpenGLSDLImageLoader();

gcn::Image::setImageLoader(imageLoader);

openGLGraphics = new gcn::OpenGLGraphics();

openGLGraphics->setTargetPlane(width, height);

gui = new gcn::Gui();

gui->setGraphics(openGLGraphics);

top = new gcn::Container();

input = new gcn::SDLInput();

gui->setInput(input);

top->setDimension(gcn::Rectangle(0, 0, width, height));

gui->setTop(top);

glEnable(GL_CULL_FACE);

glMatrixMode(GL_PROJECTION);

glLoadIdentity();

glOrtho(-width/2,width/2,-height/2,height/2,-1,1);

glMatrixMode(GL_MODELVIEW);

glEnable(GL_TEXTURE_2D);

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

glEnable(GL_BLEND);

}

void MainWindow::receiveEvents(const std::vector<SDL_Event>& events){

for(const SDL_Event& event : events)

105

input->pushInput(event);

}

void MainWindow::processLogic(){

gui->logic();

}

void MainWindow::draw() const{

gui->draw();

}

void MainWindow::end() {

delete input;

delete imageLoader;

delete openGLGraphics;

delete top;

delete gui;

delete screen;

}

void MainWindow::swapBuffers() const {

SDL_GL_SwapBuffers();

}

void MainWindow::addGlobalKeyListener(gcn::KeyListener& keyListener){

gui->addGlobalKeyListener(&keyListener);

}

void MainWindow::addMouseListener(gcn::MouseListener& mouseListener){

top->addMouseListener(&mouseListener);

}

Message.cpp

106

#include "Message.h"

#include <iostream>

Message::Message(Entity& _receiver, MessageType _type) :

sender(NULL), receiver(&_receiver), type(_type), extraInfo(NULL){}

Message::Message(Entity& _receiver, MessageType _type, void* _extraInfo) :

sender(NULL), receiver(&_receiver), type(_type), extraInfo(_extraInfo){}

Message::Message(Entity& _sender, Entity& _receiver, MessageType _type) :

sender(&_sender), receiver(&_receiver), type(_type), extraInfo(NULL){}

Message::Message(Entity& _sender, Entity& _receiver, MessageType _type, void*

_extraInfo) :

sender(&_sender), receiver(&_receiver), type(_type), extraInfo(_extraInfo){}

Message::Message(const Message& other) :

sender(other.sender), receiver(other.receiver), type(other.type),

extraInfo(other.extraInfo){}

Message& Message::operator=(const Message& other){

sender = other.sender;

receiver = other.receiver;

type = other.type;

extraInfo = other.extraInfo;

return *this;

}

Message::~Message(){}

MessageManager.cpp

#include "MessageManager.h"

#include "Entity.h"

107

#include <iostream>

MessageManager* MessageManager::instance = NULL;

void MessageManager::createInstance() {

instance = new MessageManager();

}

MessageManager& MessageManager::getInstance(){

return *instance;

}

void MessageManager::destroyInstance() {

delete instance;

}

MessageManager::MessageManager(){}

MessageManager::~MessageManager(){}

void MessageManager::sendMessage(const Message& message){

message.getReceiver()->handleMessage(message);

}

MessageType.cpp

#include "MessageType.h"

Motion.cpp

#include "Motion.h"

Motion::Motion(){}

108

Motion::~Motion(){}

Movable.cpp

#include "Movable.h"

#include "PhysicsManager.h"

Movable::Movable(Motion& _motion) : motion(_motion) {}

Movable::~Movable(){}

void Movable::update(float deltaTime){

for(auto* rigidBody : rigidBodies){

motion.setParameters(*rigidBody);

rigidBody->translate(motion.getDisplacement(deltaTime));

PhysicsManager::getInstance().update(*rigidBody);

}

}

MyGame.cpp

#include "MyGame.h"

#include <iostream>

using namespace std;

MyGame::MyGame() : isRunning(false), fps(60), window(NULL){}

MyGame::~MyGame(){}

void MyGame::init(unsigned int fps){

this->fps = fps;

}

109

void MyGame::start(){

isRunning = true;

gameClock.start();

const float updateRate = static_cast<float>(fps) / 1000.0f;

float deltaTime = 0;

while(isRunning){

deltaTime += gameClock.getDeltaTimeInScale();

if(deltaTime < updateRate){

SDL_Delay(1);

continue;

}

window->receiveEvents(GameEventManager::getInstance().getSDL_Events());

update(updateRate);

deltaTime -= updateRate;

}

}

void MyGame::end(){

GameEventManager::getInstance().end();

PhysicsManager::getInstance().end();

DrawManager::getInstance().end();

AudioManager::getInstance().end();

GameEventManager::destroyInstance();

PhysicsManager::destroyInstance();

DrawManager::destroyInstance();

AudioManager::destroyInstance();

MessageManager::destroyInstance();

EntityManager::destroyInstance();

window->end();

}

void MyGame::update(float time){

window->processLogic();

GameEventManager::getInstance().dispatch();

110

PhysicsManager::getInstance().update(time);

DrawManager::getInstance().update(time);

window->draw();

DrawManager::getInstance().draw();

window->swapBuffers();

}

void MyGame::setRunning(bool running) {

isRunning = running;

}

void MyGame::setGameWindow(GameWindow* gameWindow) {

window = gameWindow;

}

PhysicsComponent.cpp

#include "PhysicsComponent.h"

PhysicsComponent::PhysicsComponent(){}

PhysicsComponent::~PhysicsComponent(){}

PhysicsManager.cpp

#include "PhysicsManager.h"

PhysicsManager* PhysicsManager::instance = NULL;

void PhysicsManager::createInstance() {

instance = new PhysicsManager();

}

PhysicsManager& PhysicsManager::getInstance(){

111

return *instance;

}

void PhysicsManager::destroyInstance() {

delete instance;

}

PhysicsManager::PhysicsManager(){}

PhysicsManager::~PhysicsManager(){}

void PhysicsManager::init(Space* space, CollisionHandler* handler){

world = new World(space);

this->handler = handler;

}

void PhysicsManager::end(){

delete world;

delete handler;

}

void PhysicsManager::removeRigidBody(RigidBody& rigidBody) {

world->remove(rigidBody);

for(PhysicsComponent* component : components)

component->removeRigidBody(rigidBody);

}

void PhysicsManager::update(float deltaTime){

for(PhysicsComponent* component : components)

component->update(deltaTime);

}

void PhysicsManager::remove(PhysicsComponent& component){

components.erase(&component);

112

}

void PhysicsManager::insert(PhysicsComponent& component){

components.insert(&component);

}

QuadTree.cpp

#include "QuadTree.h"

#include "assert.h"

using namespace std;

QuadTree::QuadTree(const math::Rectangle& rectangle, int depth) : root(new

QuadTreeNode(rectangle)){

root->subDivide(depth);

}

QuadTree::~QuadTree(){

delete root;

}

void QuadTree::add(RigidBody& rigidBody){

if(root->intersects(rigidBody)){

QuadTreeNode* node = root->add(rigidBody);

rigidBodies.insert(make_pair(&rigidBody, node));

}

}

std::map<RigidBody*, set<RigidBody*> >* QuadTree::getCollisions() const {

std::map<RigidBody*, set<RigidBody*> > *collisions = new std::map<RigidBody*,

set<RigidBody*> >();

root->getCollisions(*collisions);

113

return collisions;

}

QuadTreeNode* QuadTree::find(RigidBody& rigidBody) const {

RigidBodies::const_iterator i = rigidBodies.find(&rigidBody);

return i != rigidBodies.end() ? i->second : NULL;

}

void QuadTree::update(RigidBody& rigidBody){

QuadTreeNode* node = find(rigidBody);

assert(node != NULL);

if(!node->contains(rigidBody)){

node->remove(rigidBody);

add(rigidBody);

}

}

void QuadTree::remove(RigidBody& rigidBody){

rigidBodies.erase(&rigidBody);

}

QuadTreeNode.cpp

#include "QuadTreeNode.h"

using namespace std;

QuadTreeNode::QuadTreeNode(const math::Rectangle& _rectangle) :

rectangle(_rectangle), nodes({NULL, NULL, NULL, NULL}){}

QuadTreeNode::~QuadTreeNode()

{

for(int i = 0; i < 4; i++)

if(nodes[i] != NULL)

114

delete nodes[i];

}

bool QuadTreeNode::isLeaf() const {

for(int i = 0; i < 4; i++)

if(nodes[i] != NULL)

return false;

return true;

}

void QuadTreeNode::subDivide(int depth){

float x = rectangle.getX();

float y = rectangle.getY();

float newWidth = rectangle.getWidth()/2;

float newHeight = rectangle.getHeight()/2;

nodes[NW] = new QuadTreeNode(math::Rectangle(x, y, newWidth, newHeight));

nodes[NE] = new QuadTreeNode(math::Rectangle(x + newWidth, y, newWidth,

newHeight));

nodes[SW] = new QuadTreeNode(math::Rectangle(x, y - newHeight, newWidth,

newHeight));

nodes[SE] = new QuadTreeNode(math::Rectangle(x - newWidth, y - newHeight,

newWidth, newHeight));

if(depth > 0){

for(int i = 0; i < 4; i++)

nodes[i]->subDivide(depth - 1);

}

}

QuadTreeNode* QuadTreeNode::add(RigidBody& rigidBody){

if(!isLeaf()){

for(int i = 0; i < 4; i++)

if(nodes[i]->contains(rigidBody))

return nodes[i]->add(rigidBody);

}

115

objects.insert(&rigidBody);

return this;

}

void QuadTreeNode::getCollisions(std::map<RigidBody*, set<RigidBody*> >&

collisions) const {

std::set<RigidBody*> copyOfObjectSet = objects;

for(RigidBody* r : objects){

for(RigidBody* r2 : copyOfObjectSet)

if(r->collidesBoundBoxWith(*r2) and r->collidesWith(*r2))

addCollision(collisions, *r, *r2);

if(!isLeaf())

for(int i = 0; i < 4; i++)

if(nodes[i]->intersects(*r))

nodes[i]->getCollisions(collisions, *r);

copyOfObjectSet.erase(r);

}

}

void QuadTreeNode::getCollisions(std::map<RigidBody*, set<RigidBody*> >&

collisions, RigidBody& rigidBody) const {

for(RigidBody* r : objects){

if(rigidBody.collidesBoundBoxWith(*r) and rigidBody.collidesWith(*r))

addCollision(collisions, rigidBody, *r);

if(!isLeaf())

for(int i = 0; i < 4; i++)

if(nodes[i]->intersects(rigidBody))

nodes[i]->getCollisions(collisions, rigidBody);

}

}

void QuadTreeNode::addCollision(std::map<RigidBody*, std::set<RigidBody*> >&

collisions, RigidBody& rigidBody1, RigidBody& rigidBody2) const{

116

std::map<RigidBody*, std::set<RigidBody*> >::iterator i =

collisions.find(&rigidBody1);

if(i != collisions.end()){

i->second.insert(&rigidBody2);

} else {

std::set<RigidBody*> c;

c.insert(&rigidBody2);

collisions.insert(make_pair(&rigidBody1, c));

}

}

Rectangle.cpp

#include "Rectangle.h"

using namespace std;

std::ostream& operator<<(std::ostream& output, const math::Rectangle& rectangle){

output << "(x = " << rectangle.getPosition().getX() << ", y = " <<

rectangle.getPosition().getY() << ", width = " <<

rectangle.getWidth() << ", height = " << rectangle.getWidth() << ")";

return output;

}

namespace math{

Rectangle::Rectangle(const Vector2D& _position, const Vector2D& _dimension) :

Shape(_position), dimension(_dimension){}

Rectangle::Rectangle(const Vector2D& _position, float width, float height) :

Shape(_position), dimension(Vector2D(width, height)){}

Rectangle::Rectangle(float x, float y, float width, float height) : Shape(x,y),

dimension(Vector2D(width, height)){}

117

Rectangle::~Rectangle(){}

Rectangle::Rectangle(const Rectangle& other) : Shape(other.position),

dimension(other.dimension){}

Rectangle& Rectangle::operator=(const Rectangle& other)

{

position = other.position;

dimension = other.dimension;

return *this;

}

bool Rectangle::intersects(const Shape& other) const{

return other.intersects(*this);

}

bool Rectangle::intersects(const Rectangle& other) const{

float right1, right2, left1, left2, top1, top2, bottom1, bottom2;

right1 = position.getX() + getWidth();

right2 = other.position.getX() + other.getWidth();

left1 = position.getX();

left2 = other.position.getX();

top1 = position.getY();

top2 = other.position.getY();

bottom1 = position.getY() - getHeight();

bottom2 = other.position.getY() - other.getHeight();

if(bottom1 > top2 || top1 < bottom2 || right1 < left2 || left1 > right2)

return false;

return true;

}

Vector2D Rectangle::getCenter() const{

return Vector2D((getX() + getX() + getWidth())/2, (getY() + getY() - getHeight())/2);

118

}

Rectangle Rectangle::getBoundBox() const {

return *this;

}

bool Rectangle::contains(const Rectangle& other) const{

float right1, right2, left1, left2, top1, top2, bottom1, bottom2;

right1 = position.getX() + getWidth();

right2 = other.position.getX() + other.getWidth();

left1 = position.getX();

left2 = other.position.getX();

top1 = position.getY();

top2 = other.position.getY();

bottom1 = position.getY() - getHeight();

bottom2 = other.position.getY() - other.getHeight();

return left1 < left2 and right1 > right2 and top1 > top2 and bottom1 < bottom2;

}

}

Resource.cpp

#include "Resource.h"

Resource::Resource(const std::string& _name) : name(_name){}

Resource::~Resource(){}

ResourceManager.cpp

#include "ResourceManager.h"

RigidBody.cpp

119

#include "RigidBody.h"

RigidBody::RigidBody(math::Shape& _shape) : shape(_shape), speed(0,0),

acceleration(0,0){}

RigidBody::~RigidBody(){}

Shape.cpp

#include "Shape.h"

namespace math{

Shape::Shape(const Vector2D& _position) : position(_position){}

Shape::Shape(float x, float y) : position(x,y){}

Shape::~Shape(){}

}

SimpleCollisionHandler.cpp

#include "SimpleCollisionHandler.h"

SimpleCollisionHandler::SimpleCollisionHandler(){}

SimpleCollisionHandler::~SimpleCollisionHandler(){}

void SimpleCollisionHandler::onCollision(RigidBody& rigidBody1, RigidBody&

rigidBody2){

120

rigidBody1.backToPreviousPosition();

rigidBody2.backToPreviousPosition();

}

Sound.cpp

#include "Sound.h"

#include "Exception.h"

#include <iostream>

#include "AudioManager.h"

using namespace std;

Sound::Sound(const std::string& name) :

soundResource(*AudioManager::getInstance().load(name)),

channel(INVALID_CHANNEL){}

Sound::~Sound(){}

void Sound::setVolume(int volume){

Mix_VolumeChunk(soundResource.getChunk(), volume);

}

void Sound::play(int channel, int numberOfLoops){

if(channel != INVALID_CHANNEL){

if(Mix_Paused(channel))

Mix_Resume(channel);

else if(Mix_Playing(channel))

throw util::Exception("The sound: " + soundResource.getName() + " is

already playing!");

} else {

this->channel = Mix_PlayChannel(channel, soundResource.getChunk(),

numberOfLoops);

Mix_ChannelFinished(finish);

121

}

}

void Sound::play(){

if(channel != INVALID_CHANNEL){

if(Mix_Paused(channel))

Mix_Resume(channel);

else if(Mix_Playing(channel))

throw util::Exception("The sound: " + soundResource.getName() + " is

already playing!");

} else {

channel = Mix_PlayChannel(-1, soundResource.getChunk(), 0);

Mix_ChannelFinished(finish);

}

}

void Sound::resume(){

if(channel != INVALID_CHANNEL){

if(Mix_Paused(channel)){

Mix_Resume(channel);

} else {

throw util::Exception("The sound: " + soundResource.getName() + " is not

paused!");

}

} else {

throw util::Exception("The sound: " + soundResource.getName() + " is not

started!");

}

}

void Sound::pause(){

if(channel != INVALID_CHANNEL){

if(Mix_Playing(channel)){

Mix_Pause(channel);

122

} else {

throw util::Exception("The sound: " + soundResource.getName() + " is not

playing!");

}

} else {

throw util::Exception("The sound: " + soundResource.getName() + " is not

started!");

}

}

void Sound::stop(){

if(channel != INVALID_CHANNEL){

if(Mix_Paused(channel)){

Mix_Playing(channel);

Mix_HaltChannel(channel);

channel = INVALID_CHANNEL;

} else {

Mix_HaltChannel(channel);

channel = INVALID_CHANNEL;

}

} else {

throw util::Exception("The sound: " + soundResource.getName() + " is not

started!");

}

}

void Sound::finish(int channel){

channel = INVALID_CHANNEL;

}

SoundResource.cpp

#include "SoundResource.h"

#include "Exception.h"

123

SoundResource::SoundResource(const std::string& fileName) : Resource(fileName){

chunk = loadMixChunk(fileName);

}

SoundResource::~SoundResource()

{

Mix_FreeChunk(chunk);

}

Mix_Chunk* SoundResource::loadMixChunk(const std::string& fileName){

Mix_Chunk* chunk = Mix_LoadWAV(fileName.c_str());

if(!chunk){

throw util::Exception("Unable to load " + fileName + "!");

}

return chunk;

}

Space.cpp

#include "Space.h"

Space::Space(){}

Space::~Space(){}

Sprite.cpp

#include "Sprite.h"

#include "DrawManager.h"

Sprite::Sprite(const Frame& _frame, const std::string& textureName) : frame(_frame),

texture(ResourceManager<Texture>::getInstance().load(textureName)){}

124

Sprite::Sprite(const Sprite& other) : frame(other.frame), texture(other.texture){}

Sprite::~Sprite(){}

Sprite& Sprite::operator=(const Sprite& other)

{

frame = other.frame;

texture = other.texture;

return *this;

}

void Sprite::draw(Graphics& graphics) const{

graphics.drawImage(texture, position, frame.getPosition().getX(),

frame.getPosition().getY(), frame.getWidth(), frame.getHeight());

}

Texture.cpp

#include <stdexcept>

#include <iostream>

#include <SDL.h>

#include <SDL_image.h>

#include "Texture.h"

#include "Exception.h"

using namespace std;

Texture::Texture(const string& fileName) : Resource(fileName){

SDL_Surface* image = IMG_Load(fileName.c_str());

if (image == NULL)

throw util::Exception("Unable to load texture " + fileName);

width = image->w;

height = image->h;

glGenTextures(1, &id);

125

glBindTexture(GL_TEXTURE_2D, id);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,

GL_NEAREST);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,

GL_NEAREST);

GLint format = image->format->BytesPerPixel == 3 ? GL_RGB : GL_RGBA;

glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format,

GL_UNSIGNED_BYTE, image->pixels);

SDL_FreeSurface(image);

if(GL_NO_ERROR != glGetError())

throw util::Exception("Error at texture creation. Texture name: " + fileName);

}

Texture::~Texture(){}

UniformMotion.cpp

#include "UniformMotion.h"

UniformMotion::UniformMotion(){}

UniformMotion::~UniformMotion(){}

Vector2D UniformMotion::getDisplacement(float deltaTime) const{

return speed * deltaTime;

}

void UniformMotion::setParameters(const RigidBody& rigidBody){

speed = rigidBody.getSpeedVector();

}

Vector2D.cpp

126

#include "Vector2D.h"

Vector2D::Vector2D() : x(0), y(0){}

Vector2D::Vector2D(float _x, float _y) : x(_x), y(_y) {}

Vector2D::~Vector2D(){}

Vector2D::Vector2D(const Vector2D& other)

{

x = other.x;

y = other.y;

}

Vector2D& Vector2D::operator=(const Vector2D& other)

{

x = other.x;

y = other.y;

return *this;

}

bool Vector2D::operator!=(const Vector2D& other){

if(x!=other.x)

return true;

if(y!=other.y)

return true;

return false;

}

bool Vector2D::operator==(const Vector2D& other){

if(x!=other.x)

return false;

if(y!=other.y)

127

return false;

return true;

}

Vector2D & Vector2D::operator/=(float scalar)

{

x/=scalar;

y/=scalar;

return *this;

}

Vector2D Vector2D::operator/(float scalar) const

{

return Vector2D(x/scalar, y/scalar);

}

Vector2D & Vector2D::operator*=(float scalar)

{

x*=scalar;

y*=scalar;

return *this;

}

Vector2D Vector2D::operator*(float scalar) const

{

return Vector2D(x*scalar, y*scalar);

}

Vector2D & Vector2D::operator-=(const Vector2D& other)

{

x-=other.x;

y-=other.y;

return *this;

}

128

Vector2D Vector2D::operator-(const Vector2D& other) const

{

return Vector2D(x - other.x, y - other.y);

}

Vector2D & Vector2D::operator+=(const Vector2D& other)

{

x+=other.x;

y+=other.y;

return *this;

}

Vector2D Vector2D::operator+(const Vector2D& other) const

{

return Vector2D(x + other.x, y + other.y);

}

Vector2D Vector2D::operator-() const

{

return Vector2D(-x, -y);

}

std::ostream& operator<<(std::ostream& output, const Vector2D& vector2D){

output << "(" << vector2D.getX() << ", " << vector2D.getY() << ")";

return output;

}

View.cpp

#include <algorithm>

#include "View.h"

#include "assert.h"

129

using namespace std;

View::View() : currentDrawable(NULL){}

View::~View(){}

void View::draw(Graphics& graphics) const{

currentDrawable->draw(graphics);

}

void View::setCurrentDrawable(Drawable& drawable){

DrawableSet::iterator k = drawableSet.find(&drawable);

assert(k==drawableSet.end());

DrawableMap::iterator i = drawableMap.find(&drawable);

if(i != drawableMap.end())

DrawManager::getInstance().insertComponents(i->second);

if(currentDrawable != NULL){

DrawableMap::iterator j = drawableMap.find(currentDrawable);

if(j != drawableMap.end())

DrawManager::getInstance().insertComponents(j->second);

}

currentDrawable = &drawable;

}

void View::addDrawable(Drawable& drawable){

drawableSet.insert(&drawable);

}

void View::removeDrawable(Drawable& drawable){

drawableSet.erase(&drawable);

drawableMap.erase(&drawable);

}

void View::removeAllDrawables(){

130

for(auto i = drawableMap.begin(); i != drawableMap.end(); i++)

for(auto* j : i->second)

DrawManager::getInstance().removeComponent(*j);

for(auto* d : drawableSet)

delete d;

drawableSet.clear();

drawableMap.clear();

}

void View::bind(Drawable& drawable, Component& component){

auto i = drawableMap.find(&drawable);

if(i != drawableMap.end()){

auto& s = i->second;

s.insert(&component);

} else {

set<Component*> s;

s.insert(&component);

drawableMap.insert(make_pair(&drawable, s));

}

}

World.cpp

#include "World.h"

#include "EntityManager.h"

World::World(Space* _space) : space(_space){}

World::~World(){

delete space;

}

void World::checkCollisions() const{

typedef std::map<RigidBody*, std::set<RigidBody*> > Collisions;

131

Collisions * collisions = space->getCollisions();

for(Collisions::const_iterator i = collisions->begin(); i != collisions->begin(); i++){

const std::set<RigidBody*> & rigidBodies = i->second;

for(RigidBody* rigidBody : rigidBodies)

EntityManager::getInstance().sendCollision(*(i->first), *rigidBody);

}

delete collisions;

}