Atualizações de objetos, internas e externas?

8

O título é um pouco confuso, mas não pensei em como explicar minha pergunta em uma frase curta. Então aqui está:

Sempre que estou escrevendo motores de jogos, seja com base em física / base etc, sempre chego ao ponto em que não tenho certeza de como devo gerenciar as coisas. As entidades no mundo devem lidar por conta própria ou deve haver algum sistema global para gerenciá-las?

Aqui está um exemplo fácil: Movendo coisas. Cada objeto deve ver o mundo ao seu redor (verifique se há colisões) e se mover com base nisso.

[note, este é um jogo baseado em blocos em que objetos se movem por bloco, então não estou usando a física para passar de bloco a bloco]

public class Actor : GameObject
    {
        private void MoveTo(Vector2 location)
        {
            if (world.getTile(location) != solid && world.objAtTile(location) == null)
            {
                Tweener.addTween(this, location);
            }
          }
        }

Ou o movimento de cada objeto deve ser manipulado no mundo, onde o mundo verifica tudo?

public class Actor : GameObject
    {
        private void MoveTo(Vector2 location)
        {
            world.moveTo(location);
          }
        }

public class World
{

    public void moveObject(GameObject obj, Vector2 location)
    {
      //called from object

      if (getTile(location) != solid && objAtTile(location) == null)
         {
                Tweener.addTween(obj, location);
         }
    }
}

Realmente não importa muito para este exemplo, suponho, mas posso me ver tendo problemas mais tarde. Desde já, obrigado.

omgnoseat
fonte
11
Mais para a pergunta, deve Actorsaber sobre worldtudo?
Deceleratedcaviar
11
Eu acho que você está confundindo Objetos com Componentes e isso está confundindo a questão de quem está fazendo o que. Pode ser um problema semântico, pois Component é um termo muito sobrecarregado. Eu acho que se você esclarecer o que é um Objeto, o que você quer dizer com Componente e como você organiza os pequenos mecanismos que realmente executam o trabalho, poderá responder sua própria pergunta.
Patrick Hughes
11
Eu tenho que concordar com os outros que você está perguntando se o objeto do jogo se move ou o mundo move o objeto do jogo. O código acima não me leva a acreditar que você realmente quer dizer um sistema de tipo de componente e, portanto, pode ser enganoso ter no título da sua pergunta.
James
Eu usei alguns termos enganosos agora que vocês mencionaram. Vai ajustá-lo agora! Edit: Veja, ele já foi alterado, obrigado!
omgnoseat
@daniel Os atores claramente precisam ter conhecimento sobre o mundo ao seu redor para se moverem de maneira eficaz, embora eu normalmente projetei em torno de um contêiner que encapsule os dois, mas que ninguém saiba. por exemplo. Mundo contém Ator e Nível; O ator sabe sobre Level (ou melhor, é informado sobre Level quando necessário, mas não uma referência permanente), mas não sobre World.
Jhocking

Respostas:

2

Vejo que seus exemplos estão em Java, mas suas tags não especificam nenhum idioma. No C ++, a resposta é nenhuma - você não deve estar usando uma função de membro para isso!

void move_to(actor *a, world *w, vec2 location);

fonte
4
Tenho que dizer grande fã dos livros que o autor do artigo escreveu. No entanto, o raciocínio do artigo é, tanto quanto eu poderia dizer, boa teoria, mas não código prático. Ao mover métodos como esses para fora dos objetos a que pertencem, você está adicionando mais locais para procurar funcionalidades relacionadas a eles, o que apenas causa confusão e níveis mais altos de manutenção necessária. Apenas a minha opinião sobre isso, então não + ou -. Apenas dois centavos.
James
11
Além disso, devo dizer que se parece com C # com a herança usando: ClassName em vez de estender ClassName.
James
É o código pseudo baseado fora C #, mas isso não deve ser relevante para a minha pergunta :)
omgnoseat
11
@omgnoseat: é relevante porque o conselho é impossível em um idioma sem funções simples, e sua utilidade é reduzida em qualquer idioma sem ADL. Uma boa arquitetura não é independente da escolha do idioma.
2
@ James: Não há nada não-OO em funções simples, se as próprias funções invocam métodos que seguem os princípios de design do OO. Esse é o objetivo do artigo. (OO também não sinônimo de bom design Se um design clara ou simples requer que você não usar OO, vala OO, não o design..)
1

Não existe uma resposta simples.

Como na maioria das coisas na programação, é uma troca. Dar mais poder a objetos individuais os torna maiores - e, portanto, mais lentos - mas facilita a compreensão e a extensão do mecanismo. Ter uma megaclasse capaz de lidar com tudo juntos pode ser mais rápido, mas ao custo de ter uma megaclasse; isto é, geralmente é considerado uma má forma criar classes supermassivas.

Li alguns artigos sobre design orientado a componentes e dados, em que você tem uma única classe que representa todos os objetos de um determinado tipo, armazena os dados em listas e transmite apenas índices para obter propriedades de um objeto individual. Embora eu possa ver isso como um tipo viável de arquitetura, acho que isso se complica com o ponto inteiro da orientação a objetos, que também recebeu sua parte justa de críticas.

Eu, pessoalmente, recomendo dar mais poder aos objetos. Faz mais sentido em uma linguagem orientada a objetos e (imagino) mais fácil de entender e manter ao longo do tempo.

Quasiperfect
fonte
Você subestima um pouco as coisas dizendo que o estilo de componente e design orientado a dados que você descreveu "parafusa com todo o sentido do OO" - não é absolutamente OO . Pelo menos, conforme descrito em "Os sistemas de entidades são o futuro do desenvolvimento do MMOG" (vinculado na resposta da pek), os sistemas de entidades são um paradigma de programação completamente diferente e, se você tentar tornar seus componentes ou entidades OO além da simples conveniência de empacotamento, você re fazendo errado.
Dave Sherohman 13/09/11
6
Eles são perfeitamente orientados a objetos. Não é apenas o OO com o qual a maioria das pessoas está acostumada, mas tudo bem. A orientação a objetos trata do estado de acoplamento com os métodos que operam nesse estado, só isso. Não requer que cada objeto tenha que ser um objeto da vida real ou algo assim.
Kylotan
@Kylotan: Mais uma vez, estou falando especificamente de modelos como os descritos em "Os sistemas de entidades são o futuro do desenvolvimento do MMOG", onde os componentes são dados burros operados por "sistemas" externos. Eles não são objetos, pois não têm comportamento / métodos; são relações (pense em linhas do banco de dados ou estruturas C). Uma equivalência equivocada entre POO e uso diário de "objeto" não tem nada a ver com isso. Da parte 3: "Eu sou vigorosamente contra o uso de OOP em qualquer lugar de uma implementação de ES; é muito fácil colocar alguns OOP de volta onde é inapropriado e iludir-se a pensar que essa é uma boa idéia".
Dave Sherohman
Estou ciente do artigo, mas é apenas a opinião de uma pessoa sobre como essas coisas devem ser feitas - não é o caminho padrão ou necessariamente o melhor, portanto, não é correto dizer que o design orientado a componentes é completamente diferente paradigma de programação. É perfeitamente possível - e muito mais comum - implementar uma arquitetura baseada em componente com código OO, e o uso do código OO não prejudica de maneira alguma o "componente" dele, assim como o uso de procedimentos armazenados prejudica o aspecto relacional de um banco de dados .
Kylotan
@ Dave, esta questão não é de forma alguma sobre sistemas de entidade-componente. O ES nem o design orientado a dados não são OO, é um erro comum, mas grande, ao assumir isso. se você pesquisar no Google, encontrará bons artigos que revelam esse mito. No entanto, esta pergunta é sobre os princípios gerais de OO de como projetar seus objetos, padrões de design de nível superior não respondem a essa pergunta.
Maik Semder 14/09/11
0

Estou atualizando minha resposta porque muitas coisas não estavam claras antes dos comentários. Por favor, fique comigo enquanto explico meus pensamentos.

Em geral, dois aspectos principais a serem considerados em qualquer projeto são a coesão e o acoplamento . Todos sabemos que precisamos de alta coesão e baixo acoplamento para poder criar um design mais reutilizável e extensível.

Portanto, se o mundo precisa gerenciar tudo, isso significa que ele possui baixa coesão e acoplamento rígido (porque precisa saber e fazer tudo). No entanto, esse também é o caso quando uma entidade do jogo precisa fazer tudo. Atualize sua localização, renderize sua textura, etc. etc.

O que você realmente está interessado é criar sistemas que se concentrem em um aspecto da entidade. Por exemplo, uma entidade do jogo poderia ter uma Textura, mas um Renderer seria responsável por renderizar essa textura na tela. O Renderer não se importa com as outras propriedades da entidade.

Indo um pouco mais longe, uma entidade do jogo é simplesmente um saco de propriedades. Essas propriedades são manipuladas por sistemas focados em propriedades específicas. E é aí que os Sistemas de Entidades Baseados em Componentes (CBES) entram, onde propriedades = componentes.

Especificamente, CBES com sistemas (ou subsistemas). Esse design tende a ter alguns sistemas focados em componentes específicos de uma entidade, sem se preocupar com os outros componentes que a entidade possui. Além disso, os sistemas são acoplados apenas às informações necessárias para processar esses componentes.

Vamos dar o seu exemplo. Como a entrada de onde mover a entidade é baseada no controlador do player, você provavelmente teria um PlayerControllerSystem. Esse sistema controlaria, além de muitas outras coisas, o PositionComponent da entidade. Nesse caso, o PlayerControllerSystem precisaria saber sobre o Level e o PositionComponent. Se, posteriormente, você decidir adicionar a detecção de colisão, criaria um CollisionSystem que usaria novamente a posição das entidades, mas desta vez para calcular as caixas delimitadoras (ou você pode ter um BoundingBoxComponent, sua chamada). O fato é que você pode ativar ou desativar facilmente o comportamento (mesmo em movimento) simplesmente adicionando / removendo componentes. Portanto, mais comportamento significa que mais sistemas estão manipulando os componentes de uma entidade, mas todos eles estão em uma classe bem definida com baixo acoplamento. Quer scripts? Adicione um ScriptComponent. BAM! Você acabou de adicionar recursos de script com 2 classes. Física? Som? O mesmo novamente.

Portanto, a razão pela qual estou defendendo o CBES com os subsistemas é que ele é perfeitamente OO e um sistema fácil de manter / extensível em geral. Adicionar um comportamento a uma entidade é tão simples quanto decidir quais dados esse comportamento precisa e quais entidades precisam.

Para obter mais informações sobre os sistemas de entidades baseados em componentes com subsistemas, há uma excelente série de postagens de blog de T = Machine na Entity Systems, que são o futuro do desenvolvimento do MMOG . O autor chegou a criar um wiki para coletar várias implementações denominadas Entity Systems Project

Um post geral (e bem conhecido) sobre os sistemas de entidades baseados em componentes em geral é o Evolve sua hierarquia que criou o sistema para o Tony Hawk Pro.

Finalmente, se você estiver procurando por uma biblioteca com código de exemplo, não vá além da biblioteca Artemis . Artemis é principalmente em Java, mas aqui está uma porta em C # (que atualmente estou usando no meu projeto XNA).

pek
fonte
2
-1, como isso não resolve o problema, apenas o move. Ele ainda não terá uma resposta clara sobre qual objeto toma a decisão, seja composta de componentes ou não.
Kylotan
11
O segundo link fornece um tutorial extenso sobre por que o comportamento das entidades do jogo deve ser externalizado para o Entity Systems. Artemis segue exatamente a mesma arquitetura.
pek
O segundo link é uma peça de opinião. Muitos são os componentes preferidos, mas não são uma solução para todos os problemas e, definitivamente, não têm uma solução intrínseca a esse.
Kylotan
A resposta mais votada nesta pergunta começa "Não há uma resposta simples. Como na maioria das coisas em programação, é uma troca". O segundo link fornece uma das muitas soluções. De fato, defende exatamente o oposto dessa resposta. Talvez eu deva editar minha resposta para torná-la mais clara. Mas, novamente, talvez isso não vai mudar os votos negativos: P
pek
@pek: Meu voto negativo é porque "usar componentes" não é uma resposta para esta pergunta. Uso componentes e recomendo o padrão de componentes, mas "usar um sistema de entidades baseado em componentes" não é uma resposta para todos os problemas no desenvolvimento de jogos ou mesmo na arquitetura de software de jogos - essa questão surge da mesma forma com componentes ou herança hierarquias ou tipos de entidade estática totalmente não relacionados!
0

Eu costumo ir com um design no qual os objetos se manipulam (afinal, para isso servem os métodos), mas o World base tem listas de todos os objetos e usa essas listas para coordená-los. Então, algo como:

class World {
  var walls = [];
  var actors = [];
  function update() {
    for each (actor in actors) {
      actor.update(walls);
    }
  }
}

class Actor {
  function update(walls) {
    this.move();
    for each (wall in walls) {
      this.collide(wall);
    }
  }
}
jhocking
fonte
0

Mantenha-o seco, tímido e diga ao outro cara.

É melhor pedir ao mundo que mova seu ator do que perguntar ao mundo se você pode ir para onde você quer ir. Dessa forma, você pode alterar o algoritmo de busca de caminhos facilmente na classe mundial e tal. Claro, você poderia deixar isso para uma classe básica para o ator, mas essa é a direção que eu tomaria e a razão disso.

Daniel Carlsson
fonte
-1

EDIT: Reescrito devido ao feedback afirmando que eu não tinha desenhado a linha suficientemente clara para esta resposta à pergunta original.

Gostaria de esclarecer um pouco mais a questão, se possível. A lógica que atua sobre um objeto deve ser interna ou externa a esse objeto. A última parte do post menciona especificamente sobre a longevidade do design como base para a pergunta, para evitar problemas mais tarde.

Minha resposta simples de alto nível é manter toda a lógica que atua sobre um objeto dentro desse objeto.

Você não precisa que o mundo mova um objeto, tudo o que você precisa é o objeto a ser movido e o vetor que representa o local a ser movido para ele. O mundo pode entrar em jogo quando um destino está sendo escolhido ou quando uma reação ao meio ambiente é necessária devido a uma colisão, mas essas estão fora do exemplo dado para trabalhar.

Para abordar a parte subjacente da questão e alcançar a longevidade do design, eu sugeriria uma arquitetura orientada a componentes. As arquiteturas de componentes dividem um objeto em conjuntos discretos de dados e funcionalidade (supondo que você vá com a minha resposta acima, que afirma manter a lógica com os dados). Se sua estrutura se parece com CEntity-> CHuman-> CSoldier-> CPlayerCharacter, você invariavelmente entrará em problemas em que precisa alterar alguma lógica e dependendo de onde ela fica nessa árvore (quantos outros tipos de seres humanos existem, por exemplo?) pode ter efeitos de varredura em vários tipos de objetos.

Em vez disso, um sistema de componentes teria um conjunto de interfaces que define o que o objeto CEntity é composto como ICompRenderable, ICompMoveable, ICompHealth, ICompInventory e assim por diante. Onde você teria CCompMoveableHuman e possivelmente um CCompMoveableSoldier e CCompMoveablePlayer para manter seus padrões de movimento individuais separados. Digamos que o soldado seja alterado para rodar em formações. Essa alteração afetará apenas entidades criadas usando esse componente.

Então, para resumir, sugiro que você contenha a lógica com os dados aos quais a lógica se aplica. Também recomendo dividir objetos em componentes discretos para reduzir o 'Onde devo colocar isso?' perguntas e proporcionar estabilidade no futuro com facilidade de manutenção e sua extensão.

Espero que isto ajude.

James
fonte
Estive analisando o padrão de componente nas últimas duas horas. (os links do pek são muito bons) .Mas ainda não está claro onde o "comportamento" real deve ocorrer. É claro que uma entidade possui componentes diferentes para entrada, renderização e física. Mas se eu entendi corretamente, os componentes contêm apenas os dados necessários para poder funcionar, mas na verdade não contêm a implementação. Vamos voltar ao movimento novamente; como devo fazer uma entidade se mover usando os componentes? Devo estender o componente de entrada e codificar a implementação lá? Ou alguma outra coisa gerenciar a implementação?
Omgoseat
Para mim, mantenho a lógica nos objetos, mas na verdade estou fazendo um sistema de componentes sobre o Design Orientado a Dados, o que significa que a maioria dos meus objetos é na verdade apenas a lógica de qualquer maneira que é mapeada para locais dentro dos dados para um processamento rápido contíguo ... E, no final do dia, aprenda um padrão de design de cada vez: D Quanto ao -1, gostaria de saber por que isso foi marcado dessa maneira, mesmo que seja apenas "eu gosto mais da minha resposta" eu ainda gostaria de sabe porque.
James
11
@ James, é justo. Fiz uma votação baixa porque os sistemas de entidade-componente não respondem à pergunta original. Um componente posição ainda pode mover -se ou o mundo pode iterar sobre todos os componentes de posição e movê-los. A mesma razão pela qual Kylotan rejeitou a resposta de pek, então pensei que a razão -1 fosse óbvia. E mesmo que fosse obrigatório que um sistema de componentes se movesse (o que não é o caso), ainda assim não seria uma resposta, apenas um caso de uso para uma das opções. Mas a pergunta era sobre as razões para usar um ou outro, seus prós e contras. A resposta não cobre isso.
Maik Semder
@Maik Semder. Obrigado pelo feedback. Reescrevi a resposta para tentar traçar as linhas com mais clareza para a pergunta inicial e a motivação geral para ela em primeiro lugar, conforme indicado no final da pergunta.
James
@ James, obrigado por reservar um tempo para editar a resposta em resposta ao meu comentário, muito apreciado. A resposta proposta para "manter toda a lógica que age sobre um objeto dentro desse objeto" derrota um recurso principal do OO, o encapsulamento. Como mostra o link de Joe, o uso de funções que não são de amigos e não membros aumenta o nível de encapsulamento de um objeto. Além disso, apresenta um método claro de como realmente medir o encapsulamento. Colocar a função de movimentação no mundo ou no ator diminui o encapsulamento do design, resultando em menos código flexível e mais difícil de manter.
Maik Semder 14/09/11
-1

Eu recomendo que você tente ler sobre arquiteturas baseadas em componentes. Existem algumas postagens no blog, apresentações e artigos sobre eles que podem fazer um trabalho melhor do que eu jamais poderia.

AltDevBlogADay tem algumas postagens sobre o assunto, sendo uma delas muito boa essa série sobre entidades de jogos: http://altdevblogaday.com/2011/07/10/the-game-entity-–-part-ia-retrospect/ várias partes que se concentram no problema que você está tentando resolver.

Kyle
fonte