Lezione 1 di The Time of Conundrum – Come sviluppare un controller in prima persona in VR a nausea ridotta

di Antony Vitillo, venerdì 4 marzo 2016 alle 16:10

Ciao a tutti e benvenuti al primo episodio di questa serie che parla di ciò che abbiamo imparato durante lo sviluppo della concept demo "The Time of Conundrum".

Oggi voglio parlare di uno dei più grandi problemi della realtà virtuale: il movimento. OK, lo so, state pensando che questo sia semplicemente un altro post riguardante il nostro sistema ImmotionRoom, ma no, non è un post che parla di quanto sia figo il nostro sistema, di quanto abbia le potenzialità per togliere ogni tipo di motion sickness dai movimenti del giocatore e tutte queste cose qui (che sono tutte vere, comunque ;) ). Oggi vi voglio parlare dei sistemi di input tradizionali perché, sì, il room-scale del Vive e il walk-scale di ImmotionRoom sono fighi, ma non tutti ce li hanno, quindi penso che in questo momento, noi, come sviluppatori, dovremmo pubblicare giochi che supportino almeno un duplice sistema di input, con uno che riguardi l’usare il gamepad o un qualsiasi altro strumento di input classico, per fare in modo che i nostri giochi vengano utilizzati dal maggior numero di giocatori possibile.

undefined

Ma questo ci porta ad un problema: gamepad e tastiere, con i loro controller in prima persona tradizionali, ci portano al cosiddetto motion sickness, a causa del noto problema della disparità tra ciò che vediamo con i nostri occhi e la propriocezione del nostro corpo, quindi non vanno affatto bene. La comunità di sviluppatori ha creato un sacco di alternative interessanti, dove il movimento viene effettuato utilizzando ad esempio sistemi di teletrasporto, ma, siamo sinceri, per la maggior parte dei giochi, questo tipo di movimento fa abbastanza schifo. Riuscite ad immagine uno sparatutto in prima persona con i movimenti ottenuti solo tramite il teletrasporto? Sarebbe strano…

Quindi, come possiamo risolvere questo problema? Onestamente non lo so come si possa risolvere, ma so come si può ridurre, con un metodo fantastico: il mio socio Gianni soffre molto di motion sickness, quindi non era in grado di giocare a The Time Of Conundrum per più di un minuto sui Gear VR con un controller FPS tradizionale… ma dopo aver applicato questo metodo, è stato in grado di rimanere anche 10 minuti all’interno del gioco! (Questa sembra una di quelle frasi che si possono leggere sui banner nei siti web che ti promettono di perdere peso in un mese utilizzando uno strano metodo!)

undefined

A dirla tutta, questo NON è nemmeno il NOSTRO metodo. L’ho letto su Gamasutra e su altri siti, ma mi sono accorto che non è ancora diffuso, quindi mi sto adoperando per diffondere la sua conoscenza, perché lo abbiamo provato e abbiamo visto che è un sistema valido. Il segreto è: TOGLIETE TUTTE LE ACCELERAZIONI E LE DECELERAZIONI. Cioè, il vostro giocatore non può andare più velocemente o più lentamente, non ha più una fisica realistica: va immediatamente da 0 ad una velocità costante quando l’utente vuole andare avanti e decelera immediatamente da questa velocità a 0 quando l’utente non fornisce più alcun input. Lo so, lo so, non è un grande modo per fornire un controllo realistico, ma… pensate veramente che il teletrasporto sia meglio?

Questo metodo funziona perché il corpo umano genera il malessere quando percepisce un’accelerazione quindi, rimuovendole tutte, il gioco diventa più confortevole.

Ma… come le possiamo rimuovere? Bene, sporchiamoci un po’ le mani e scaviamo nel codice dell’OVRPlayerController in Unity (qui sto modificando il codice delle Oculus Utilities v0.1.3).

Innanzitutto rimuoviamo tutti i riferimenti alla parola accelerazione.

Alla riga 34, sostituiamo questo:

31
32
33
34
 /// <summary>
 /// The rate acceleration during movement.
 /// </summary>
 public float Acceleration = 0.1f;

con questo:

31
32
33
34
 /// <summary>
 /// The rate ConstantSpeed during movement.
 /// </summary>
 public float ConstantSpeed = 0.3f;

e questo (riga 85):

85
 private Vector3 MoveThrottle = Vector3.zero;

con:

85
 private Vector3 MoveSpeed = Vector3.zero;

Poi sostituiamo tutte le occorrenze delle parole Acceleration and MoveThrottle di conseguenza. Adesso non consideriamo più accelerazioni, ma solo velocità.

Ovviamente non è sufficiente un semplice cambio di nomi. Dobbiamo far sparire tutti i cambi di velocità presenti all’interno del codice. Quindi, andiamo alle righe 180-182 e commentiamo le seguenti righe:

180
181
182
 MoveSpeed.x /= motorDamp;
 MoveSpeed.y = (MoveSpeed.y > 0.0f) ? (MoveSpeed.y / motorDamp) : MoveSpeed.y;
 MoveSpeed.z /= motorDamp;

per rimuovere l’attrito (che implica una variazione di velocità). Similarmente, commentiamo via le linee 197-201, per rimuovere il cambio di velocità dovuto alle asperità del terreno:

197
198
199
200
201
 if (Controller.isGrounded && MoveThrottle.y <= transform.lossyScale.y * 0.001f)
 {
	bumpUpOffset = Mathf.Max(Controller.stepOffset, new Vector3(moveDirection.x, 0, moveDirection.z).magnitude);
	moveDirection -= bumpUpOffset * Vector3.up;
 }

OK, abbiamo ucciso un po’ di realismo del nostro gioco. Ci sentiamo male, lo so. Ma dobbiamo andare avanti.

Prima della linea 216 (all’inizio del metodo UpdateMovement), aggiungiamo la seguente riga:

216
 MoveSpeed = Vector3.zero;

per inizializzare il movimento del giocatore a zero in ogni frame: in questo modo, se il giocatore non preme nessun tasto, starà fermo.

Commentiamo via la riga 251:

251
 MoveScale *= SimulationRate * Time.deltaTime;

perché anche questa riguarda l’accelerazione (anche se non si capisce subito). Poi alla linea 304, sostituiamo questo:

304
 moveInfluence = SimulationRate * Time.deltaTime * Acceleration * 0.1f * MoveScale * MoveScaleMultiplier;

con:

304
 moveInfluence = ConstantSpeed * 0.1f * MoveScale * MoveScaleMultiplier;

Essenzialmente, considerando che MoveScale varrà sempre 1 (se il controller è a terra), avremo una moveInfluence che è proporzionale alla velocità costante e al delta time di esecuzione del gioco.

Et… voilà, abbiamo il nostro player controller a nausea-ridotta! Provatelo voi stessi e verificate se anche voi provate meno nausea! (e siate felici come una renna che suona la chitarra)!!!

undefined

 

Nel caso vi stiate domandando qualcosa sul movimento rotazionale… beh, a dir la verità non ho ancora trovato una soluzione a questo problema, ma posso dire tre cose:

1) Alla linea 327, sostituendo questo:

327
 euler.y += secondaryAxis.x * rotateInfluence;

con questo:

327
328
329
330
 if (secondaryAxis.x > 0.1f)
     euler.y += rotateInfluence;
 else if (secondaryAxis.x < -0.1f)
     euler.y -= rotateInfluence;

si fa in modo di essere sicuri che si sta ruotando sempre alla stessa velocità, indipendentemente da quanto venga spinto il thumbstick del gamepad. Questo aiuta un pochino ad avere una velocità di rotazione costante.

2) Un modo per ridurre la nausea da rotazione è quella di rendere la rotazione non continua, in modo da far ruotare il giocatore a scatti, con brevi piccole rotazioni intervallate da più lunghi intervalli in cui il giocatore resta fermo (lo stesso meccanismo fornito dal RotationRatchet della classe OVRPlayerController). Ho provato questo metodo e penso che sia vero che riduca la nausea, ma rende anche il player controller inutilizzabile per la maggior parte delle situazioni.

3) Se il giocatore non ruota utilizzando un gamepad, ma lo fa semplicemente con una sedia rotante, è molto meglio e il problema viene risolto… ma non puoi obbligare i tuoi giocatori a giocare così.

Una nota finale: ho fornito il codice precedente come esempio… sono quasi sicuro che da qualche parte sia nascosto un bug che farà prendere fuoco al vostro computer e che se un super programmatore di StackOverflow leggesse il codice, avrebbe un infarto… ma in qualche modo funziona… prendetelo come un punto di partenza per fare la vostra implementazione!

Spero che questo possa essere di aiuto per qualcuno… se avete suggerimenti, sentitevi liberi di farli nei commenti!

Se vi è piaciuto questo tutorial, considerate l’idea di supportarci in qualche modo, come dare un’occhiata ai video del nostro sistema ImmotionRoom, iscrivendovi alla nostra newsletter o condividendo questo articolo sui social network!

Vi auguro una felice giornata senza nausea!