Come posso implementare le funzionalità in un sistema di entità?


31

Dopo aver fatto due domande sui sistemi di entità ( 1 , 2 ) e aver letto alcuni articoli su di essi, penso di capirli molto meglio di prima. Ho ancora alcune incertezze, principalmente sulla costruzione di un emettitore di particelle, un sistema di input e una telecamera. Ovviamente ho ancora qualche problema nella comprensione dei sistemi di entità, e potrebbero applicarsi a tutta un'altra gamma di oggetti, ma ho scelto questi tre perché sono concetti molto diversi, dovrebbero coprire un terreno abbastanza ampio e aiutarmi a capire i sistemi di entità e come gestire problemi come questi, io stesso, mentre si presentano.

Sto costruendo un motore in JavaScript e ho implementato la maggior parte delle funzionalità principali, tra cui: gestione dell'input, sistema di animazione flessibile, emettitore di particelle, classi e funzioni matematiche, gestione delle scene, una telecamera e un rendering e un sacco di cose di altre cose che i motori di solito supportano. Ho letto la risposta di Byte56, che mi ha interessato a trasformare il motore in un sistema di entità. Rimarrebbe comunque un motore di gioco HTML5, con la filosofia di base della scena, ma dovrebbe supportare la creazione dinamica di entità dai componenti.


Il problema che ho, ora, sta adattando il mio vecchio concetto di motore a questo nuovo paradigma di programmazione. Queste sono alcune delle definizioni delle domande precedenti, aggiornate:

  • Un Entity è un identificatore. Non ha dati, non è un oggetto, è un semplice ID che rappresenta un indice nell'elenco scene di tutte le entità (che in realtà ho intenzione di implementare come matrice di componenti).

  • Un componente è un detentore di dati, ma con metodi che possono operare su tali dati. Il miglior esempio è un componente Vector2D, o "Posizione". Ha dei dati: xe y, ma anche alcuni metodi che rendono operanti sui dati un po 'più facile: add(), normalize(), e così via.

  • Un sistema è qualcosa che può operare su un insieme di entità che soddisfano determinati requisiti; di solito le entità devono avere un set specifico di componenti su cui operare. Il sistema è la parte "logica", la parte "algoritmo", tutte le funzionalità fornite dai componenti sono puramente per una più semplice gestione dei dati.


telecamera

La telecamera ha una Vector2Dproprietà position, una proprietà di rotazione e alcuni metodi per centrarla attorno a un punto. Ogni fotogramma viene inviato a un renderer, insieme a una scena, e tutti gli oggetti vengono tradotti in base alla sua posizione. La scena viene quindi renderizzata.

Come potrei rappresentare questo tipo di oggetto in un sistema di entità? La fotocamera sarebbe un'entità, un componente o una combinazione (secondo la mia risposta )?

Emettitore di particelle

Il problema che ho con il mio emettitore di particelle è, di nuovo, quale dovrebbe essere cosa. Sono abbastanza sicuro che le particelle stesse non dovrebbero essere entità, in quanto voglio supportarne oltre 10.000, e credo che la creazione di molte entità sarebbe un duro colpo per la mia performance.

Come potrei rappresentare questo tipo di oggetto in un sistema di entità?

Gestione input

L'ultimo di cui voglio parlare è come gestire l'input. Nella mia attuale versione del motore, esiste una classe chiamata Input. È un gestore che si iscrive agli eventi del browser, come la pressione dei tasti e le modifiche alla posizione del mouse, e mantiene anche uno stato interno. Quindi, la classe del giocatore ha un react()metodo, che accetta un oggetto di input come argomento. Il vantaggio di ciò è che l'oggetto di input potrebbe essere serializzato in .JSON e quindi condiviso sulla rete, consentendo simulazioni multiplayer fluide.

Come si traduce in un sistema di entità?

Risposte:


26
  • Fotocamera: rendere questo un componente sarebbe abbastanza pulito. Avrebbe solo unisRenderingbandiera e profondità di campo come ha detto Sean. Oltre al "campo visivo" (immagino che potresti chiamarlo scala in 2D?) E una zona di output. La zona di output potrebbe definire la parte della finestra di gioco a cui viene riprodotta questa telecamera. Non avrebbe una posizione / rotazione separata come dici tu. L'entità che crei che ha un componente telecamera utilizza i componenti di posizione e rotazione di quell'entità. Quindi avresti un sistema di telecamere che cerca entità con componenti di telecamera, posizione e rotazione. Il sistema prende quell'entità e disegna tutte le entità che può "vedere" dalla sua posizione, rotazione, profondità di vista e campo visivo, alla porzione specificata dello schermo. Questo ti offre molte opzioni per simulare più porte di visualizzazione, finestre di "visualizzazione caratteri", multiplayer locale,

  • Emettitore di particelle: anche questo dovrebbe essere solo un componente. Il sistema particellare cercherebbe entità con posizione, rotazione ed emettitore di particelle. L'emettitore ha tutte le proprietà necessarie per riprodurre il tuo emettitore attuale, non sono sicuro di cosa siano tutti, roba del genere: velocità, velocità iniziale, tempo di decadimento e così via. Non dovresti fare più passaggi. Il sistema particellare sa quali entità hanno quel componente. Immagino che potresti riutilizzare buona parte del tuo codice esistente.

  • Input: dovrei dire che trasformare questo in un componente ha più senso dato i suggerimenti che ho fatto sopra. Il tuoinput systemverrebbe aggiornato ogni frame con gli eventi di input correnti. Quindi quando attraversa tutte le entità che hanno il componente di input, applicherà quegli eventi. Il componente di input avrebbe un elenco di eventi tastiera e mouse tutti i callback dei metodi associati. Non sono sicuro di dove vivrebbero i callback del metodo. Forse qualche classe di controller di input? Qualunque cosa abbia più senso per successive modifiche da parte degli utenti del tuo motore. Ma questo ti darebbe il potere di applicare facilmente il controllo di input alle entità della telecamera, entità del giocatore o qualunque cosa tu abbia bisogno. Vuoi sincronizzare il movimento di un gruppo di entità con la tastiera? Fornisci loro tutti i componenti di input che rispondono agli stessi input e il sistema di input applica quegli eventi di spostamento a tutti i componenti che li richiedono.

Quindi la maggior parte di questo è appena fuori dalla mia testa, quindi probabilmente non avrà senso senza ulteriori spiegazioni. Fammi solo sapere su cosa non sei chiaro. Fondamentalmente, ti ho dato molto su cui lavorare :)


Un'altra grande risposta! Grazie! Ora, il mio unico problema è l'archiviazione e il recupero di entità rapidamente, in modo che l'utente possa effettivamente implementare un loop / logica di gioco ... Cercherò di capirlo da solo, ma devo prima imparare come JavaScript gestisce le matrici, gli oggetti e valori indefiniti in memoria per fare una buona ipotesi ... Questo sarà un problema perché diversi browser potrebbero implementarlo in modo diverso.
jcora,

Ciò sembra architettonicamente puro, ma come fa il sistema di rendering a determinare la telecamera attiva a corto di iterazioni attraverso tutte le entità?
Pace

@Pace Dato che vorrei che la telecamera attiva fosse trovata molto rapidamente, avrei probabilmente permesso al sistema di telecamere di mantenere un riferimento alle entità che hanno una telecamera attiva.
MichaelHouse

Dove metti la logica per controllare più telecamere (guarda, ruota, sposta, ecc.)? Come controlli le telecamere multiple?
plasmacel

@plasmacel Se hai più oggetti che condividono i controlli, sarà responsabilità del tuo sistema di controllo determinare quale oggetto riceve gli input.
MichaelHouse

13

Ecco come mi sono avvicinato a questo:

telecamera

La mia fotocamera è un'entità come qualsiasi altra, che ha componenti collegati:

  1. Transformha Translation, Rotatione Scaleproprietà, oltre ad altri per la velocità, ecc.

  2. Pov(Punto di vista) ha FieldOfView, AspectRatio, Near, Far, e quant'altro necessario per produrre una matrice di proiezione, oltre a una IsOrthobandiera utilizzato per commutare tra prospettiva e proiezioni ortogonali. Povfornisce inoltre una ProjectionMatrixproprietà di caricamento lento utilizzata dal sistema di rendering calcolata internamente in lettura e memorizzata nella cache fino a quando non viene modificata una delle altre proprietà.

Non esiste un sistema di telecamere dedicato. Il sistema di rendering mantiene un elenco di Pove contiene una logica per determinare quale utilizzare per il rendering.

Ingresso

Un InputReceivercomponente può essere collegato a qualsiasi entità. Questo ha un gestore di eventi collegato (o lambda se la tua lingua lo supporta) che viene utilizzato per contenere l'elaborazione di input specifica dell'entità, che accetta parametri per lo stato chiave attuale e precedente, la posizione attuale e precedente del mouse e lo stato del pulsante, ecc. (In realtà, ci sono gestori separati per mouse e tastiera).

Ad esempio, in un gioco di test simile agli Asteroidi che ho creato quando mi sono abituato a Entità / Componente, ho due metodi lambda di input. Uno gestisce la navigazione della nave elaborando i tasti freccia e la barra spaziatrice (per sparare). L'altro gestisce l'input di tastiera generale: tasti per uscire, mettere in pausa, ecc., Livello di riavvio, ecc. Creo due componenti, attacco ogni lambda al suo componente, quindi assegno il componente ricevitore di navigazione all'entità nave, l'altro a un entità del processore comandi non visibile.

Ecco il gestore dell'evento per gestire le chiavi che si trovano tra i frame che vengono collegati al InputReceivercomponente della nave (C #):

  void ship_input_Hold(object sender, InputEventArgs args)
    {
        var k = args.Keys;
        var e = args.Entity;

        var dt = (float)args.GameTime.ElapsedGameTime.TotalSeconds;

        var verlet = e.As<VerletMotion>();
        var transform = e.As<Transform>();

        if (verlet != null)
        {

        /// calculate applied force 
            var force = Vector3.Zero;
            var forward = transform.RotationMatrix.Up * Settings.ShipSpeedMax;

            if (k.Contains(Keys.W))
                force += forward;

            if (k.Contains(Keys.S))
                force -= forward;

            verlet.Force += force * dt;
        }

        if (transform != null)
        {
            var theta = Vector3.Zero;

            if (k.Contains(Keys.A))
                theta.Z += Settings.TurnRate;

            if (k.Contains(Keys.D))
                theta.Z -= Settings.TurnRate;

            transform.Rotation += theta * dt;
        }

        if (k.Contains(Keys.Space))
        {
            var time = (float)args.GameTime.TotalGameTime.TotalSeconds - _rapidFireLast;

            if (time >= _rapidFireDelay)
            {
                Fire();
                _rapidFireLast = (float)args.GameTime.TotalGameTime.TotalSeconds;
            }
        }
    }

Se la tua fotocamera è mobile, dai il suo componente InputReceivere il suo Transformcomponente, collega un lambda o un gestore che implementa qualsiasi tipo di controllo tu voglia, e il gioco è fatto.

Questo è un po 'pulito in quanto puoi spostare il InputReceivercomponente con l'handler di navigazione attaccato dalla nave a un asteroide, o qualsiasi altra cosa, e volare invece. Oppure, assegnando un Povcomponente a qualsiasi altra cosa nella tua scena - un asteroide, un lampione, ecc. - puoi vedere la tua scena dalla prospettiva di quell'entità.

Una InputSystemclasse che mantiene uno stato interno per tastiera, mouse, ecc. InputSystemFiltra la sua raccolta di entità interna su entità che hanno un InputReceivercomponente. Nel suo Update()metodo, scorre attraverso quella raccolta e chiama i gestori di input collegati a ciascuno di quei componenti nello stesso modo in cui il sistema di rendering disegna ciascuna entità con un Renderablecomponente.

Particelle

Questo dipende davvero da come pensi di interagire con le particelle. Se hai solo bisogno di un sistema di particelle che si comporti come un oggetto - diciamo, uno spettacolo pirotecnico che il giocatore non può toccare o colpire - allora creerei una singola entità e un ParticleRenderGroupcomponente che contiene tutte le informazioni necessarie per le particelle - decadimento, ecc. - che non è coperto dal Renderablecomponente. Durante il rendering, il sistema di rendering vedrebbe se un'entità ha RenderParticleGroupallegato e lo gestirà di conseguenza.

Se hai bisogno di singole particelle per partecipare al rilevamento delle collisioni, rispondere all'input, ecc., Ma vuoi solo renderle come un batch, creerei un Particlecomponente che contiene tali informazioni su una base per particella e le creo come entità separate. Il sistema di rendering può ancora raggrupparli, ma verranno trattati come oggetti separati dagli altri sistemi. (Funziona molto bene con l'istanza.)

Quindi, sia nel tuo MotionSystem(o qualunque sia il tuo utilizzo che gestisca l'aggiornamento della posizione dell'entità, ecc.) O in quello dedicato ParticleSystem, esegui qualunque elaborazione sia richiesta per ogni particella per trama. Il RenderSystemsarebbe responsabile per la costruzione / dosaggio e caching collezioni di particelle come sono creati e distrutti, e renderli come richiesto.

Una cosa bella di questo approccio è che non devi avere casi particolari di collisione, abbattimento, ecc. Per le particelle; possono ancora essere utilizzati il ​​codice che scrivi per ogni altro tipo di entità.

Conclusione

Se stai prendendo in considerazione l'idea di passare multipiattaforma - non super applicabile a JavaScript - tutto il tuo codice specifico per la piattaforma (vale a dire, rendering e input) è isolato in due sistemi. La tua logica di gioco rimane in classi agnositiche (movimento, collisione, ecc.), Quindi non dovresti toccarle durante il porting.

Capisco e concordo con la posizione di Sean secondo cui le cose che scaricano le scarpe in uno schema per aderire rigorosamente allo schema, piuttosto che modificarlo per soddisfare le esigenze della tua applicazione, sono cattive. Semplicemente non vedo nulla in Input, Camera o Particles che richieda quel tipo di trattamento.


Dove metti la logica per controllare più telecamere (guarda, ruota, sposta, ecc.)?
Plasmacel

7

La logica di input e di gioco verrà probabilmente gestita in un blocco di codice dedicato esterno al sistema dei componenti dell'entità. È tecnicamente possibile inserirlo nel design, ma ci sono pochi vantaggi: la logica del gioco e l'interfaccia utente sono confuse e piene di astrazioni che perdono, qualunque cosa tu faccia, e cercare di forzare il piolo quadrato in un foro rotondo solo per purezza architettonica è uno spreco di tempo.

Allo stesso modo, gli emettitori di particelle sono bestie speciali, soprattutto se ti preoccupi delle prestazioni. Un componente emettitore ha un senso, ma la grafica farà qualche magia speciale con quei componenti, mescolati con la magia per il resto del rendering.

Per quanto riguarda la tua fotocamera, dai alle telecamere solo un flag attivo e forse un indice di "profondità" e lascia che il sistema grafico li renda tutti abilitati. Questo in realtà è utile per molti trucchi, tra cui le GUI (vuoi che la tua GUI sia resa in modalità ortografica in cima al mondo di gioco? Nessun problema, sono solo due telecamere con diverse maschere di oggetti e la GUI impostata su un livello superiore). È anche utile per i livelli di effetti speciali e simili.


4

La fotocamera sarebbe un'entità o semplicemente un componente?

Non sono sicuro di cosa stia davvero facendo questa domanda. Dato che le uniche cose che hai nel gioco sono entità, le telecamere devono essere entità. La funzionalità della fotocamera è implementata tramite una sorta di componente della fotocamera. Non avere componenti "Posizione" e "Rotazione" separati - che è di livello troppo basso. Dovrebbero essere combinati in una sorta di componente WorldPosition che si applicherebbe a qualsiasi entità situata nel mondo. Per quanto riguarda quale usare ... devi in ​​qualche modo ottenere la logica nel sistema. O codificalo nel tuo sistema di gestione della videocamera, oppure allega script o altro. È possibile avere un flag abilitato / disabilitato su un componente della telecamera se aiuta.

Sono abbastanza sicuro che le particelle stesse non dovrebbero essere entità

Anch'io. Un emettitore di particelle sarebbe un'entità e il sistema di particelle traccerebbe le particelle associate a una data entità. Cose come questa sono dove ti rendi conto che "tutto è un'entità" è assurdamente poco pratico. In pratica, le uniche cose che sono entità sono oggetti relativamente complessi che beneficiano di combinazioni di componenti.

Per quanto riguarda l'input: l'input non esiste nel mondo di gioco in quanto tale, quindi è gestito da un sistema. Non necessariamente un "sistema di componenti" perché non tutto il gioco ruota attorno ai componenti. Ma ci sarà un sistema di input. Potresti voler contrassegnare l'entità che risponde all'input con una sorta di componente Player, ma l'input sarà complesso e completamente specifico del gioco, quindi non ha senso cercare di creare componenti per questo.


1

Ecco alcune delle mie idee per risolvere questi problemi. Probabilmente avranno qualcosa di sbagliato in loro, e probabilmente ci sarà un approccio migliore, quindi per favore, indirizzami a quelli nella tua risposta!

Macchina fotografica :

C'è un componente "Camera", che può essere aggiunto a qualsiasi entità. Non riesco davvero a capire quali dati dovrei inserire in questo componente, però: potrei avere componenti "Posizione" e "Rotazione" separati! Il followmetodo non ha bisogno di essere implementato, perché è già in seguito l'entità a cui è collegato! E sono libero di spostarlo. Il problema con questo sistema sarebbe costituito da diversi oggetti fotocamera: come si può RendererSystemsapere quali usare? Inoltre, passavo semplicemente l'oggetto della telecamera, ma ora sembra che la RendererSystemnecessità di iterare due volte su tutte le entità: prima per trovare quelle che agiscono come telecamere, e in secondo luogo, per rendere effettivamente tutto.

ParticleEmitter :

Ci sarebbe uno ParticleSystemche aggiornerebbe tutte le entità che avevano un componente "Emettitore". Le particelle sono oggetti stupidi in uno spazio di coordinate relativo, all'interno di quel componente. C'è un problema di rendering qui: avrei bisogno di creare un ParticleRenderersistema o estendere la funzionalità di quello esistente.

Sistema di input :

La preoccupazione principale per me qui era la logica o il react()metodo. L'unica soluzione che mi è venuta in mente è un sistema separato per questo, e un componente per ciascun sistema, che indicherebbe quale usare. Sembra davvero troppo confuso e non so come gestirlo bene. Una cosa è che, per quanto mi riguarda, Inputpuò essere implementato come una classe, ma non vedo come potrei integrarlo nel resto del gioco.


Non c'è davvero alcun motivo per cui il RendererSystem esegua l'iterazione su tutte le entità: dovrebbe già avere un elenco di disegni (e telecamere e luci (a meno che le luci non siano disegnabili)) o sapere dove si trovano tali elenchi. Inoltre, probabilmente vorrai fare la selezione per le telecamere che vuoi renderizzare, quindi forse la tua fotocamera potrebbe contenere un elenco di ID entità disegnabili che sono visibili ad essa. Potresti avere molte videocamere e una attiva o una videocamera che viene collegata a diversi POV, entrambi i quali potrebbero essere controllati da un numero qualsiasi di cose, come script, trigger e input

@ melak47, è vero, ci ho pensato anche io, ma volevo implementarlo come fa Aremis. Ma questo "sistema memorizza i riferimenti a entità rilevanti" sembra essere sempre più imperfetto ...
jcora

Artemis non memorizza ogni tipo di componente nella sua lista? quindi non avresti esattamente quegli elenchi di componenti disegnabili, componenti di telecamere, luci e cosa non da qualche parte?
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.