Atualmente, estou estudando desenvolvimento de jogos e praticando fazer jogos.
Eu uso muita OOP nos meus jogos. Por exemplo, cada míssil disparado é uma instância de um Missile
objeto e adicionado a uma lista de Missile
objetos. Cada tanque no jogo é um Tank
objeto. Etc.
Todo o design do programa é baseado nisso. Por exemplo, ter uma lista de Missile
objetos permite que todos os quadros movam os mísseis, desenhe-os Tank
, etc.
É difícil para mim imaginar como um jogo (que é mais complexo que o Pac-Man) poderia ser programado em uma linguagem não OO. (Sem desrespeito aos programadores não OO, é claro). Não apenas em termos de quanto tempo levará, mas principalmente em termos de como um jogo pode ser projetado dessa maneira.
Não consigo imaginar projetar um jogo sem usar a programação orientada a objetos, porque todo o meu entendimento de como projetar um programa de jogo é baseado em OOP.
Gostaria de perguntar: Hoje, existem jogos que não são programados usando OOP, de maneira semelhante ao que descrevi acima? Existem jogos 'profissionais' que não usam OOP como um fator importante no processo de desenvolvimento?
Se sim, você poderia me dar uma idéia de como, por exemplo, a detecção de colisão entre um tanque e um número N de mísseis poderia ser implementada, sem OOP?
fonte
Respostas:
Provavelmente será bom tentar escrever alguns programas no estilo não OO. Mesmo se você descobrir que isso não é pragmático para você, provavelmente aprenderá muito ao longo do caminho que o ajudará no futuro.
O estilo OO é bastante adequado para jogos, porque quase sempre se trata de manipulação de objetos com estado. Um raio laser atinge o robô e o estado do robô muda enquanto sua identidade permanece a mesma.
No entanto, é possível programar jogos em um estilo funcional . Em um estilo funcional, o estado não muda por si só. Objetos são imutáveis. Em vez de alterar objetos, você faz a pergunta: como o universo seria diferente se eu mudasse isso? e, em seguida, produza um universo totalmente novo que possui a propriedade alterada. Obviamente, você pode reutilizar grande parte do universo existente anteriormente porque é imutável .
Na programação funcional, cada função deve calcular seu valor de retorno apenas a partir das informações passadas; não há leitura do "estado global".
Se você fizer isso, o problema fundamental que você terá que passar por sua cabeça é que toda atualização é não destrutiva . Quando o laser atinge o robô, você não altera o estado do robô. Por fim, você calcula um universo inteiramente novo idêntico ao antigo, exceto que o robô tem um estado diferente; se você precisar desse universo antigo, ele ainda está lá, inalterado.
Esta série de artigos de blog reflete mais sobre como escrever jogos em um estilo funcional:
http://prog21.dadgum.com/23.html
Este artigo aborda especificamente sua pergunta "shell atinge um tanque":
http://prog21.dadgum.com/189.html
Na verdade, basta ler o blog inteiro. Há coisas boas lá e os artigos são curtos.
fonte
Qualquer programa orientado a objetos pode ser refatorado para um programa procedural, substituindo todas as classes por estruturas e convertendo todas as funções-membro em função autônoma que aceita o objeto que seria
this
um argumento.assim
torna-se
ou quando essa função é trivial, você apenas faz
A principal diferença entre programação orientada a objetos e programação procedural é como você trata seus dados. No POO, os dados são inteligentes . Ele gerencia e se manipula. Mas na programação procedural, os dados são estúpidos . Ele não faz nada por si só e precisa ser manipulado de fora.
Quando você considera as estruturas muito orientadas a objetos, pode substituir uma matriz de estruturas por várias matrizes, uma para tudo o que seria uma variável de um míssil. assim
torna-se
Nesse projeto, uma função que realiza uma operação envolvendo vários atributos no mesmo míssil agora usaria um índice de matriz em vez de uma referência a uma estrutura. Sua implementação ficaria assim:
fonte
missile
é uma instância de um objeto. Em não-POO, não há instâncias, estou correto? Em caso afirmativo, como você poderia definir setMissileVelocity (míssil, 100)?Missile
, que é uma estrutura com vários campos, comox
,y
,angle
evelocity
.Eu faço da seguinte maneira:
this
. Para utilizarthis
em uma abordagem que não seja de OO, basta passar o que for necessário (veja o próximo ponto)this
como o primeiro parâmetro.struct
s para suas funções comothis
, mas acho que a melhor maneira de obter um bom desempenho de cache para objetos prolíficos, como entidades ou partículas, é simplesmente passar um único índice para várias matrizes de primitivas oustruct
s pequeno . Portanto, esse índice é usado para cada membro de dados individual da classe original. Então, por exemplo, se você tivesse...
Você substituiria por
Agora você está passando um índice para a função para obter o que normalmente seria
this
.Lembre-se de que você pode usar matrizes de primitivas como acima, ou matrizes de
struct
s, dependendo da melhor forma de intercalar seus dados para obter uma boa localização do cache (localidade de referência). Obviamente, o desempenho decente do cache depende muito mais do que isso - particularmente em qual idioma / plataforma você está escrevendo seu código - mas mesmo em linguagens dinamicamente baseadas em VM, como Java, grandes matrizes lineares de primitivas tendem a exibir melhores características de desempenho do que instâncias de objetos. A principal razão para isso é que os objetos são acessados por referência e isso significa que você está pulando toda a memória para acessar dados - ineficiente em comparação com o acesso primitivo de forma contígua a partir de uma grande matriz.Para obter mais informações sobre a construção de entidades etc. como uma matriz de estruturas ou primitivas, consulte Evolução da sua hierarquia , de Mick West .
fonte
Além das respostas existentes, você pode querer saber como fazer polimorfismo em uma linguagem processual.
Existem duas abordagens:
Armazenando o tipo
Nesse caso, a estrutura possui um campo para o identificador de tipo, provavelmente um enum, que é verificado usando uma
switch
instrução quando uma ação específica do tipo precisa ser executada.A outra maneira é:
Armazenando ponteiros de função
Você não mencionou em qual linguagem de programação possui experiência, mas em várias linguagens elas também são chamadas de retornos de chamada, delegados, eventos ou funções de alta ordem ou simplesmente objetos de função.
Nesse caso, você não armazenará um tipo, mas armazenará um ponteiro / referência a uma função que executa a ação específica. Quando uma ação específica de tipo precisa ser executada, você simplesmente chama essa função. Este se parece muito com os métodos virtuais comuns.
Ao permitir definir cada função independentemente, você obtém o padrão de estratégia livre.
Em relação ao seu último parágrafo com a detecção de colisão. Eu acho que você provavelmente tem vários tipos de tanques e vários tipos de mísseis, e cada combinação pode ter um resultado diferente quando colidem. Se é isso que você procura, você tem um problema que ainda não foi resolvido pelas linguagens OOP: despacho múltiplo e métodos múltiplos que podem ter vários
this
parâmetros de tipos diferentes. Para esse problema, novamente existem duas alternativas:fonte