Eu sempre me perguntei até que ponto a orientação a objetos é usada pelos videogames, especialmente os grandes projetos em 3D. Eu acho que seria legal que tanto troll
e werewolf
herdou seus atributos de enemy
e substituiu alguns deles. Mas talvez as aulas sejam muito pesadas para serem práticas, eu não sei ...
18
Respostas:
Para dar o seu exemplo, explicarei meus pensamentos sobre Entites nos videogames. Não discutirei as vantagens e desvantagens gerais de objetos e classes, nem todo o seu papel nos videogames.
As entidades em pequenos videogames são definidas principalmente por classes separadas; em grandes jogos, geralmente são definidas em arquivos. Existem duas abordagens gerais a seguir:
Extensão : no seu exemplo
Troll
eWerewolf
será estendidoEnemy
, o que se estendeEntity
.Entity
cuida das coisas básicas como movimento, saúde etc., enquantoEnemy
declararia as subclasses como inimigos e faria outras coisas (o Minecraft segue essa abordagem).Baseado em componentes : a segunda abordagem (e a minha favorita) é uma
Entity
classe, que possui componentes. Um componente pode serMoveable
ouEnemy
. Estes componentes cuidar de uma coisa como movimento ou física. Esse método quebra longas cadeias de extensão e minimiza o risco de ocorrer um conflito. Por exemplo: Como você pode criar inimigos não móveis, se eles herdarem de um personagem móvel? .Em grandes videogames como World of Warcraft ou Starcraft 2, as Entidades não são classes, mas conjuntos de dados, que especificam toda a Entidade. O script também é importante, porque você define o que a Entidade não define em uma Classe, mas em um Script (se for exclusivo, não como mover).
Atualmente, estou programando um RTS e adoto a abordagem baseada em componentes com arquivos de descritor e script para Entites, Skills, Items e tudo mais dinâmico no jogo.
fonte
component
é nesse contexto. Um link seria muito apreciado.Infelizmente, essa abordagem do polimorfismo, popular nos anos 90, mostrou-se uma má ideia na prática. Imagine que você adicione 'wolf' à lista de inimigos - bem, ele compartilha alguns atributos com o lobisomem, então você deseja consolidá-los em uma classe base compartilhada, por exemplo. 'WolfLike'. Agora você adiciona 'Humano' à lista de inimigos, mas os lobisomens às vezes são humanos, então eles também compartilham atributos, como andar com duas pernas, poder conversar, etc. Você cria uma base comum 'Humanóide' para humanos e lobisomens , e você precisa parar de lobisomens derivados de WolfLike? Ou você multiplica a herança de ambos - nesse caso, qual atributo tem precedência quando algo no Humanoid colide com algo no WolfLike?
O problema é que tentar e modelar o mundo real como uma árvore de classes é ineficaz. Faça três coisas e provavelmente você pode encontrar quatro maneiras diferentes de fatorar o comportamento deles em compartilhado e não compartilhado. É por isso que muitos optaram por um modelo modular ou baseado em componente, onde um objeto é composto por vários objetos menores que compõem o todo, e os objetos menores podem ser trocados ou alterados para criar o comportamento desejado. Aqui você pode ter apenas 1 classe Inimigo e contém subobjetos ou componentes de como anda (por exemplo: Bipedal vs. Quadrupedal), seus métodos de ataque (por exemplo: mordida, garra, arma, punho), sua pele (verde para trolls, peludo para lobisomens e lobos), etc.
Isso ainda é completamente orientado a objetos, mas a noção do que é um objeto útil é diferente do que costumava ser ensinado nos livros didáticos. Em vez de ter uma grande árvore de herança de várias classes abstratas e várias classes concretas nas pontas da árvore, você normalmente tem apenas 1 classe concreta representando um conceito abstrato (por exemplo, 'inimigo'), mas que contém mais classes concretas representando conceitos mais abstratos (por exemplo, ataques, tipo de pele). Às vezes, essas segundas classes podem ser melhor implementadas por meio de um nível de herança (por exemplo, uma classe de ataque básica e várias classes derivadas para ataques específicos), mas a árvore de herança profunda desapareceu.
Na maioria dos idiomas modernos, as aulas não são 'pesadas'. Eles são apenas outra maneira de escrever código e geralmente envolvem a abordagem processual que você teria escrito de qualquer maneira, exceto com uma sintaxe mais fácil. Mas, por todos os meios, não escreva uma classe onde uma função funcionará. As classes não são intrinsecamente melhores, apenas onde elas tornam o código mais fácil de gerenciar ou entender.
fonte
Não sei ao certo o que você quer dizer com "muito pesado", mas C ++ / OOP é a língua franca do desenvolvimento de jogos pelas mesmas boas razões que são usadas em outros lugares.
Falar em soprar caches é um problema de design, não um recurso de idioma. O C ++ não pode ser tão ruim, pois é usado em jogos nos últimos 15 a 20 anos. O Java não pode ser tão ruim, pois é usado nos ambientes de tempo de execução muito apertado dos smartphones.
Agora, a verdadeira questão: por muitos anos, as hierarquias de classe se tornaram profundas e começaram a sofrer problemas relacionados a isso. Em tempos mais recentes, as hierarquias de classes se achataram e a composição e outros projetos orientados a dados estão sendo mais do que a herança orientada a lógica.
É tudo OOP e ainda é usado como linha de base ao projetar um novo software, apenas um foco diferente no que as classes incorporam.
fonte
As classes não envolvem nenhuma sobrecarga de tempo de execução. O polimorfismo em tempo de execução é apenas chamar através de um ponteiro de função. Essas despesas gerais são extremamente mínimas em comparação com outros custos, como chamadas Draw, sincronização de simultaneidade, E / S de disco, comutadores de modo kernel, localidade insuficiente de memória, uso excessivo de alocação dinâmica de memória, má escolha de algoritmos, uso de linguagens de script e a lista continua. Há uma razão pela qual a orientação a objetos substituiu essencialmente o que veio antes e é porque é muito melhor.
fonte
Uma coisa a ter em mente é que os conceitos de código orientado a objetos costumam ser mais valiosos do que sua implementação em linguagens. Em particular, ter que fazer milhares de chamadas de atualização virtual para entidades em um loop principal pode causar uma bagunça no cache em C ++ em plataformas como 360 ou PS3 - para que a implementação evite palavras-chave "virtuais" ou mesmo "de classe". de velocidade.
Muitas vezes, embora ainda siga os conceitos OO de herança e encapsulamento - apenas implementando-os de maneira diferente da maneira como a linguagem se faz (por exemplo, modelos curiosamente recursivos, composição em vez de herança (o método baseado em componentes descrito por Marco)). Se a herança sempre pode ser resolvida no tempo de compilação, os problemas de desempenho desaparecem, mas o código pode se tornar um pouco complicado com modelos, implementações personalizadas de RTTI (novamente as incorporadas geralmente são impraticamente lentas) ou lógica de tempo de compilação. macros etc.
No Objective-C para iOS / Mac, existem problemas semelhantes, mas piores, com o esquema de mensagens (mesmo com o armazenamento em cache sofisticado) ...
fonte
O método que tentarei usar mistura herança de classe e componentes. (Primeiro, eu não faço do Enemy uma classe - faço disso um componente.) Não gosto da idéia de usar uma classe de entidade genérica para tudo e, em seguida, adicionar os componentes necessários para aprimorar a entidade. Em vez disso, prefiro que uma classe adicione automaticamente componentes (e valores padrão) ao construtor. Em seguida, as subclasses adicionam seus componentes adicionais em seu construtor, para que a subclasse tenha seus componentes e seus componentes de classe pai. Por exemplo, uma classe de armas adicionaria componentes básicos comuns a todas as armas e, em seguida, a subclasse Espada adicionaria componentes adicionais específicos para espadas. Ainda estou debatendo sobre o uso de ativos de texto / xml para definir valores para entidades (ou espadas específicas, por exemplo), ou fazer tudo isso em código, ou qual é a combinação certa. Isso ainda permite que o jogo use as informações de tipo e o polimorfismo da linguagem de código, e torna o ObjectFactories muito mais simples.
fonte