Gestione degli input nella progettazione basata su componenti


12

So che questa domanda è stata posta più volte, ma non sono ancora sicuro di come implementare la gestione degli input in un motore basato su componenti.

Il design basato sui componenti che ho usato era basato sulla serie di blog di T = Machine e su Artemis in cui le Entità sono solo ID.

Ci sono tre idee principali che ho nell'implementazione della gestione degli input:

  1. Il componente di input conterrà eventi di suo interesse. Il sistema di input tradurrà gli eventi chiave e del mouse in eventi di gioco e passerà in rassegna le entità con il componente di input e se sono interessati all'evento verrà intrapresa un'azione appropriata dal sistema di input. Questa azione verrebbe codificata sul sistema di input.
  2. Nessun componente di input. Registrereste entità con eventi specifici nel sistema di input. Il sistema di input invierebbe quindi messaggi (con ID entità e tipo di evento) ad altri sistemi in modo che questi possano intraprendere le azioni appropriate. O come nel primo caso, le azioni sarebbero codificate nel sistema di input.
  3. Simile al primo metodo, ma invece di codificare a fondo l'azione sul sistema di input, il componente conterrebbe una mappa di eventi per funzioni (cioè std::map<std::function>) che verrebbero chiamati dal sistema di input. Ciò ha l'effetto aggiuntivo di poter accoppiare lo stesso evento ad azioni diverse.

Consiglieresti uno dei metodi sopra indicati o hai qualche suggerimento che mi aiuterebbe a implementare un sistema di gestione dell'input flessibile? Inoltre, non ho ancora familiarità con il multi-threading, ma qualsiasi suggerimento che renderebbe l'implementazione intuitiva per i thread è il benvenuto.

Nota: un ulteriore requisito che mi piacerebbe che l'implementazione soddisfacesse è che sarei in grado di trasmettere lo stesso input a molte entità, come ad esempio spostare un'entità videocamera e il giocatore contemporaneamente.


2
Di solito (quando la telecamera segue il lettore) non si desidera ricevere input nella telecamera, invece si fa controllare la posizione del player e seguirla.
Luca B.

1
Non importa davvero concettualmente se la telecamera segue il lettore o "se stessa". Tuttavia, non sono sicuro di come il tuo suggerimento verrebbe implementato in una progettazione basata su componenti senza infrangere i principi di progettazione.
Grieverheart,

1
@Luke B .: Dopo averci pensato, vedo che potresti anche rendere la videocamera come una classe separata, portando un puntatore su un'entità da seguire.
Grieverheart,

Risposte:


8

Penso che, proprio come la mia risposta per quanto riguarda i materiali in un sistema di componenti , stai riscontrando un problema in cui stai cercando di inserire tutto in un "componente". Non è necessario farlo e nel farlo probabilmente stai creando un'interfaccia davvero ingombrante cercando di inserire un gruppo di pioli quadrati in fori rotondi.

Sembra che tu abbia già un sistema che gestisce l'acquisizione di input dal lettore. Opterei per un approccio che traduca quell'input in azioni ("sposta avanti" o "sposta indietro") o in eventi e invia quelli alle parti interessate. In passato, ho annullato componenti da registrare se stessi per questi eventi, preferendo un approccio in cui il sistema di livello superiore selezionato in modo esplicito il "soggetto controllato." Ma potrebbe funzionare diversamente se preferisci, specialmente se riutilizzerai gli stessi messaggi per intraprendere azioni che non sono state stimolate direttamente dall'input.

Tuttavia, non suggerirei necessariamente l'implementazione del comportamento di seguito della videocamera facendo sì che sia l'entità della videocamera sia l'entità del giocatore rispondano al messaggio "sposta avanti" (eccetera). Questo crea una connessione estremamente rigida tra i due oggetti che probabilmente non si sentirà bene con il giocatore, e rende anche un po 'più complicato gestire cose come avere la videocamera in orbita quando il giocatore ruota a sinistra oa destra: hai un'entità rispondendo a "ruota a sinistra" assumendo che sia schiavo del giocatore, ma ciò significa che non può rispondere correttamente se non viene mai schiavizzato ... a meno che tu non introduca quel concetto come un certo stato che puoi controllare. E se hai intenzione di farlo, puoi anche implementare un sistema adeguato per schiavizzare due oggetti fisici insieme, completo di opportune modifiche elastiche e così via.

Per quanto riguarda il multi-threading, non vedo davvero la necessità di impiegarlo qui perché probabilmente causerebbe più complicazioni di quanto valga la pena, e hai a che fare con un problema intrinsecamente seriale quindi avresti solo bisogno di coinvolgere un sacco di thread primitive di sincronizzazione.


Ho pensato un po 'alla mia domanda e stavo per rispondere da solo. Sono anche giunto alla conclusione che dovrei essere migliore nel disaccoppiare la gestione degli input dal sistema EC, quindi è bello vedere una conferma di ciò. Il modo in cui ho pensato di farlo è l'uso dei segnali e la capacità di associare diverse entità a un tipo di evento. Ho anche deciso di disaccoppiare la fotocamera, anche se questo non è davvero necessario e averlo come entità sarebbe ugualmente praticabile. Penso che quando sei ancora un principiante con gli EC devi davvero pensare ai vantaggi di rendere qualcosa un componente o un'entità.
Grieverheart,

4

La mia esperienza potrebbe essere distorta ma in progetti multipiattaforma i dispositivi di input non sono direttamente esposti al sistema di entità.

I dispositivi di input sono gestiti da un sistema di livello inferiore che riceve gli eventi da tasti, pulsanti, assi, mouse, superfici tattili, accelerometri ...

Questi eventi vengono quindi inviati attraverso uno strato di generatori di intenzioni dipendenti dal contesto.

Ogni registro del generatore di stato modifica da componenti, entità e sistemi rilevanti per le sue funzioni.

Questi generatori inviano quindi messaggi / intenzioni per il routing al sistema di intenzione in cui le entità hanno un componente o direttamente ai componenti giusti.

In questo modo puoi semplicemente fare affidamento sul fatto che "sempre" abbia lo stesso input, ad esempio JUMP_INTENT (1), JUMP_INTENT (0), AIM_INTENT (1) ...

E "tutto" il lavoro di input dipendente dalla piattaforma sporca rimane al di fuori del sistema di entità.


Per quanto riguarda la videocamera, se si desidera spostarla all'interno del lettore, è possibile registrare il proprio componente di intenti e ascoltare quelli che si invieranno.

Altrimenti quando segue il giocatore non dovrebbe mai ascoltare gli ingressi destinati al giocatore. Dovrebbe ascoltare i cambiamenti di stato emessi dal giocatore (ENTITY_MOVED (trasformare)) ... e muoversi di conseguenza. Se si utilizza un sistema fisico, è anche possibile collegare la fotocamera al lettore utilizzando una delle varie articolazioni.


Coyote, grazie per la tua risposta. Ho anche letto il tuo altro post qui . La mia più grande preoccupazione non è come astrarre l'input. Ho già un costrutto di livello inferiore che gestisce le pressioni dei tasti e simili, e aggiungere un ulteriore livello di riferimento indiretto non sarebbe difficile. Il mio problema è nel gestire gli eventi generati, ad esempio, dal tuo sistema di intenti. Se ho capito bene, non hai alcun componente di input nel tuo metodo. Come fai a sapere quali entità necessitano di input e come le gestisci? Potresti dare alcuni esempi più concreti?
Grieverheart,

2

Quali vantaggi porta un InputComponent? Sicuramente è una prerogativa del comando di input decidere su quali entità sta eseguendo un'azione. L'esempio classico è quello di far saltare il giocatore. Invece di avere un InputComponent su ogni entità in ascolto degli eventi "Jump", perché non fare in modo che il comando jump cerchi l'entità contrassegnata come "player" ed esegua la logica necessaria stessa?

Action jump = () =>
{
    entities["player"].Transform.Velocity.Y += 5;
};

Un altro esempio, dall'OP:

Action moveRight = () =>
{
    foreach (var entity in entities.Tagged("player", "camera"))
        entity.Transform.Position.X += 5;
};
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.