2009-03-25 12 views
5

Ho una domanda non necessariamente specifica per qualsiasi piattaforma o API ma più specifica per le interazioni nel codice tra animazioni.Animazione e logica

Un gioco è un buon esempio. Diciamo che il giocatore muore e c'è un'animazione di morte che deve finire prima che l'oggetto venga rimosso. Questo è tipico in molti casi in cui alcune animazioni devono finire prima di continuare con qualsiasi azione che normalmente seguirà. Come faresti a fare questo?

La mia domanda riguarda il controllo e la logica dell'animazione. Come progetteresti un sistema in grado di guidare l'animazione ma allo stesso tempo implementare un comportamento personalizzato?

Il problema che si verifica in genere è che la logica di gioco e i dati di animazione diventano codipendenti. Cioè, l'animazione deve richiamare il codice o in qualche modo contenere metadati per la durata delle sequenze di animazione. Quello che in genere è ancora più un problema, è quando un'animazione, che deve attivare qualche altro codice, dire dopo la 1.13s genera uno sprite personalizzato, questo tende a provocare una nidificazione profonda di codice e animazione. Una bomba con un timer sarebbe un esempio sia di logica che di animazione in cui entrambe le cose interagiscono, ma voglio mantenerle il più separate possibile.

Ma cosa faresti per mantenere l'animazione e codificare due cose separate?

Recentemente ho provato mgrammar e, sto pensando, una DSL potrebbe essere la strada da percorrere. Ciò consentirebbe all'animazione o all'animatore di esprimere certe cose in un modo presumibilmente sicuro che andrebbe poi nella pipeline dei contenuti ...

+0

Vorrei rispondere a questa domanda, ma sto avendo difficoltà a capire ciò che si vuole separare - fare si vuole separare il codice per la logica generale del gioco e il codice per rendere graficamente il gioco? L'animazione – CiscoIPPhone

+0

è guidata da qualcosa, ma le animazioni sono complesse, a un certo punto devono essere richiamate nel codice. A voler minimizzare questo e disaccoppiare il più possibile per capire se c'è un set minimo di funzionalità che è necessario farlo funzionare. –

risposta

5

La soluzione dipende dal gameplay che stai cercando. Se il gameplay è guidato dal codice al 100%, l'animazione è guidata dallo stato dell'entità (animazione guidata dallo stato). Se è basato su grafica/animazione, la lunghezza dell'animico determina per quanto tempo l'entità si trova in quello stato (stato animato dall'animazione).

Quest'ultimo è in genere più flessibile in un ambiente di produzione commerciale, in quanto il progettista può semplicemente dire "abbiamo bisogno che quell'anime della morte sia più breve" e venga negoziato. Ma quando si hanno in mente regole ben precise o un sistema simulato come la fisica, può essere preferibile l'animazione guidata dallo stato. Non esiste una soluzione ortogonale al 100% e pulita per il caso generale.

Una cosa che aiuta a tenerlo da ottenere troppo disordinato è quello di considerare di gioco modelli di intelligenza artificiale:

gioco AI è generalmente implementato come una forma di macchina a stati finiti, forse più macchine statali o strati in qualche modo (la divisione più comune è un formato di scripting di alto livello con azioni/transizioni di basso livello).

Al livello basso puoi dire cose come "nello stato hitreact, riprodurre il mio animatore hitreact fino al termine, quindi scoprire dalla logica di alto livello da quale stato continuare". Al livello elevato ci sono molti modi per definire la logica, ma i semplici cicli ripetuti come "approccio/attacco/ritiro" sono un buon modo per iniziare.

Questo aiuta a mantenere i due tipi di comportamenti - le attività pianificate e le reazioni a nuovi eventi - dall'essere troppo mescolati. Anche in questo caso, non sempre funziona in questo modo, per gli stessi motivi per cui a volte si desidera che il codice guidi i dati o viceversa.Ma questa è l'intelligenza artificiale per te. Nessuna soluzione generale qui!

0

Credo di non vedere il problema.

Se si dispone di un certo numero di callback, è possibile capire perché la cosa potrebbe essere difficile da seguire, ma se si dispone di una sola chiamata per tutti gli eventi di animazione e una funzione di aggiornamento che avvia le animazioni, è piuttosto semplice seguire il codice.

Quindi cosa ottieni separandoli?

+0

A me sembra sbagliato che l'animazione si intrecci con il codice. Voglio mantenere queste cose completamente separate per rendere i dati di animazione resuable e modulari. Potrebbe sembrare strano ma una volta entrato nella parte di codifica è davvero evidente che si tratta di un problema. –

+0

Scrivo questo tipo di codice abbastanza spesso. Sembra che tu stia chiedendo come puoi disaccoppiare un sistema basato sui dati dai dati che lo guidano. Non penso che tu possa Penso che il tuo problema sia il nesting, ma puoi refactoring il tuo codice per rimuoverlo. Se inserisci il codice con il tuo problema, lo rifatterò. – BigSandwich

+0

Non c'è alcun codice da pubblicare, ma sto tentando in ogni momento di separare i dati da un sistema basato sui dati. Come dici tu, potrebbe non essere possibile, ma io sono ottimista, non voglio fare due cose. –

0

Penso che dovresti separare il rendering dalla logica di gioco.

Di avere almeno due diversi tipi di oggetti:

  • un soggetto che detiene i dati dell'unità (punti ferita, posizione, velocità, resistenza, ecc) e logica (come dovrebbe muoversi, cosa succede se esaurisce i punti ferita, ...).
  • La sua rappresentazione, cioè lo sprite, i colori, le particelle attaccate ad esso, i suoni, qualsiasi cosa. La rappresentazione può accedere ai dati dell'entità, quindi conosce la posizione, i punti ferita, ecc.
  • Forse un controller se l'entità può essere controllata direttamente da un essere umano o da un AI (come una macchina in una simulazione di un'auto).

Sì, sembra l'architettura Model-View-Controller. Ci sono molte risorse su questo, vedere this article from deWiTTERS o The Guerrilla Guide to Game Code di Jorrit Rouwé, per esempi specifici del gioco.

Informazioni sui problemi di animazione: per un'unità di morte, quando il Modello aggiorna la propria entità e calcola che non ha più punti ferita, potrebbe impostare un flag per dire che è morto e rimuovere l'entità dal gioco (e dalla memoria). Successivamente, quando la vista si aggiorna, legge il flag e avvia l'animazione morente. Ma può essere difficile decidere dove archiviare questo flag poiché l'oggetto entità dovrebbe scomparire.

C'è un modo migliore nel mio umile IMHO. Quando la tua entità muore, puoi inviare un evento a tutti gli ascoltatori registrati all'unità UnitDiedEvent che appartiene a questa specifica entità, quindi rimuovere l'entità dal gioco. L'oggetto di rappresentazione entità sta ascoltando quell'evento e il suo gestore avvia l'animazione morente. Quando l'animazione è finita, la rappresentazione dell'entità può finalmente essere rimossa.

Il observer design pattern può essere utile, qui.

+0

Questo risolve il problema della morte ma non il generico. Per es. Gioca alla barra della spada, quando l'animazione raggiunge il riquadro 25, attiva la scatola dei danni della spada. Vuoi che l'animazione continui. Un callback è davvero l'unico buon modo per farlo. – BigSandwich

+0

Inoltre, chi dice che vuoi liberarti di lui quando muore? Forse voglio un mucchio di ragdoll da calciare. Forse si trasforma in uno zombi e si rialza. Stai facendo molte ipotesi. – BigSandwich

+0

Suppongo che aggiungendo altri eventi e gestori di eventi farebbe il trucco. I gestori di eventi possono anche modificare o creare nuovi oggetti (ad esempio una nuova animazione sul frame 25). – Splo

0

Per un paio di partite che ho fatto per risolvere questo problema ho creato due classi di animazione

asyncAnimation - Per il fuoco e dimenticare di tipo animazioni

syncAnimation - Se volevo aspettare per l'animazione di risolvere prima di restituire il controllo

come giochi di solito hanno un ciclo principale sembrava qualcosa di simile C# stile di codice pseudo

while(isRunning) 
{ 

    renderStaticItems(); 
    renderAsyncAnimations(); 

    if (list_SyncAnimations.Count() > 0) 
    { 
     syncAnimation = list_SyncAnimations.First(); 
     syncAnimation.render(); 

     if (syncAnimation.hasFinished()) 
     { 
      list_SyncAnimations.removeAt(0); 
      // May want some logic here saying if 
      // the sync animation was 'player dying' update some variable 
      // so that we know the animation has ended and the 
      // game can prompt for the 'try again' screen 
     } 

    } 
    else 
    { 
     renderInput(); 
     handleOtherLogic(); // Like is player dead, add sync animation if required. 
    } 
} 

Quindi, ciò che fa il codice è mantenere un elenco di animazioni di sincronizzazione che devono essere risolti prima di continuare con il gioco - Se devi aspettare diverse animazioni, impilarle semplicemente.

Inoltre, potrebbe essere una buona idea esaminare il modello di comando o fornire una richiamata per quando l'animazione di sincronizzazione ha finito di gestire la logica - è davvero come lo si vuole fare.

Per quanto riguarda il vostro "Spawn al sec 1.13" forse la classe SyncAnimation dovrebbe avere un metodo .OnUpdate override(), che può fare un po 'la logica personalizzata (o chiamare uno script)

Dipende quali possano essere le vostre esigenze .

+0

Quindi, invece di una richiamata quando arrivi al frame 25, devi eseguire il polling dell'animazione con una funzione di aggiornamento? Come va meglio? Inoltre, dove conserverai i 1,13 secondi? Se è nell'animazione, tutto ciò che hai fatto è spostare le cose. – BigSandwich

+0

I giochi hanno comunque una funzione di polling - e sì, si stanno muovendo le cose. La logica deve andare da qualche parte, metterla dove ha più senso per te. – JSmyth

0

L'animazione può richiamare una funzione di callback fornita o inviare nuovamente un evento generico al codice. Non ha bisogno di qualcosa di più, che mantiene tutta la logica nel codice. Basta iniettare il callback o connettere l'evento quando viene creata l'animazione.

1

Il tuo nemico deve avere più stati. Vivi e morti non sono abbastanza. Vivo, morire e morto potrebbe essere. Il vostro nemico ciclo di elaborazione dovrebbe controllare il suo stato ed eseguire operazioni diverse:

if(state == alive and hp == 0) 
{ 
    state = dying 
    currentanimation = animation(enemy_death) 
    currentanimation.play() 
} 
elseif(state == dying and currentanimation.isPlaying == true) 
{ 
    // do nothing until the animation is finished. 
    // more efficiently implemented as a callback. 
} 
elseif(state == dying and currentanimation.isPlaying == false) 
{ 
    state = dead 
    // at this point either the game engine cleans up the object, or the object deletes itself. 
} 
0

cerco il più possibile per mantenere i callback di animazioni per bambini. Le animazioni dovrebbero indicare che sono complete, le azioni intraprese su un completamento di animazioni dovrebbero essere chiamate dal livello di controller dell'applicazione.

In Actionscript questa è la bellezza dell'evento di dispacciamento/ascolto - L'oggetto controller può creare la mira e quindi assegnare un gestore per un evento che l'animazione invia quando è completo.

Ho usato lo schema per diverse cose nei progetti Flash e aiuta a mantenere il codice indipendente molto meglio dei callback.

Soprattutto se si scrivono oggetti evento personalizzati che estendono l'evento per trasportare il tipo di informazioni necessarie. come MouseEvent che porta localX, localY, stageX e stageY. Uso una personalizzazione che ho chiamato NumberEvent per trasmettere qualsiasi tipo di informazione numerica intorno alle mie applicazioni.

in oggetto controler ActionScript:

var animObj:AwsomeAnim = AwsomeAnim(); 
animObj.start(); 
animObj.addEventListener(AwsomeAnim.COPLETE,_onAnimFinish); 

function _onAnimFinish():void 
{ 
    // actions to take when animation is complete here 
} 

in JavaScript in cui non esistono eventi personalizzati. Ho solo una variabile booleana nell'oggetto dell'animazione e la controllo su un timer dal controller.

in javascript oggetto controller:

var animObj = new animObj();// among other things must set this.isComplete = false 
animObj.start(); 

function checkAnimComplete() 
{ 
    if(animObj.isComplete == true) 
    { 
     animCompleteActions(); 

    }else{ 
     setTimeout(checkAnimComplete,300); 
    } 
} 
checkAnimComplete(); 


function animCompleteActions() 
{ 
    // anim complete actions chere 
}