Aggiornamenti oggetti, interni vs esterni?


8

Il titolo è un po 'confuso, ma non posso pensare a come spiegare la mia domanda in una breve frase. Quindi eccolo qui:

Ogni volta che scrivo motori di gioco, sia che si tratti di fisica / piastrelle, ecc., Arrivo sempre al punto in cui non sono sicuro di come dovrei gestire le cose. Le entità nel mondo dovrebbero gestirsi da sole o dovrebbe esistere un sistema globale che le gestisca?

Ecco un semplice esempio: spostare le cose. Ogni oggetto dovrebbe vedere il mondo che lo circonda (verificare la presenza di collisioni) e muoversi in base a quello.

[nota, questo è un gioco basato su tessere in cui l'oggetto si sposta per tessera, quindi non sto usando la fisica per passare da tessera a tessera]

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

O il movimento di ogni oggetto dovrebbe essere gestito nel mondo, dove il mondo controlla tutto?

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);
         }
    }
}

Non ha molta importanza per questo esempio, suppongo, ma in seguito mi vedrò nei guai. Grazie in anticipo.


1
Più alla domanda, dovresti Actorsaperlo world?
Deceleratedcaviar

1
Penso che stai unendo gli oggetti ai componenti e questo confonde il problema di chi sta facendo cosa. Potrebbe essere un problema semantico poiché Componente è un termine fortemente sovraccarico. Penso che se chiarisci cos'è un Oggetto, cosa intendi per Componente e come organizzi i piccoli motori che effettivamente eseguono il lavoro, potresti rispondere alla tua domanda.
Patrick Hughes,

1
Devo essere d'accordo con gli altri sul fatto che stai chiedendo se l'oggetto di gioco si muove da solo o il mondo muove l'oggetto di gioco. Il codice sopra non mi porta a credere che tu intenda veramente un sistema di tipi di componenti e quindi potrebbe essere fuorviante nel titolo della tua domanda.
James,

Ho usato alcuni termini fuorvianti ora che voi ragazzi ne parlate. Andiamo a regolarlo ora! Modifica: vedi che è già stato modificato, grazie!
omgnoseat

@daniel Gli attori devono chiaramente essere informati sul mondo che li circonda per muoversi in modo efficace, anche se in genere progetterei un contenitore che incapsula entrambi, ma nessuno dei due lo sa. per esempio. World contiene sia Actor che Level; L'attore conosce Level (o meglio viene detto Level quando necessario, ma non un riferimento permanente) ma non World.
jhocking

Risposte:


2

Vedo che i tuoi esempi sono in Java, ma i tuoi tag non specificano alcuna lingua. In C ++, la risposta è nessuna delle due : non dovresti usare una funzione membro per questo!

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

4
Devo dire un grande fan dei libri che l'autore dell'articolo ha scritto. Comunque il ragionamento dell'articolo è, per quanto potrei dire, buona teoria ma non codice pratico. Spostando metodi come questi dagli oggetti che ti riguardano, stai aggiungendo più posizioni per cercare funzionalità che li riguardano, il che causa solo confusione e livelli più elevati di manutenzione richiesta. Solo il mio punto di vista, quindi no + o -. Solo due centesimi.
James,

1
Inoltre, devo dire che assomiglia a C # con l'ereditarietà usando: ClassName invece di estendere ClassName.
James,

È uno pseudo codice basato su C #, ma non dovrebbe essere rilevante per la mia domanda :)
omgnoseat

1
@omgnoseat: è rilevante perché il consiglio è impossibile in una lingua senza funzioni spoglie e la sua utilità è ridotta in qualsiasi lingua senza ADL. La buona architettura non è indipendente dalla scelta della lingua.

2
@James: non c'è nulla di non-OO nelle funzioni nude, se le stesse funzioni invocano metodi che seguono i principi di progettazione OO. Questo è il punto dell'articolo. (OO è anche non è sinonimo di buon design Se un design trasparente o semplice richiede non si utilizza OO, OO fosso, non il disegno..)

1

Non esiste una risposta semplice.

Come per la maggior parte delle cose in programmazione, è un compromesso. Dare più potenza ai singoli oggetti li rende più grandi, e quindi più lenti, ma facilita la comprensione e l'estensione del motore. Avere una mega-classe in grado di gestire tutto insieme potrebbe essere più veloce, ma a costo di avere una mega-classe; cioè, è generalmente considerata una cattiva forma per fare lezioni supermassicci.

Ho letto alcuni articoli sulla progettazione basata su componenti e dati, in cui hai una singola classe che rappresenta tutti gli oggetti di un certo tipo, memorizzando i loro dati in elenchi e passando solo gli indici per ottenere le proprietà di un singolo oggetto. Anche se posso vedere questo come un tipo di architettura praticabile, lo sento piuttosto fregare con l'intero punto di orientamento dell'oggetto, che ha anche ottenuto la sua giusta dose di critiche.

Personalmente raccomando di dare più potenza agli oggetti. Ha più senso in un linguaggio orientato agli oggetti e (immagino) più facile da capire e mantenere nel tempo.


In qualche modo sottovaluti dicendo che lo stile del componente e il design guidato dai dati che hai descritto "avvitano con il punto centrale di OO" - non è affatto OO . Almeno come descritto in "I sistemi di entità sono il futuro dello sviluppo di MMOG" (collegato nella risposta di Pek), i sistemi di entità sono un paradigma di programmazione completamente diverso e se ti ritrovi a provare a rendere i tuoi componenti o entità OO al di là della semplice praticità di packaging, re farlo nel modo sbagliato.
Dave Sherohman,

6
Sono perfettamente orientati agli oggetti. Non è la OO a cui la maggior parte delle persone è abituata, ma va bene. L'orientamento agli oggetti riguarda lo stato di accoppiamento con i metodi che operano su quello stato, tutto qui. Non richiede che ogni oggetto debba essere un oggetto reale o qualcosa del genere.
Kylotan,

@Kylotan: Ancora una volta, sto parlando in particolare di modelli come quelli descritti in "I sistemi di entità sono il futuro dello sviluppo di MMOG", in cui i componenti sono dati stupidi gestiti da "sistemi" esterni. Non sono oggetti in quanto non hanno alcun comportamento / metodo; sono relazioni (si pensi alle righe del database o alle strutture C). Un'equivalenza errata tra OOP e l'uso quotidiano di "oggetto" non ha nulla a che fare con esso. Dalla parte 3: "Sono fermamente contrario all'utilizzo di OOP ovunque in un'implementazione di ES; è troppo facile intrufolarsi di nuovo in OOP dove è inappropriato e illudersi nel pensare che questa sia una buona idea".
Dave Sherohman,

Sono a conoscenza dell'articolo, ma è solo l'opinione di una persona su come dovrebbero essere fatte queste cose - non è il modo standard, o necessariamente il modo migliore, quindi non è corretto affermare che il design guidato dai componenti è completamente diverso paradigma di programmazione. È perfettamente possibile - e molto più comune - implementare un'architettura basata su componenti con codice OO, e l'uso del codice OO non danneggia in alcun modo la "componente" di esso più che l'utilizzo di procedure memorizzate danneggia l'aspetto relazionale di un database .
Kylotan,

@Dave, questa domanda non riguarda affatto i sistemi con componenti entità. Né ES né la progettazione basata sui dati non sono OO, è un errore comune, ma grosso da supporre. se vai su Google per questo troverai alcuni buoni articoli che svelano questo mito. Tuttavia, questa domanda riguarda i principi generali di OO su come progettare i tuoi oggetti, i modelli di progettazione di livello superiore non rispondono affatto a questa domanda.
Maik Semder,

0

Sto aggiornando la mia risposta perché molte cose non erano chiare prima dei commenti. Per favore, nudo con me mentre spiego i miei pensieri.

In generale, due aspetti chiave da considerare in qualsiasi progetto sono la coesione e l' accoppiamento . Sappiamo tutti che abbiamo bisogno di elevata coesione e basso accoppiamento per poter realizzare un design più riutilizzabile ed estensibile.

Quindi, se il mondo deve gestire tutto, ciò significa che ha una bassa coesione e un accoppiamento stretto (perché deve sapere e fare tutto). Tuttavia, questo è anche il caso in cui un'entità di gioco deve fare tutto. Aggiorna la sua posizione, renderizza la sua trama, ecc. Ecc.

Ciò a cui sei veramente interessato è creare sistemi incentrati su un aspetto dell'entità. Ad esempio, un'entità di gioco potrebbe avere una trama, ma un renderizzatore sarebbe responsabile del rendering di quella trama sullo schermo. Al Renderer non importa quali altre proprietà ha l'entità.

Portandolo un po 'oltre, un'entità di gioco è semplicemente una borsa di proprietà. Queste proprietà sono manipolate da sistemi focalizzati su proprietà specifiche. Ed è qui che entrano in gioco i sistemi di entità basati su componenti (CBES), dove proprietà = componenti.

In particolare, CBES con sistemi (o sottosistemi). Questo design tende ad avere alcuni sistemi focalizzati su componenti specifici di un'entità senza preoccuparsi di quali altri componenti l'entità abbia. Inoltre, i sistemi sono associati solo alle informazioni necessarie per elaborare questi componenti.

Facciamo il tuo esempio. Poiché l'input di dove spostare l'entità si basa sul controller del giocatore, probabilmente avresti un PlayerControllerSystem. Questo sistema controllerebbe, a parte molte altre cose, il Componente di posizione dell'entità. In questo caso, il PlayerControllerSystem dovrebbe conoscere il Level e il PositionComponent. Se in seguito decidessi di aggiungere il rilevamento delle collisioni, creeresti un CollisionSystem che utilizzerebbe nuovamente la posizione delle entità, ma questa volta per calcolare i riquadri di delimitazione (o potresti avere un BoundingBoxComponent, la tua chiamata). Il fatto è che puoi facilmente attivare o disattivare il comportamento (anche al volo) semplicemente aggiungendo / rimuovendo i componenti. Quindi, più comportamento significa che più sistemi stanno manipolando i componenti di un'entità, ma sono tutti in una classe ben definita con basso accoppiamento. Vuoi lo scripting? Aggiungi un componente Script. BAM! Hai appena aggiunto funzionalità di scripting con 2 classi. Fisica? Suono? Ancora lo stesso.

Quindi, il motivo per cui sto sostenendo CBES con sottosistemi è che è perfettamente OO e un sistema globale facilmente gestibile / estensibile. Aggiungere un comportamento a un'entità è semplice come decidere quali dati hanno bisogno di quel comportamento e quali entità ne hanno bisogno.

Per ulteriori informazioni sui sistemi di entità basati su componenti con sottosistemi, esiste un'eccellente serie di post di blog di T = Machine presso Entity Systems, il futuro dello sviluppo di MMOG . L'autore è persino arrivato alla creazione di un wiki per la raccolta di varie implementazioni nominate Entity Systems Project

Un post generale (e ben noto) sui sistemi di entità basati su componenti in generale è Evolve la tua gerarchia che ha creato il sistema per Tony Hawk Pro.

Infine, se stai cercando una libreria con un codice di esempio, non andare oltre la libreria Artemis . Artemis è principalmente in Java ma qui c'è una porta in C # (che sto attualmente usando nel mio progetto XNA).


2
-1, poiché questo non risolve il problema, lo sposta e basta. Non avrà ancora una risposta chiara su quale oggetto prende la decisione, che sia composta da componenti o meno.
Kylotan,

1
Il secondo link fornisce un ampio tutorial sul perché il comportamento delle entità di gioco dovrebbe essere esternalizzato ai sistemi di entità. Artemide segue esattamente la stessa architettura.
pek,

Il secondo link è un'opinione. I componenti sono preferiti da molti ma non sono una soluzione a tutti i problemi e sicuramente non hanno una soluzione intrinseca a questo.
Kylotan,

La risposta più votata in questa domanda inizia "Non esiste una risposta semplice. Come per la maggior parte delle cose nella programmazione, è un compromesso". Il secondo collegamento fornisce una delle tante soluzioni. In realtà, sostiene l'esatto contrario di quella risposta. Forse dovrei modificare la mia risposta per renderlo più chiaro. Ma poi di nuovo, forse questo non cambierà i voti negativi: P
pek,

@pek: il mio downvote è perché "usa componenti" non è una risposta a questa domanda. Uso i componenti e raccomando lo schema dei componenti, ma "utilizzare un sistema di entità basato sui componenti" non è una risposta a tutti i problemi nello sviluppo del gioco, o persino nell'architettura del software di gioco - questa domanda si pone allo stesso modo con i componenti o l'ereditarietà gerarchie o tipi di entità statiche totalmente indipendenti!

0

Di solito vado con un design in cui gli oggetti si gestiscono da soli (ecco a cosa servono i metodi), ma il mondo di base ha elenchi di tutti gli oggetti e li usa per coordinarli. Quindi qualcosa del tipo:

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);
    }
  }
}

0

Tienilo ASCIUTTO, timido e dillo all'altro ragazzo.

È meglio chiedere al mondo di spostare il tuo attore piuttosto che chiedere al mondo se puoi spostarti dove vuoi andare. In questo modo è possibile modificare facilmente l'algoritmo di pathfinding nella classe mondiale e simili. Certo, potresti lasciarlo solo a una base per l'attore, ma questa è la direzione che prenderei e il motivo per cui.


-1

EDIT: riscritto a causa del feedback che afferma che non avevo tracciato la linea abbastanza chiara per questa risposta alla domanda originale.

Vorrei chiarire un po 'di più la domanda, se possibile. La logica che agisce su un oggetto deve essere interna o esterna a quell'oggetto. L'ultima parte del post menziona specificamente la longevità del design come base per porre la domanda, per evitare problemi in seguito.

La mia semplice risposta di alto livello è mantenere tutta la logica che agisce su un oggetto all'interno di quell'oggetto.

Non è necessario che il mondo muova un oggetto, tutto ciò che serve è l'oggetto da spostare e il vettore che rappresenta la posizione per spostarsi su di esso. Il mondo può entrare in gioco quando viene scelta una destinazione o quando è richiesta una reazione all'ambiente a causa di una collisione, ma quelli sono al di fuori dell'esempio dato su cui lavorare.

Per affrontare la parte sottostante della domanda e raggiungere la longevità del design, suggerirei un'architettura orientata ai componenti. Le architetture componenti suddividono un oggetto in insiemi discreti di dati e funzionalità (supponendo che tu vada con la mia risposta sopra che afferma di mantenere la logica con i dati). Se la tua struttura assomiglia a CEntity-> CHuman-> CSoldier-> CPlayerCharacter, invariabilmente ti imbatterai in problemi in cui è necessario modificare alcune logiche e a seconda di dove va in quell'albero (quanti altri tipi di umani ci sono ad esempio?) può avere effetti ampi su più tipi di oggetti.

Un sistema a componenti avrebbe invece una serie di interfacce che definiscono la composizione dell'oggetto CEntity come ICompRenderable, ICompMoveable, ICompHealth, ICompInventory e così via. Dove avresti CCompMoveableHuman e possibilmente un CCompMoveableSoldier e CCompMoveablePlayer per mantenere separati i loro schemi di movimento individuali. Quindi diciamo che il soldato viene modificato per correre in formazioni. Questa modifica interesserà solo le entità create utilizzando quel componente.

Quindi, per riassumere, suggerisco di contenere la logica con i dati a cui si applica la logica. Consiglio anche di suddividere gli oggetti in componenti discreti per ridurre il 'Dove lo metto?' domande e fornire stabilità nel futuro con facilità di manutenzione ed è estensibile.

Spero che sia di aiuto.


Ho esaminato il modello dei componenti nelle ultime due ore. (I collegamenti di pek sono molto belli). Ma non è ancora chiaro dove debba avvenire il "comportamento" reale. È chiaro che un'entità ha componenti diversi per input, rendering, fisica, ma se ho capito bene, i componenti contengono solo i dati necessari per poter funzionare, ma in realtà non contengono l'implementazione. Torniamo di nuovo allo spostamento; Come devo fare spostare un'entità usando i componenti? Devo estendere il componente di input e codificare l'implementazione lì? O qualcos'altro gestire l'implementazione?
omgnoseat

Per me mantengo la logica negli oggetti, ma in realtà sto facendo un sistema di componenti in cima al Design orientato ai dati, il che significa che la maggior parte dei miei oggetti sono in realtà solo la logica che viene comunque mappata in posizioni all'interno dei dati per un'elaborazione veloce contigua ... E alla fine della giornata, impara un modello di progettazione alla volta: D Per quanto riguarda -1, apprezzerebbe sapere perché è stato taggato in quel modo, anche se è solo 'Mi piace la mia risposta migliore' Mi piacerebbe comunque sapere perché.
James,

1
@James, abbastanza giusto. Ho effettuato il downvoting perché i sistemi con componenti entità non rispondono alla domanda originale. Un componente posizione può ancora muoversi in sé o del mondo potrebbe iterare su tutti i componenti di posizione e spostarli. Lo stesso motivo per cui Kylotan ha votato male alla risposta di Pek, quindi ho pensato che il motivo -1 fosse ovvio. E anche se sarebbe obbligatorio per un sistema componente spostarsi da solo (il che non è il caso) non sarebbe comunque una risposta, solo un caso d'uso per 1 delle opzioni. Ma la domanda riguardava i motivi per usare l'uno o l'altro, i loro pro e contro. La risposta non copre questo.
Maik Semder,

@Maik Semder. Grazie per il feedback. Ho riscritto la risposta per provare a tracciare le linee in modo più chiaro alla domanda iniziale e alla sua spinta generale in primo luogo, come indicato alla fine della domanda.
James,

@James, grazie per aver dedicato del tempo per modificare la risposta in risposta al mio commento, molto apprezzato. La risposta proposta per "mantenere tutta la logica che agisce su un oggetto all'interno di quell'oggetto" sconfigge una caratteristica fondamentale di OO, vale a dire l'incapsulamento. Come mostra il collegamento di Joe, l'uso di funzioni non membro non amico aumenta il livello di incapsulamento di un oggetto. Inoltre, introduce un metodo chiaro su come misurare effettivamente l'incapsulamento. Mettere la funzione di spostamento in un mondo o in un attore riduce l'incapsulamento del design, risultando in un modo meno flessibile e più difficile da mantenere il codice.
Maik Semder,

-1

Ti consiglio di provare a leggere su architetture basate su componenti. Ci sono parecchi post sul blog, presentazioni e scritte su di loro che possono fare un lavoro migliore di quanto io abbia mai potuto.

AltDevBlogADay ha parecchi post a riguardo, uno dei quali è molto positivo in questa serie sulle entità di gioco: http://altdevblogaday.com/2011/07/10/the-game-entity-–-part-ia-retrospect/ Ci sono più parti che si concentrano sul problema che stai cercando di risolvere.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.