Molti giochi usano motori fisici per guidare il modo in cui le cose si muovono e reagiscono. L'utilizzo di un motore fisico può aggiungere immersione, divertimento per gli occhi e, soprattutto, un gameplay emergente, ma può anche, se usato in modo errato, portare a risultati non realistici o problemi di gioco. In questo post, spiegherò come identificare e risolvere i problemi comuni visti nei giochi di oggi.
Esamineremo e risolveremo questi problemi come visto in Unity, concentrandoci sul motore fisico 3D integrato Nvidia PhysX, ma i principi fondamentali possono essere applicati a qualsiasi altra piattaforma e motore fisico.
Nota: puoi trovare un'ampia selezione di modelli 3D per iniziare su Envato Market.
Questa demo mostra la maggior parte degli errori citati in questo articolo sia in uno stato errato o rotto, e in uno stato fisso:
Questa è una scena semplice con una palla che colpisce in una pila di barili. La versione alternativa Scala errata (10x) mostra come la scena cambia alla scala 10x (vedi i segni di scala sul pavimento della demo). Nota come sembra essere al rallentatore, ma questo effetto è semplicemente causato dalla scala della scena.
Questo mostra un semplice gioco a scorrimento laterale che funziona come previsto. L'alternativa "cattiva"; Rigidbody & Character Controller Insieme mostra lo stesso gioco, ma con un componente Rigidbody collegato al personaggio. Nota come il Rigidbody rompe il comportamento del Character Controller.
I barili vengono sparati dal lato della scena e vengono lanciati in aria. Quando arrivano schiantandosi, rimbalzano da terra e si muovono in modi semplici ma maestosi. La versione spezzata mostra la stessa scena senza sobbalzi e sembra molto più noiosa in confronto.
In questo scenario, una palla pesante viene spinta su una rampa usando Rigidbody.AddForce ()
. Nel secondo scenario, invece di usare Rigidbody.AddForce ()
per spostare la palla sulla rampa, transform.position
viene usato. Il risultato dell'uso transform.position
è che la palla rotola giù, a causa del fatto che il corpo rigido non sta prendendo in considerazione correttamente il cambio di velocità del corpo rigido, ma poi la modifica della posizione fa sì che la palla si agiti su e giù per la rampa.
Nella maggior parte dei giochi, i giocatori presumono che la scala del mondo sia riconducibile alla scala della Terra. Si aspetterebbero, per esempio, che un nemico che cade da una torre di avvistamento cada allo stesso ritmo che percepiresti sulla Terra. Se il nemico cade troppo lentamente o troppo velocemente, può sminuire l'immersione, in particolare se il nemico è di dimensioni umane!
Nvidia PhysX in Unity è impostato per utilizzare un'unità per metro. Puoi usare questo tratto per verificare se la scala dei tuoi oggetti è corretta semplicemente aggiungendo un cubo primitivo Unity. Il cubo primitivo è esattamente un metro cubo. Se noti, ad esempio, che un barile di petrolio nella tua scena è 2x più grande del cubo, significa che il tuo barile di petrolio è alto due metri (6,56 piedi)!
Fissare la scala è facile come ridimensionare ogni oggetto nella scena. Basta selezionare tutti gli oggetti nella scena e utilizzare il Scala strumento per renderli più grandi o più piccoli. Se noti che i tuoi oggetti si muovono troppo velocemente, ingrandisci gli oggetti. Se noti il contrario - che gli oggetti si muovono troppo lentamente - devi ridimensionare gli oggetti.
È possibile ridimensionare gli oggetti con maggiore precisione raggruppandoli in un oggetto nullo e ridimensionando tale oggetto. Ad esempio, impostando la scala dell'oggetto genitore su 1.2
su ogni asse aumenterà la dimensione di ogni oggetto all'interno dell'oggetto del 20%. È inoltre possibile ridimensionare gli oggetti in incrementi utilizzando lo strumento ridimensionamento tenendo premuto Ctrl-LMB (Windows) o Cmd-LMB (OS X).
Ho visto questo accadere un paio di volte, e in realtà ha senso quante volte questo si verifica. Lo sviluppatore presume che sia necessario un Character Controller per controllare il proprio avatar, ma vogliono che l'avatar sia influenzato dalla gravità e da altri oggetti nell'ambiente.
Il problema è che a Controller di caratteri è progettato per controlli più classici, come quelli che si trovano in genere in un platformer o sparatutto in prima persona. UN Corpo rigido è semplicemente un oggetto non deformabile che è influenzato dalla gravità e da altre forze fisiche (come altri oggetti che si scontrano con esso). Questi sono due componenti molto separati, con diversi usi previsti.
Scegli solo un controller di caratteri quando vuoi controllare il modo in cui il giocatore si muove. D'altra parte, se vuoi che il tuo personaggio sia guidato dal motore fisico, usa un Rigidbody. Aggiungendo un Rigidbody a un personaggio, probabilmente vorrai limitare la rotazione in modo che il giocatore non si rovesci.
A differenza di un controller di caratteri, non è una buona pratica impostare la posizione o la rotazione di un corpo rigido o scalare costantemente un oggetto di corpo rigido (per il controllo del giocatore e così via). Invece, dovresti usare i metodi AddForce () e AddTorque () trovati nella classe Rigidbody. Va bene impostare direttamente la posizione e la rotazione di un Rigidbody se, per esempio, si sta generando l'oggetto per la prima volta o si ripristina la scena. In quella situazione andrà bene, a patto che il Rigidbody non stia incrociando altri oggetti.
Ciò è importante perché, quando un corpo rigido viene spostato in una posizione esatta o in uno stato di rotazione, potrebbe passare attraverso un oggetto. Il motore fisico deve quindi correggere questo problema, e il più delle volte il motore fisico non funziona contemporaneamente Aggiornare()
messaggio Il risultato finale è un comportamento nervoso ogni volta che c'è un'intersezione, ed è possibile che il corpo rigido possa passare interamente attraverso gli oggetti.
Un altro effetto collaterale negativo che può verificarsi quando, per esempio, lo spostamento di un corpo rigido lungo un asse per il movimento del giocatore è che internamente il corpo rigido viene simulato e quindi applica la sua posizione. L'aggiornamento della posizione sposta quindi il corpo rigido senza tener conto del cambio di velocità e così via. Se il corpo rigido sta tornando indietro di una pendenza, sposterà il corpo rigido all'indietro mentre il codice che modifica la posizione sta spostando il corpo rigido indietro sulla pendenza.
Diciamo che stai sviluppando un gioco di golf. C'è un problema con il modo in cui la pallina da golf non smette di ruotare, e in qualche modo riesce a continuare a rotolare all'infinito fintanto che non c'è nessun tipo di buca o fossato sul suo cammino. Il motivo per cui ciò accade è perché nella vita reale, la palla viene rallentata dall'erba che investe (tra le altre cose), poiché deve spingere le minuscole lame d'erba verso il basso, l'erba è essenzialmente come una rampa costante. Questo è chiamato resistenza al rotolamento. Unity non può simulare questo comportamento in modo accurato, quindi devono essere utilizzate forze di arresto artificiali.
In Unity la forza migliore da usare per fermare un oggetto dal rotolare per sempre è "resistenza angolare". Cambiare la resistenza angolare sulla pallina da golf è il modo per risolvere questo problema. Il valore esatto dipende molto dal comportamento che stai cercando, tuttavia potresti notare che in alcuni casi un valore di resistenza angolare di 1,0 potrebbe non essere sufficiente.
Quasi tutti gli oggetti del mondo rimbalzano dopo un impatto. Il materiale fisico interno predefinito di Unity non ha alcun rimbalzo. Il significato di ogni oggetto non rimbalzerà a meno che non si sostituisca il materiale fisico predefinito o si applichi un materiale fisico agli oggetti nella scena con un valore di rimbalzo superiore a 0.
Uno dei modi migliori per risolvere questo problema è creare un tuo materiale fisico predefinito e assegnarlo nel file Responsabile della fisica trovato cliccando Modifica> Impostazioni progetto> Fisica.
La maggior parte dei motori di fisica ha un qualche tipo di parametro che stabilisce quanto due oggetti possono essere compenetrati o intersecanti fino a che non vengono allontanati l'uno dall'altro. Questo parametro è chiamato Min Penetration For Penalty in Unity. Di default questo valore è 0,01 (metri), il che significa che, per impostazione predefinita, gli oggetti possono essere intersecati fino a 1 centimetro (quasi 0,4 pollici) prima di essere spinti a parte.
Dovresti impostare Penetrazione minima per penalità ad un valore in cui è appena visibile che gli oggetti si intersecano. Impostare il valore su qualcosa di piccolo, ad esempio 0.0001
, può causare rigidi corpi rigidi.
Se non sei un programmatore, non devi preoccuparti del seguente scenario. Quando si scrive codice che sposta, ruota o ridimensiona i corpi rigidi, è importante tenerlo nel ciclo FixedUpdate. Scrivendo questo codice nel Aggiornare
loop potrebbe portare a risultati instabili, dal momento che Aggiornare
la funzione può essere chiamata a 1000 Hz, mentre il motore fisico e il FixedUpdate
le funzioni sono chiamate ciascuna a 50 Hz per impostazione predefinita.
È possibile modificare la frequenza su passaggi di fisica modificando il parametro Fixato Timestamp, trovato in Modifica> Impostazioni progetto> Ora. Il valore determina quanto tempo è atteso, in secondi, tra ogni aggiornamento o passaggio di fisica. È possibile calcolare la frequenza in Hertz dividendo 1 per il valore (ad esempio, una attesa di 0,01 secondi significa 1 / 0,01 = 100 Hz). Più frequenti sono i passaggi, più accurata e stabile sarà la simulazione. Tuttavia, l'impostazione di una frequenza superiore a quella gestibile dalla CPU produrrà una simulazione molto instabile. Cerca di mantenere la frequenza di aggiornamento fisso tra 30 Hz e 100 Hz.
Mentre lavoravo su un muro di mattoni distruttibile, mi sono imbattuto in un problema causato dall'istanziazione dei mattoni dopo che un pezzo del muro era stato distrutto. Ho risolto questo problema inserendo il codice problematico in una Coroutine e ponendo la seguente riga prima di Distruggere l'oggetto:
// Attendi un frame yield return null; // resa C #; // UnityScript
Nell'attesa di un frame, si garantisce che la logica sia stata sincronizzata in tempo di aggiornamento anziché in tempo di aggiornamento. Ciò sembra significare che la funzione Destroy viene eseguita in sincronia con il ciclo di aggiornamento.
Il pacchetto Physics Materials, che fa parte delle Unity Standard Assets, è in realtà quasi completamente inutile. Ci sono cinque materiali fisici contenuti nel pacchetto, e tutti sono irrealistici in qualche modo.
Ogni materiale ha identico attrito statico e dinamico. Nel mondo reale, gli oggetti che stanno fermi hanno leggermente più attrito quando si muovono. Il coefficiente di attrito del materiale in gomma è 1.0
, che non è simile a qualsiasi gomma trovata nel mondo reale. E se ciò non sembra abbastanza sciocco, ogni materiale ha 0 "elasticità" (escluso il materiale "Bouncy"). Tutto ciò significa che i materiali non sono nemmeno una rappresentazione ravvicinata della loro controparte reale.
È meglio creare i propri materiali di fisica quando necessario. Ci sono molti siti web in giro che condividono le proprietà fisiche dei materiali, i più importanti sono l'attrito dinamico, l'attrito statico e la restituzione o rigidità.
Pochi problemi di fisica sono in realtà difficili da risolvere. Se c'è qualche tipo di bug relativo alla fisica che sembra difficile da rintracciare, prova a rallentare il tempo per vedere cosa sta succedendo. Se noti che il problema inizia intorno a una determinata riga di codice, puoi utilizzare Debug.Break per mettere in pausa l'editor e controllare cosa sta succedendo. Sentiti libero di commentare qui se hai qualche domanda o hai bisogno di aiuto.