Então me surgiu a idéia de fazer um compilado de coisas que aprendi durante esses 2 anos e que não teve nenhuma matéria formalmente ensinando. Coisas do dia-a-dia da programação de jogos, saca?Para começar esse compilado vou tentar explicar todos os conceitos que acabei utilizando no meu último projeto um tower defense. Este tower defense foi feito em Python 2.7 + PyGame e pode ter o código encontrado aqui. O problema que como você pode ver esse código é bem extenso, com muitos módulos e scripts de personalização. Então farei como o Jack e irei por partes. Neste post iremos abordar apenas o conceito de Entidade e o Gerente de Entidade.
Ferramentas necessárias:
- Tabelas Hash e Listas
- Se você não conhece a estrutura das tabelas hash minha dica é ir atrás de um bom livro de algoritmos. Está começando agora e não tem a mínima ideia do que são estruturas de dados? Vá atrás do Cormen. É uma facada no peito se você for comprar, mas se fosse pra escolher ter o conhecimento de apenas 1 livro de computação eu escolheria esse. Outra sugestão é ir atrás dele em bibliotecas, não considero uma biblioteca de computação boa se ele não estiver na lista.
- GameLoop
- Se você nunca fez nenhum jogo, talvez essa publicação seja muito superficial para você. Novamente tenho um livro para sugerir. Infelizmente esse não é em português, mas é de graça. O Game Programming Patterns vai te salvar o couro.
- Vetores
- Lembra das aulas de matemática? Não? Hora de lembrar delas, porque do inicio ao fim dessas séries vou me referenciar a eles. Vocês podem encontrar um bom artigo sobre o uso de vetores em jogos aqui (E não é porque ele foi meu professor não! É boa mesmo essa referencia. Tem inclusive implementação em C++). Se você for utilizar a PyGame, eu utilizei essa classe aqui e não tive nenhum problema durante esse projeto.
Não esse tipo de entidade! |
Então vamos falar de entidades. Entidade em jogos podem ser qualquer coisa que tenha uma posição. Vejamos então como defini o código dessas tais entidades. Esse código pode ser encontrado dentro do projeto no arquivo /miniEngine/Entity.py.
class Entity: def __init__(self): self.surface = None self.position = Vec2d(0,0) self.tag = "" self.rBoundingCircle = 0 self.centerBoundingCircle = Vec2d(0,0) self.animations = {} self.layer = None def update(self): pass def setPosition(self,x,y = None): if(type(x) == tuple): self.position = Vec2d(x[0],x[1]) elif(y == None): self.position = Vec2d(x.x , x.y) else: self.position = Vec2d(x,y) def setRadiusBoundingCircle(self, r): self.rBoundingCircle = r def setCenterBoundingCircle(self, x, y = None): if(type(x) == tuple): self.centerBoundingCircle = Vec2d(x[0],x[1]) elif(y == None): self.centerBoundingCircle = Vec2d(x.y , x.y) else: self.centerBoundingCircle = Vec2d(x,y)
Essas entidades tem uma superfície que será utilizada para desenha-la na tela, um vetor posição, uma etiqueta, um raio para o circulo de colisão, o centro do "bounding circle" que também é um vetor, as animações e a camada. Além destes atributos temos um método update, um método setPosition pra alterar a posição da entidade e um conjunto de sets relacionados ao "bouding circle". Neste momento só estamos interessados na posição e no método update. Dependendo da biblioteca que você está utilizando, você vai acabar criando somente esses atributos mesmo. As vezes você irá apenas encapsular um objeto que a própria biblioteca fornece e criar o método update. Este método update será chamado a cada fase de atualização dentro do GameLoop. O método update está vazio e esse é objetivo! Se você não quiser fazer nenhuma alteração nessa entidade não tem porque fazer uma atualização. Onde raios você vai usar isso? Eu utilizei para fazer os tiles dos mapas, o background da tela inicial, para fazer grande parte dos elementos utilizados na interface do usuário. Enfim fazer tudo que é estático. E se você quiser fazer algo se movimentar você faz esse objeto herdar dessa classe entidade e reescreve a função update com o que você quiser fazer.
Entendido sobre as entidades? (Não resisti)
Vamos falar sobre o gerente de entidades então. Essa classe é a responsável pela organização dessas entidades, utilizando etiquetas ("tags"). Você pode ver o código dentro do arquivo: /miniEngine/ESTManager.py
class EntityManager: def __init__(self, ): self.entitys = {} self.layers = {} self.layerOrder = [] def addEntity(self, entity, tag , layer = None): if(not self.entitys.has_key(tag)): self.entitys[tag] = [entity] if(layer != None): entity.layer = layer #Add tag to a layer if(not self.layers.has_key(layer)): self.layers[layer] = [tag] else: self.layers[layer] += [tag] else: self.entitys[tag] += [entity] def removeEntity(self, entity, tag): self.entitys[tag].remove(entity) def update(self): lEntitys = self.entitys.values() for l in lEntitys: for e in l: e.update() def getTagEntitys(self, tag): if(self.entitys.has_key(tag)): return self.entitys[tag] return [] def collision(self, tag1, tag2): if( not self.entitys.has_key(tag1) or not self.entitys.has_key(tag2)): return [] lEntitys1 = self.entitys[tag1] lEntitys2 = self.entitys[tag2] lCollisions = [] for e in lEntitys1: for e2 in lEntitys2: if(isOnCollision(e, e2)): lCollisions += [(e,e2)] return lCollisions def defineLayerOrder(self, layerOrder): self.layerOrder = layerOrder for layer in layerOrder: if not self.layers.has_key(layer): self.layers[layer] = []
Essa classe é composta de 2 tabelas hash e uma lista. Como estou implementando em Python, estou utilizando a estrutura de dicionários no lugar da hash. Se você for fazer na unha uma implementação da tabela hash, você pode utilizar constantes para a escolha das tags. O dicionário chamado de entities é responsável por guardar as entidades dentro de uma lista que utiliza a mesma etiqueta. O dicionário de layers é utilizado apenas na hora de desenhar essas entidades, junto com a lista de layers, então não tratarei deles agora. Veja que esta classe tem um método update. Este método update é responsável por atualizar todas as entidades que estão sendo gerenciadas chamando o método update de cada uma das entidades.
Outro método relevante é o collision, neste método são utilizados como parâmetros duas tagsque definem quais são as listas de entidades que serão testadas. Admito que utilizei um pensamento meio funcional na hora da idealização desta função, pois a lista retornada é construída de sub-listas. Nesta sub-lista tem os pares de objetos em colisão.
Nenhum comentário:
Postar um comentário