Domanda

Questo esempio è stato utilizzato in un'altra domanda per illustrare come co-routine può essere utilizzato per scene di script in un videogioco:

bob.walkto(jane)
bob.lookat(jane)
bob.say("How are you?")
wait(2)
jane.say("Fine")
...

Ogni rendimenti funzionali al motore principale che fa animazione, orario ecc prima di riprendere la coroutine. Una possibile alternativa di coroutine sarebbe una coda di eventi invece di codice, ma poi si deve implementare logica di controllo e passanti come eventi. Ci sono altre alternative alla co-routine che possono essere utilizzati per implementare questo tipo di funzionalità? Ho visto i callback di cui alcuni articoli, ma non sono sicuro di come il codice sarà.

È stato utile?

Soluzione

coroutine sono particolarmente adatti per questo dal momento che si arriva a mantenere tutte le variabili di stato locali senza problemi. Cioè senza dover manualmente conservarlo in un contesto da qualche parte.

Ma non vedo un sistema di eventi come alternativa. Più come un integratore che è molto probabile che vogliono avere oltre ad un sistema di scripting coroutine-based.

Esempio (in qualche modo coerente C ++):

Si hanno attuato un comportamento utilizzando coroutine lungo queste linee:

class EnterHouse : public NPCBehavior
{
    EnterHouse(House theHouse) { _theHouse = theHouse; }
    void Begin() { _theHouse.AddNPC(NPC()); }
    void Update()
    {
        NPC().WalkTo(_theHouse.GetDoor().Position());
        NPC().FaceObject(_theHouse.GetDoor());
        NPC().PlayAnimation(NPC().Animations().Get(eAnimKnockOnDoor));
        Sleep(1.0f);       
        NPC().OpenDoor(_theHouse.GetDoor());
    }
    void End() { /* Nothing */ }

    private House _theHouse;
}

Immaginate che i metodi sui NPC saranno essi stessi creare oggetti NPCBehavior, spingerli su una sorta di pila comportamento e ritorno dalla chiamata quando quei comportamenti completa.

La chiamata Sleep(1.0f) produrrà per il programmatore di script e consentire ad altri script da eseguire. Il WalkTo, FaceObject, PlayAnimation e OpenDoor anche chiamare Sleep di rendimento. Sia sulla base di una durata dell'animazione noto, svegliarsi periodicamente per vedere se il Pathfinder e il sistema di locomozione sono a piedi fatto o qualsiasi altra cosa.

Che cosa succede se la NPC incontra una situazione che avrà a che fare con sulla strada per la porta? Se non si desidera avere a verificare la presenza di tutti questi eventi ovunque nel codice coroutine-based. Avere un integratore di sistema di eventi i coroutine renderà questo facile:

Un cestino in rovescia : Il cestino può trasmettere un evento a tutti gli NPC nelle vicinanze. L'oggetto NPC decide di spingere un nuovo oggetto comportamento sul suo stack per andare a risolvere il problema. Il comportamento WalkTo è impegnato in una chiamata Sleep qualche cedimento, ma ora il comportamento FixTrashcan è in funzione a causa della manifestazione. Quando FixTrashcan completa il comportamento WalkTo si sveglierà dal Sleep e mai sapere circa l'incidente cestino. Ma sarà ancora nel suo cammino verso la porta, e sotto ci sono ancora in esecuzione EnterHouse.

Un'esplosione accade : L'esplosione trasmette un evento, proprio come il cestino, ma questa volta l'oggetto NPC decide di ripristinare in esecuzione comportamenti e spingere un comportamento FleeInPanic. Egli non tornerà a EnterHouse.

spero di vedere che cosa intendo per avere eventi e coroutine vivono insieme in un sistema di intelligenza artificiale. È possibile utilizzare coroutine per mantenere stato locale, pur cedendo a vostro scheduler copione, ed è possibile utilizzare gli eventi per gestire le interruzioni e mantenere la logica di trattare con loro centralizzato senza inquinare i vostri comportamenti.

Se non avete già visto questo articolo da Thomas Tong su come implementare coroutine singolo thread in C / C ++ posso vivamente.

Si utilizza solo il più piccolo frammento di linea di montaggio (una singola istruzione) per salvare lo stack pointer, e il codice è facilmente portabile su tutta una serie di piattaforme. L'ho avuto in esecuzione su Wintel, Xbox 360, PS3 e Wii.

Un'altra cosa bella di setup scheduler / script è che diventa banale per morire di fame fuori dallo schermo o lontano AI caratteri / oggetti script se è necessario le risorse per qualcos'altro. Appena coppia con un sistema di priorità in voi di pianificazione e siete a posto.

Altri suggerimenti

non ha menzionato che lingua si sta utilizzando, così ho intenzione di scrivere questo in Lua con orientamento agli oggetti forniti dal borghese - https://github.com/kikito/middleclass (disclaimer: io sono creatore classe media)

Un'altra opzione sarebbe quella dividendo i tuoi filmati in "liste di azioni". Questo probabilmente si fondono meglio con il tuo codice, se si dispone già di un ciclo di gioco che richiama un metodo di 'aggiornamento' su elenchi di oggetti.

In questo modo:

helloJane = CutScene:new(
  WalkAction:new(bob, jane),
  LookAction:new(bob, jane),
  SayAction:new(bob, "How are you?"),
  WaitAction:new(2),
  SayAction:new(jane, "Fine")
)

Azioni avrebbe un attributo status con tre possibili valori: 'new', 'running', 'finished'. Tutte le "classi d'azione" la sarebbero sottoclassi di Action, che definisca start e stop metodi, così come inizializzare lo stato di 'new' per impostazione predefinita. Ci sarebbe anche un metodo update default che genera un errore

Action = class('Action')

function Action:initialize() self.status = 'new' end

function Action:stop() self.status = 'finished' end

function Action:start() self.status = 'running' end

function Action:update(dt)
  error('You must re-define update on the subclasses of Action')
end

Le sottoclassi di azione può migliorare questi metodi, e implementare update. Ad esempio, ecco WaitAction:

WaitAction = class('WaitAction', Action) -- subclass of Action

function WaitAction:start()
  Action.start(self) -- invoke the superclass implementation of start
  self.startTime = os.getTime() -- or whatever you use to get the time
end

function WaitAction:update(dt)
  if os.getTime() - self.startTime >= 2 then
    self:stop() -- use the superclass implementation of stop
  end
end

La parte implementazione manca solo è cutscene. Un filmato avrà principalmente tre cose: * Un elenco di azioni da eseguire * Un riferimento all'azione corrente, o indice di tale azione nell'elenco azione * Un metodo di aggiornamento come il seguente:

function CutScene:update(dt)
  local currentAction = self:getCurrentAction()
  if currentAction then
    currentAction:update(dt)
    if currentAction.status == 'finished' then
      self:moveToNextAction()
      -- more refinements can be added here, for example detecting the end of actions
    end
  end
end

Con questa struttura, l'unica cosa che serve è il vostro ciclo di gioco chiamando helloJane:update(dt) su ogni iterazione del ciclo. E si elimina la necessità di coroutine.

callback (C # pseudocodice in stile):

bob.walkto(jane, () => {
    bob.lookat(jane), () => {
        bob.say.....
    })
})

Non è sicuramente il modo più conveniente.

Un approccio diverso è Futures (noto anche come promesse):

futureChain = bob.walkto(jane)
  .whenDone(bob.lookAt(jane))
  .whenDone(...)
  .after(2 seconds, jane.Say("fine"));

futureChain.run();

Una lingua interessante da guardare è E - ha il supporto integrato per i future, con una sintassi più bello di sopra.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top