Sistema componente entità - progressione del gioco


8

Sono abbastanza nuovo per lo sviluppo del gioco (ma non per la programmazione) e sto cercando di capire quale sarebbe il modo migliore per gestire la comunicazione tra i mondi. Quello che voglio dire è questo:

Ho letto dei sistemi di componenti di entità (ECS) e di come le persone suggeriscono di usare mondi / spazi diversi ( http://gamedevelopment.tutsplus.com/tutorials/spaces-useful-game-object-containers--gamedev-14091 ) per una sottosezione di un gioco. Ad esempio un HUD, un inventario o un combattimento / movimento ottengono ciascuno un mondo / spazio separati (perché hanno una grafica e una logica sottostanti diverse).

Tuttavia, mi chiedevo come l'inventario, o l'HUD, fosse a conoscenza della salute di un giocatore quando la salute è gestita da uno spazio / mondo diverso, ad esempio in combattimento?

Ciò vale anche per la progressione del gioco in generale, ad esempio la finestra di dialogo con NPC (una finestra di dialogo sarebbe uno spazio separato poiché è una schermata popup) ma come si trasmetterebbero le scelte fatte (o lo stato di) della finestra di dialogo ad altri spazi / mondi . O praticamente qualsiasi altro tipo di evento che influenza la progressione del gioco in diversi spazi / mondi (salute, mana, missioni, dialoghi, combattimenti, inventario, hud, ecc.)

Come si potrebbe gestire questo tipo di design? È necessario un oggetto singleton (in fase di implementazione) che contenga tutto questo tipo di informazioni? Sarebbe strano perché poi la componentsnecessità di trasmettere ogni cambiamento a questo oggetto singleton che sembra fare le cose due volte (andando contro il DRY principale della programmazione) ...

Sono in perdita qui in termini di design, qualche suggerimento?


---MODIFICARE---

Quindi ho letto alcuni altri post suggeriti dai commenti e ho avuto un'idea generale delle possibilità, tuttavia ognuna di esse sembra avere un aspetto negativo importante che le rende semplicemente sbagliate. È molto probabile che sto supervisionando i dettagli che potrebbero risolvere questi aspetti negativi, quindi sentiti libero di correggermi. Proverò a fornire una panoramica e alcune risposte ad alcune domande.

Sto vedendo tre opzioni principali per "condividere" i dati tra spazi. Anche se la maggior parte dei post riguarda la condivisione di dati tra sistemi, credo che lo stesso possa essere applicato alla condivisione di dati tra sistemi.

1. Interrogazione

Esempio : se il mondo HUD ha bisogno di conoscere la salute attuale del giocatore, può interrogare un altro mondo e chiedere la salute attuale.

Unico inconveniente : i mondi devono conoscersi a vicenda, il che rappresenta un grave problema di dipendenza e va contro il disaccoppiamento.

2: messaggistica diretta (sincronizzazione e asincronizzazione)

Esempio : se durante il combattimento la salute di un giocatore cambia, può inviare messaggi (sincronizzazione e asincronizzazione, qualunque cosa sia necessaria) ad altri mondi che devono conoscere questo cambiamento.

Unico inconveniente : ancora il problema del disaccoppiamento: i mondi devono conoscersi a vicenda.

3: messaggistica indiretta (sincronizzazione e asincronizzazione) <- opzione migliore

Esempio : se durante il combattimento la salute di un giocatore cambia, può inviare messaggi (sincronizzazione e asincronizzazione, qualunque cosa sia necessaria) all'hub messaggi generale. Altri mondi / sistemi che devono conoscere questa modifica sono abbonati al canale di messaggi specifico e leggono i messaggi.

Upside : completamente disaccoppiato, facilmente gestibile ed estensibile.

Aspetto negativo / poco chiaro : quando il canale dei messaggi sa che i messaggi devono essere eliminati? O forse il sistema sottoscritto contrassegna (solo per se stesso) il messaggio come letto e attende nuovi messaggi -> messagebox diventa enorme dopo un po '. In che modo mondi / sistemi gestiscono l'ordine? Ad esempio durante un frame: se l'HUD ha già eseguito il polling del messaggio di integrità e dopo che lo stato cambia, il frame successivo viene aggiornato. Per alcune applicazioni questo potrebbe non essere il modo giusto.

D: Un singolo oggetto di gioco può esistere in più spazi

Sto usando il framework Artemis ECS che viene fornito con spazi incorporati (chiamati mondi). Ogni entità (e con essa, i dati sotto forma di componenti) viene creata su un mondo e quindi non può essere condivisa tra mondi.


La messaggistica è l'approccio standard qui: gamedev.stackexchange.com/questions/23834/…
MichaelHouse

Da quello che posso leggere nell'articolo collegato può esistere un singolo oggetto di gioco in più spazi. Se la grafica o la logica sono diverse tra gli spazi, separare i dati dalla grafica e dalla logica. Condividi l'oggetto del gioco di dati attraverso gli spazi e aggregalo con diversi oggetti di gioco logici e grafici.
Andreas,

Anche questa risposta che ho dato sui sistemi di messaggistica può aiutarti: gamedev.stackexchange.com/questions/7718/…
James,

Ho implementato tutte e tre le soluzioni (interrogazione, diretta e indiretta) nella mia vita di gioco. E posso dire che la terza opzione funziona meglio per me. Puoi facilmente disaccoppiare i sistemi ed eseguire la loro logica in parallelo. L'unico aspetto negativo è che devi fare 9 chiamate di funzione per instradare ogni singolo messaggio / evento da un sistema a un altro. Ovviamente puoi ottimizzarlo e il grande vantaggio è che non hai bisogno di mutex o singoli in questo approccio.
Gregorio,

@Gregory Grazie per il tuo imput, mi aspettavo che i messaggi indiretti fossero l'opzione migliore. Non ero a conoscenza delle 9 chiamate di funzione ma durante la pianificazione di questo messagehub mi sono reso conto che in effetti sarebbero state parecchie chiamate. Hai mai trovato una buona soluzione / alternativa alla cancellazione dei messaggi quando nessun sistema ne ha più bisogno?
Tim

Risposte:


1

Un modo per vederlo è che probabilmente stai mettendo troppo nei tuoi oggetti di gioco.

Non c'è motivo che il codice che in realtà si aggancia l'HUD per il vostro gioco in-world necessità di essere in un componente / sistema che vive in uno spazio particolare. Quel codice sarà forse meglio vivere in un gestore centrale o in uno script globale che ha accesso a tutti gli spazi e tutti gli oggetti e può quindi interagire con il codice che sa quando creare effettivamente uno spazio e cosa inserire (ad esempio il codice che genera il giocatore, salva il suo stato tra i livelli, ecc.).

Potresti anche avere uno "spazio principale" che contiene oggetti di gioco con logica o dati che devono persistere o manipolare gli spazi utilizzati per i livelli e l'interfaccia utente. Questo approccio è comune nei motori che costringono gli sviluppatori a inserire tutti gli script / la logica in componenti / oggetti (ad esempio, in Unity, creeresti un oggetto principale globale e lo imposti a persistere attraverso lo scarico della scena; se Unity avesse effettivamente spazi, tu " d usare quelli al posto della bandiera).

Ricorda, abusare di ECS equivale a abusare di schemi di progettazione; solo perché hai qualche nuovo strumento elegante non significa che dovresti usarlo per risolvere tutti i problemi che incontri. Valuta il tuo spazio problematico e seleziona la soluzione più adatta, anche se è la vecchia cosa oscura che i tuoi antenati usavano nei tempi bui degli anni '90. : p


0

Ho creato alcuni prototipi, ma niente di troppo grande e il modo in cui ho usato per gestire più spazi era semplicemente creare un oggetto di gioco che contiene, mondo, giocatore ecc. E che avrei impostato alcune proprietà che sono richieste da altri spazi come la salute , nell'oggetto di gioco

Ogni volta che viene chiamato otterrà la salute del giocatore. in questo modo potrei inviarlo all'HUD e visualizzare la barra della salute.

Non è il più pulito, ma fa il suo lavoro, ho fatto alcuni test di performance nel 2013 e tutto sembrava funzionare senza problemi. per evitare tali dipendenze potresti sempre far cadere la barra della salute quando la salute del giocatore è nulla.

Di solito quando il giocatore non esiste significa che l'utente si trova in un menu o in una scena.

Esempio di codice:

public float PlayerHealth {
  get {
    if (player !+ null) 
      return player.Health;
    return -1;
  }
}

Spero che questo sia quello che stavi cercando.


0

questo è qualcosa su cui sto effettivamente lavorando nelle ultime due settimane. Sto lavorando sulla mia libreria ECS (volevo farlo per esperienza e solo per provarlo, perché ho voluto farlo per un bel po 'di tempo).

Questo è il link github: https://github.com/gioragutt/xna-ecs

Per il tuo problema, ho sempre scritto una piccola libreria pubsub, che puoi vedere qui

Fondamentalmente, ho una EmsClientlezione da cui possono derivare cose. Attualmente, i miei componenti non lo fanno, ma le classi di livello più alto, anche se non c'è motivo di non farlo. Mi iscrivo a nomi di messaggi, e fornire un callback con la seguente firma: Action<JObject>. Come hai già capito, sto usando Json Objects come mezzo per trasferire messaggi. L'ho fatto dopo aver usato solo quelli di prima byte[], e ho scoperto che avevo bisogno di qualcosa di più generale, e dal momento che sono abituato a qualcosa di simile dal mio posto di lavoro (abbiamo un IPCD che funziona in modo simile, tranne il callback il metodo è sempre lo stesso, poiché di solito separiamo la responsabilità da gestori diversi).

Ce n'è uno EmsServer(uno sul server e uno su ciascun client) che è responsabile dello spostamento dei messaggi tra EmsClientil suo regno ( EmsServersul lato server sposta i messaggi tra EmsClientsil lato server, viceversa per il lato client).

Per la messaggistica tra il client e il server, ho creato un EmsServerEndpointche è un EmsClientse stesso, fa solo la logica del buffering dei messaggi inviati sul suo realm e li scarica in altri realm (FE il client invia il messaggio al server, mentre quando il server trasferisce ciascun messaggio a tutti i client connessi.

Puoi vedere l'uso in molti posti, fe: ClientGameManager, ServerGameManager.

Considerando che, per il tuo esempio, se voglio aggiungere un componente GUI per un giocatore, puoi guardare QUI , i metodi BeginAllocateLocale BeginAllocateRemote, che sono responsabili della costruzione GameObjectsdei giocatori. Ciascuno GameObjectcontiene un Entity(dalla libreria ECS) e un IComponentContainer(che è anche, dalla libreria ECS). Ognuno GameObjectottiene automaticamente una trasformazione (come in Unity, da cui ho preso ispirazione).

Il mio codice parla praticamente da solo, e se ci riesci, sto cercando critiche per questo, quindi vorrei alcune critiche costruttive :)

Spero che il mio codice possa aiutarti con alcune idee!


0

Considera i componenti del modello osservatore / soggetto per i componenti di gioco e i componenti dell'interfaccia utente. Li colleghi insieme al momento della creazione / caricamento e poi li dimentichi. Se la salute del personaggio cambia, avvisa tutti gli osservatori, che possono fare ciò che vogliono con le informazioni.

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.