J'ai réussi à accomplir ce dont j'avais besoin, principalement avec l'aide de ce billet de blog pour la pièce du puzzle en surface et j'ai trouvé mes propres idées pour le mouvement des joueurs et la caméra.
Accrochage du lecteur à la surface d'un objet
La configuration de base se compose d'une grande sphère (le monde) et d'une sphère plus petite (le joueur), toutes deux avec des collisionneurs de sphères qui leur sont attachés.
La majeure partie du travail effectué se faisait selon les deux méthodes suivantes:
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;
}
le Vector3 movementDirection
paramètre est juste tel qu'il semble, la direction dans laquelle nous allons déplacer notre lecteur dans cette image, et le calcul de ce vecteur, bien que relativement simple dans cet exemple, était un peu difficile à comprendre au début. Plus à ce sujet plus tard, mais gardez à l'esprit qu'il s'agit d'un vecteur normalisé dans la direction dans laquelle le joueur déplace cette image.
En progressant, la première chose que nous faisons est de vérifier si un rayon, provenant de la position future hypothétique dirigée vers le vecteur des joueurs vers le bas (-transform.up), frappe le monde en utilisant WorldLayerMask qui est une propriété LayerMask publique du script. Si vous souhaitez des collisions plus complexes ou plusieurs calques, vous devrez créer votre propre masque de calque. Si le raycast frappe avec succès quelque chose, hitInfo est utilisé pour récupérer le point normal et le point de coup pour calculer la nouvelle position et rotation du joueur qui devrait se trouver juste sur l'objet. Un décalage de la position du joueur peut être nécessaire en fonction de la taille et de l'origine de l'objet joueur en question.
Enfin, cela n'a vraiment été testé et ne fonctionne probablement bien que sur des objets simples tels que des sphères. Comme le suggère le billet de blog sur lequel j'ai basé ma solution, vous souhaiterez probablement effectuer plusieurs diffusions et les faire une moyenne pour votre position et votre rotation afin d'obtenir une transition beaucoup plus agréable lorsque vous vous déplacez sur un terrain plus complexe. Il peut également y avoir d'autres pièges auxquels je n'ai pas pensé à ce stade.
Caméra et mouvement
Une fois que le joueur collait à la surface de l'objet, la prochaine tâche à accomplir était le mouvement. J'avais à l'origine commencé par le mouvement par rapport au joueur, mais j'ai commencé à rencontrer des problèmes aux pôles de la sphère où les directions ont soudainement changé, ce qui a fait que mon joueur changeait rapidement de direction encore et encore ne me permettant jamais de passer les pôles. J'ai fini par faire bouger mes joueurs par rapport à la caméra.
Ce qui fonctionnait bien pour mes besoins était d'avoir une caméra qui suivait strictement le joueur uniquement en fonction de la position des joueurs. En conséquence, même si la caméra était techniquement en rotation, une pression vers le haut déplaçait toujours le lecteur vers le haut de l'écran, vers le bas vers le bas, etc. avec la gauche et la droite.
Pour ce faire, ce qui suit a été exécuté sur la caméra où l'objet cible était le joueur:
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);
}
Enfin, pour déplacer le joueur, nous avons exploité la transformation de la caméra principale de sorte que nos commandes montent, descendent, etc. Et c'est ici que nous appelons UpdatePlayerTransform qui obtiendra notre position accrochée à l'objet monde.
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);
}
Pour implémenter une caméra plus intéressante mais les commandes étant à peu près les mêmes que celles que nous avons ici, vous pouvez facilement implémenter une caméra qui n'est pas rendue ou juste un autre objet factice pour baser le mouvement, puis utiliser la caméra la plus intéressante pour rendre ce vous voulez que le jeu ressemble. Cela permettra de belles transitions de caméra lorsque vous contournerez des objets sans casser les commandes.