私は必要に応じて、主にパズルの表面をスナップするためのこのブログ投稿の助けを借りて、プレイヤーの動きとカメラに関する独自のアイデアを思いつきました。
プレーヤーをオブジェクトの表面にスナップする
基本的なセットアップは、大きな球体(世界)と小さな球体(プレイヤー)で構成され、どちらにも球体コライダーが接続されています。
行われた作業の大部分は、次の2つの方法で行われました。
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;
}
の Vector3 movementDirection
パラメーターは、このフレームでプレーヤーを移動する方向であり、この例では比較的単純になりますが、このベクトルで計算する方向は、私が最初に理解するのが少し面倒でした。それについては後で詳しく説明しますが、プレーヤーがこのフレームを移動する方向の正規化されたベクトルであることに注意してください。
最初に行うことは、スクリプトのパブリックLayerMaskプロパティであるWorldLayerMaskを使用して、プレイヤーダウンベクトル(-transform.up)に向けられた架空の未来の位置から発生するレイがワールドにヒットするかどうかを確認することです。より複雑な衝突や複数のレイヤーが必要な場合は、独自のレイヤーマスクを作成する必要があります。レイキャストが何かに成功した場合、hitInfoを使用して法線とヒットポイントを取得し、オブジェクト上にあるはずのプレーヤーの新しい位置と回転を計算します。プレーヤーの位置のオフセットは、問題のプレーヤーオブジェクトのサイズと起源に応じて必要になる場合があります。
最後に、これは実際にテストされただけであり、球などの単純なオブジェクトでのみうまく機能する可能性があります。私のソリューションのベースとなったブログ記事が示唆しているように、複数のレイキャストを実行し、それらをあなたの位置と回転に対して平均化して、より複雑な地形を移動するときに、より良い遷移を得ることができます。この時点では考えもしなかった落とし穴もあるかもしれません。
カメラと動き
プレイヤーがオブジェクトの表面にくっついたら、次に取り組むべきタスクは動きでした。私はもともとプレイヤーに対する動きから始めていましたが、球の極で問題が発生し始めたため、方向が突然変わり、プレイヤーは極を通過させずに何度も何度も方向を変えました。私がやったことは、カメラに対するプレイヤーの動きを作ることでした。
私のニーズに合ったのは、プレーヤーの位置のみに基づいて、プレーヤーに厳密に追従するカメラを持つことでした。その結果、カメラが技術的に回転していても、上に押すと、常に画面の上部に向かって、下部に向かって下に、というようにプレイヤーが移動しました。
これを行うために、ターゲットオブジェクトがプレーヤーであるカメラで次のコマンドが実行されました。
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);
}
最後に、プレーヤーを移動するために、メインカメラの変換を活用して、コントロールを上下に移動します。そして、ここでUpdatePlayerTransformを呼び出して、位置をワールドオブジェクトにスナップさせます。
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);
}
より興味深いカメラを実装しますが、コントロールをここにあるものとほぼ同じにするには、レンダリングされていないカメラ、または動きのベースとなる別のダミーオブジェクトを簡単に実装し、より興味深いカメラを使用してゲームは次のようになります。これにより、コントロールを壊さずにオブジェクトを移動する際に、カメラの切り替えがうまくなります。