Sono riuscito a realizzare ciò di cui avevo bisogno, principalmente con l'aiuto di questo post sul blog per il pezzo del puzzle scattante in superficie e ho avuto le mie idee per il movimento del giocatore e la telecamera.
Snapping Player sulla superficie di un oggetto
L'impostazione di base è composta da una sfera grande (il mondo) e una sfera più piccola (il giocatore) entrambe con collettori di sfere attaccati ad esse.
La maggior parte del lavoro svolto era nei seguenti due metodi:
private void UpdatePlayerTransform(Vector3 movementDirection)
{
RaycastHit hitInfo;
if (GetRaycastDownAtNewPosition(movementDirection, out hitInfo))
{
Quaternion targetRotation = Quaternion.FromToRotation(Vector3.up, hitInfo.normal);
Quaternion finalRotation = Quaternion.RotateTowards(transform.rotation, targetRotation, float.PositiveInfinity);
transform.rotation = finalRotation;
transform.position = hitInfo.point + hitInfo.normal * .5f;
}
}
private bool GetRaycastDownAtNewPosition(Vector3 movementDirection, out RaycastHit hitInfo)
{
Vector3 newPosition = transform.position;
Ray ray = new Ray(transform.position + movementDirection * Speed, -transform.up);
if (Physics.Raycast(ray, out hitInfo, float.PositiveInfinity, WorldLayerMask))
{
return true;
}
return false;
}
Il Vector3 movementDirection
parametro è proprio come sembra, la direzione in cui stiamo andando a spostare il nostro giocatore in questo frame, e calcolare quel vettore, anche se è risultato relativamente semplice in questo esempio, è stato un po 'complicato da capire all'inizio. Ne parleremo più avanti, ma tieni presente che è un vettore normalizzato nella direzione in cui il giocatore sta spostando questo frame.
Facendo un passo avanti, la prima cosa che facciamo è controllare se un raggio, originato dall'ipotetica posizione futura diretta verso il vettore verso il basso dei giocatori (-transform.up), colpisce il mondo usando WorldLayerMask che è una proprietà pubblica LayerMask della sceneggiatura. Se vuoi collisioni più complesse o più livelli dovrai costruire la tua maschera di livello. Se il raycast colpisce con successo qualcosa, hitInfo viene utilizzato per recuperare il normale e il punto ferita per calcolare la nuova posizione e rotazione del giocatore che dovrebbe essere proprio sull'oggetto. La compensazione della posizione del giocatore può essere richiesta a seconda delle dimensioni e dell'origine dell'oggetto giocatore in questione.
Infine, questo è stato davvero testato e probabilmente funziona bene solo su oggetti semplici come le sfere. Come suggerisce il post sul blog su cui ho basato la mia soluzione, probabilmente vorrai eseguire più raycast e fare una media per la tua posizione e rotazione per ottenere una transizione molto più piacevole quando ti muovi su terreni più complessi. Potrebbero esserci anche altre insidie a cui non ho pensato a questo punto.
Macchina fotografica e movimento
Una volta che il giocatore si è attaccato alla superficie dell'oggetto, il compito successivo da affrontare era il movimento. Inizialmente avevo iniziato con i movimenti relativi al giocatore, ma ho iniziato a incorrere in problemi ai poli della sfera in cui le direzioni improvvisamente cambiarono, facendo sì che il mio giocatore cambiasse rapidamente direzione più e più volte non facendomi passare mai i poli. Quello che ho finito per fare è stato il movimento dei miei giocatori rispetto alla telecamera.
Ciò che ha funzionato bene per le mie esigenze è stato quello di avere una videocamera che seguisse rigorosamente il giocatore basandosi esclusivamente sulla posizione dei giocatori. Di conseguenza, anche se la telecamera stava tecnicamente ruotando, premendo su si spostava sempre il lettore verso la parte superiore dello schermo, verso il basso, e così via con sinistra e destra.
Per fare ciò, è stato eseguito quanto segue sulla telecamera in cui l'oggetto target era il giocatore:
private void FixedUpdate()
{
// Calculate and set camera position
Vector3 desiredPosition = this.target.TransformPoint(0, this.height, -this.distance);
this.transform.position = Vector3.Lerp(this.transform.position, desiredPosition, Time.deltaTime * this.damping);
// Calculate and set camera rotation
Quaternion desiredRotation = Quaternion.LookRotation(this.target.position - this.transform.position, this.target.up);
this.transform.rotation = Quaternion.Slerp(this.transform.rotation, desiredRotation, Time.deltaTime * this.rotationDamping);
}
Infine, per muovere il giocatore, abbiamo sfruttato la trasformazione della telecamera principale in modo che con i nostri controlli su si muova su, giù si muova giù, ecc. Ed è qui che chiamiamo UpdatePlayerTransform che farà scattare la nostra posizione sull'oggetto mondo.
void Update ()
{
Vector3 movementDirection = Vector3.zero;
if (Input.GetAxisRaw("Vertical") > 0)
{
movementDirection += cameraTransform.up;
}
else if (Input.GetAxisRaw("Vertical") < 0)
{
movementDirection += -cameraTransform.up;
}
if (Input.GetAxisRaw("Horizontal") > 0)
{
movementDirection += cameraTransform.right;
}
else if (Input.GetAxisRaw("Horizontal") < 0)
{
movementDirection += -cameraTransform.right;
}
movementDirection.Normalize();
UpdatePlayerTransform(movementDirection);
}
Per implementare una fotocamera più interessante, ma i controlli per essere quasi identici a quelli che abbiamo qui, potresti facilmente implementare una fotocamera che non è renderizzata o solo un altro oggetto fittizio su cui basare il movimento e quindi utilizzare la fotocamera più interessante per rendere ciò che vuoi che il gioco assomigli. Ciò consentirà piacevoli transizioni della videocamera mentre vai in giro per gli oggetti senza rompere i controlli.