Como fazer um jogo sem OOP? [fechadas]

10

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 Missileobjeto e adicionado a uma lista de Missileobjetos. Cada tanque no jogo é um Tankobjeto. Etc.

Todo o design do programa é baseado nisso. Por exemplo, ter uma lista de Missileobjetos 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?

user3150201
fonte
6
Esta é uma pergunta filosófica? Mesmo se você não chamar seus tanques de "objetos", provavelmente desejará "entidades", "atores", "agentes", "estruturas" ou apenas algum outro nome para a mesma idéia, que é uma coleção de atributos e comportamentos que compõem uma coisa rotóide cubóide com uma torre que pode atirar em coisas, chamada tanque. As linguagens de programação terão maneiras diferentes de formalizar essa mesma idéia, mas no final, será um tanque.
Anko
Muitos jogos usam um sistema baseado em componentes como esta resposta descreve: gamedev.stackexchange.com/a/31491/9366
John McDonald
Isso é extremamente amplo (que você pode corrigir restringindo o escopo) e não é realmente específico para o desenvolvimento de jogos (já que criar software sem técnicas OO não é algo que um desenvolvedor de jogos lhe daria uma resposta melhor do que qualquer outro desenvolvedor de software), o que torna o tópico aqui, receio.
Pode ser adequado para o StackOverflow, ou você pode consultar a central de ajuda para encontrar uma seleção de sites específicos para o desenvolvimento de jogos (como o GDNet) que permitiriam esse tipo de tópico amplo e orientado para a discussão. Boa sorte!

Respostas:

16

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.

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.

Eric Lippert
fonte
12

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 thisum argumento.

assim

 missile.setVelocity(100);

torna-se

 setMissileVelocity(missile, 100);

ou quando essa função é trivial, você apenas faz

 missile.velocity = 100;

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

struct Missile {
     int x;
     int y;
     int velocity;
}

Missile missiles[256];

torna-se

int missileX[256];
int missileY[256];
int missileVelocities[256];

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:

function updateMissilePosition(int index) {
     missileX[index] += missileVelocity[index];
}
Philipp
fonte
1
Mas 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)?
user3150201
1
@ user3150201 Você não está totalmente correto. A maioria das linguagens não OOP (e eu diria que qualquer outra que seja adequada para desenvolvimento sério de jogos) suporta estruturas. Uma estrutura é como uma classe, apenas que não contém nada, exceto variáveis ​​públicas. Por isso lhe permitiria criar um tipo Missile, que é uma estrutura com vários campos, como x, y, anglee velocity.
Philipp
@ user3150201 Resposta atualizada com uma seção sobre como fazer isso sem estruturas.
Philipp
Boa resposta Philipp, embora eu não entenda por que alguém não gostaria de programar Orientado a Objetos. É realmente difícil ler idiomas não OOP e pode ser frustrante. O código pode se tornar uma bagunça em pouco tempo.
Zhafur
2
@ Zhafur Você está ciente de que sua afirmação é uma enorme chama de flamebe, não é?
Philipp
6

Eu faço da seguinte maneira:

  • Todas as classes / métodos OOP têm acesso this. Para utilizar thisem uma abordagem que não seja de OO, basta passar o que for necessário (veja o próximo ponto) thiscomo o primeiro parâmetro.
  • Agora, como nas instâncias, você pode passar structs para suas funções como this, 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 ou structs pequeno . Portanto, esse índice é usado para cada membro de dados individual da classe original. Então, por exemplo, se você tivesse

...

class Entity //let's say you had 100 instances of this
{
   int a;
   char b;
   function foo() 
   {
      .../*can access 'this' herein*/
   }
}

Você substituiria por

int a[100];
char b[100];
function foo(int index);

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 structs, 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 .

Engenheiro
fonte
0

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 switchinstruçã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 thisparâmetros de tipos diferentes. Para esse problema, novamente existem duas alternativas:

  • Interruptores aninhados para cada combinação de tanque e míssil.
  • Matrizes de expedição bidimensionais, que contêm indicadores para funções de cada combinação de tanque e míssil.
Calmarius
fonte