はじめに
今回の記事は
現在Unityで制作中の3D横スクロールアクションにおけるプレイヤー移動の続きになっています。プレイヤーの移動に関しての説明は割愛しているので、こちらのページをご覧ください。
また、前回同様Unity初心者さん向けの記事なので上級者さんはブラウザバック推奨です。
前回の補足
前回のままだと壁がないのでz軸移動すると下に落ちてしまいます。側面にCubeを配置して、meshFilterをNoneにして透明な壁を作りましょう。
プレイヤーのジャンプ実装
まずは、プレイヤーのインスペクターのRigidbodyを画像のように変更してください。(URPプロジェクトなのでビルトインだと配置が違うかも)
public class PlayerMoveJump : MonoBehaviour
{
//移動関係
Vector3 moveDirection;
[SerializeField] private float speedMag;
private Rigidbody rb;
[SerializeField] private Vector3 moveVel;
//入力関係
private float InputX;
private float InputZ;
private bool InputJump;
// ジャンプ関係
[SerializeField] private float jumpMomentPower;
// 地面判定関係
[SerializeField] private float rayLength = 0.1f;
[SerializeField] private LayerMask groundLayer;
private bool Landing;
void Start()
{
rb = GetComponent<Rigidbody>();
}
void Update()
{
InputDetection();
CalculateMoveVec();
}
private void FixedUpdate()
{
Landing = IsGround();
MoveFixed();
RotationFixed();
JumpFixed();
}
private void InputDetection()
{ //プレイヤーの入力検知
InputX = Input.GetAxisRaw("Horizontal");
InputZ = Input.GetAxisRaw("Vertical");
if (Input.GetButtonDown("Jump"))
{
InputJump = true;
}
else
{
InputJump = false;
}
}
private void CalculateMoveVec()
{
//移動のベクトル計算
moveDirection = new Vector3(InputX, 0, InputZ);
moveDirection.Normalize();
moveVel = moveDirection * speedMag;
}
private void MoveFixed()
{
//ジャンプ中は移動ベクトルは触らないように
if (!Landing) return;
//移動
rb.velocity = new Vector3(moveVel.x, rb.velocity.y, moveVel.z);
}
private void RotationFixed()
{
// 回転
if (moveVel != Vector3.zero)// 入力していないときに(0,0,0)に向かないように
{
transform.localRotation = Quaternion.Lerp(transform.localRotation,
Quaternion.LookRotation(moveVel),
20.0f * Time.deltaTime);//入力方向に回転
}
}
private void JumpFixed()
{
if (!Landing) return;//ジャンプ中なら出る
if (InputJump)//ジャンプ押した瞬間のみ
{
rb.AddForce(transform.up * jumpMomentPower, ForceMode.Impulse);
}
}
private bool IsGround()
{
//四つのレイキャストを出して地面にいるか判定しているよ
float rad = 0.7f;
Vector3 rayCastPos1 = transform.position;
rayCastPos1.x += rad;
rayCastPos1.z += rad;
Ray ray1 = new Ray(rayCastPos1, Vector3.down);
Debug.DrawRay(rayCastPos1, Vector3.down * rayLength, Color.red);
bool hit1 = Physics.Raycast(ray1, rayLength, groundLayer);
Vector3 rayCastPos2 = transform.position;
rayCastPos2.x -= rad;
rayCastPos2.z += rad;
Ray ray2 = new Ray(rayCastPos2, Vector3.down);
Debug.DrawRay(rayCastPos2, Vector3.down * rayLength, Color.red);
bool hit2 = Physics.Raycast(ray2, rayLength, groundLayer);
Vector3 rayCastPos3 = transform.position;
rayCastPos3.x += rad;
rayCastPos3.z -= rad;
Ray ray3 = new Ray(rayCastPos3, Vector3.down);
Debug.DrawRay(rayCastPos3, Vector3.down * rayLength, Color.red);
bool hit3 = Physics.Raycast(ray3, rayLength, groundLayer);
Vector3 rayCastPos4 = transform.position;
rayCastPos4.x -= rad;
rayCastPos4.z -= rad;
Ray ray4 = new Ray(rayCastPos4, Vector3.down);
Debug.DrawRay(rayCastPos4, Vector3.down * rayLength, Color.red);
bool hit4 = Physics.Raycast(ray4, rayLength, groundLayer);
if (hit1 || hit2 || hit3 || hit4)
{
return true;//地面についてたらtrue
}
return false;//他false
}
}
MoveFixed()内も変更が加わっているので注意です。ジャンプ中も移動の処理が動いてしまうと、右に走りながらジャンプしたのに空中で左に移動したりできてします。とりあえずはジャンプ中に移動の処理は動かないようにしています。
プレイヤーの入力はInputDetection()で受け取って、InputJump変数に格納しています。
ジャンプの処理はJumpFixed()でAddForceを使用しています。ForceMode.Impulseで、瞬発的に力を加えています。地上にいるときのみジャンプできるようにしています。
IsGround()では、プレイヤーが地面にいるか判定しています。判定方法はRaycastを使用。
一つだけだと、崖際などで地面についているのに浮いている判定を食らってしまうので、四つ使用しています。
RayCastの判定を地面で取るために新しくGroundLayerを設定し、地面のオブジェクトに設定しましょう
私のスクリプトの変数値はこんな感じ。自分好みの操作感を見つけてみてください。
プロジェクトの重力を変える
今のままだとジャンプ後の着地が遅く、フワフワした動きになってしまいます。
Edit→Project Settings→Physicsから、GravityのYを変更しましょう。
-45~-50辺りがいい感じです。
崖や透明な壁との摩擦を無くす。
今のままだと、ジャンプ中に崖や透明な壁に当たると、高さが出なかったり、ゆっくり落ちたりすると思います。
Projectタブで右クリック→Create→Physic Material(結構下の方です)で、Physic Materialを作成し全ての値を0にして
プレイヤー、地面、透明な壁のcoliderのMaterialに適応しましょう。
完成はこんな感じ
Qiita用ジャンプ処理動画 pic.twitter.com/TK3GeX5VuH
— 胡桃メガネ/Unity個人開発 (@MeGaNeKuRuMIII) May 2, 2024
ここにアニメーションの処理を書いていたり、コヨーテタイムの実装もしていたりするのが現状のジャンプ処理です。
また何か機能を実装したら記事に書きます。お疲れ様でした。