キャラクターの移動(Input.GetAxisRaw)

キャラクターの移動(Input.GetAxisRaw)

 Input.GetAxisRawを用いてキャラクターの移動を行うScriptを作成しました。

Script

 作成したScriptは以下の通りです。カメラをコントロールする箇所は前記事に掲載したScriptと同じです。Input.GeaAxisはキーが押されると、1もしくは-1へ徐々に近づきます。逆に、キーが離されたときは0へ徐々に近づきます。これに対し、Input.GetAxisRawは押したキーに応じて1か-1を返します。また、キーが押されていないときは0を返します。そのため、加速や減速を行う際は、自身でScriptへ記述する必要があります。よって、加速や減速を自身が決めた方法で行うことができます。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerController_Raw : MonoBehaviour
{
    private Transform trf_player;
    private CharacterController controller;
    private Animator anim;

    //カメラ関連
    private float sub_radius;
    private float angle_x, angle_y = -45.0f, radius = 9.0f;
    private const float radius_min = 0.1f, radius_max = 15.0f, radius_speed = 3.0f, offset_speed = 1.8f;
    private Vector2 mouse_speed = new Vector2(1.0f, 1.5f);
    private Vector2 camera_offset_range = new Vector2(2.0f, 0.1f);
    private Vector3 v3_camera_offset_h = new Vector3(0.0f, 0.0f, 0.0f);
    private Vector3 v3_camera_offset_v = new Vector3(0.0f, 0.5f, 0.0f);
    private Transform trf_main_camera;
    private LayerMask layer_mask = 1;

    //プレイヤーの移動や回転
    private float sub_rot_angle, player_angle;
    private float forward_speed = 6.0f, back_speed = 2.0f, gravity = 1.0f, rot_velocity = 0.0f;
    private float velocity = 0.0f, max_velocity = 1.0f, accel = 1.0f, decel = 1.0f;
    private const float rot_smoothing_time = 0.2f;
    private int tmp_h = 0, tmp_v = 0;
    private Vector3 v3_right_dir;
    private Vector3 move_dir = Vector3.zero;
    private Quaternion rot_player;
    private bool flg_simultaneous_h = false, flg_simultaneous_v = false;

    // Start is called before the first frame update
    void Start()
    {
        trf_player = gameObject.transform;
        controller = gameObject.GetComponent();
        anim = gameObject.GetComponent();

        trf_main_camera = Camera.main.transform;
        trf_main_camera.position = new Vector3(0.0f, 3.0f, -3.0f) + trf_player.position;

        sub_radius = radius;
        rot_player = trf_player.rotation;
        sub_rot_angle = 0.0f;
        v3_right_dir = trf_player.right;

        layer_mask = ~(1 << LayerMask.NameToLayer("Water"));
        layer_mask &= ~(1 << LayerMask.NameToLayer("Player"));
    }

    //加速
    private float Acceleration(float vel, float dir)
    {
        vel += dir * accel * Time.deltaTime;
        vel = Mathf.Clamp(vel, -max_velocity, max_velocity);
        return vel;
    }

    //減速
    private float Deceleration(float vel)
    {
        float dir = Mathf.Sign(vel);
        vel = Mathf.Abs(vel) - decel * Time.deltaTime;
        vel = Mathf.Max(0.0f, vel) * dir;
        return vel;
    }

    //プレイヤーの回転
    private void PlayerRotation(float angle)
    {
        sub_rot_angle = Mathf.SmoothDamp(sub_rot_angle, angle, ref rot_velocity, rot_smoothing_time);
        trf_player.rotation = rot_player * Quaternion.AngleAxis(sub_rot_angle, Vector3.up);
    }

    // Update is called once per frame
    void Update()
    {
        int h = (int)Input.GetAxisRaw("Horizontal_2");
        int v = (int)Input.GetAxisRaw("Vertical_2");
        angle_x = Input.GetAxis("Mouse X") * mouse_speed.x;
        angle_y -= Input.GetAxis("Mouse Y") * mouse_speed.y;
        angle_y = Mathf.Clamp(angle_y, -179.0f, -1.0f);

        float speed = forward_speed;

        if (controller.isGrounded)
        {
            //同時押しの時、後に押したキーを優先(同時押しした場合GetAxisRawは0となる)
            if (Input.GetButton("Vertical_2") && v == 0)
            {
                v = tmp_v;
                if (!flg_simultaneous_v)
                {
                    v = -tmp_v;
                    flg_simultaneous_v = true;
                }
            }
            else
            {
                flg_simultaneous_v = false;
            }

            if (Input.GetButton("Horizontal_2") && h == 0)
            {
                h = tmp_h;
                if (!flg_simultaneous_h)
                {
                    h = -tmp_h;
                    flg_simultaneous_h = true;
                }
            }
            else
            {
                flg_simultaneous_h = false;
            }

            //同時押しの時、先に押したキーを優先(同時押しした場合GetAxisRawは0となる)
            //if (Input.GetButton("Vertical_2") && v == 0)
            //{
            //    v = tmp_v;
            //}

            //if (Input.GetButton("Horizontal_2") && h == 0)
            //{
            //    h = tmp_h;
            //}


            //方向転換時に速度を0へ(前後)
            if (v * tmp_v < 0)
            {
                velocity = 0.0f;
            }
            //方向転換時に速度を0へ(左右)
            if (h * tmp_h < 0 && v == 0)
            {
                velocity = 0.0f;
            }

            if (h * v != 0)
            {
                //斜め移動
                if (h * v > 0.0f)
                {
                    PlayerRotation(45.0f);
                }
                else
                {
                    PlayerRotation(-45.0f);
                }

                if (v < 0.0f)
                {
                    //横移動=>斜め後ろ移動のとき速度0
                    if (velocity > 0.0f)
                    {
                        velocity = 0.0f;
                    }
                }

                velocity = Acceleration(velocity, v);
            }
            //横移動
            else if (h != 0)
            {
                if (h > 0)
                {
                    PlayerRotation(90.0f);
                }
                else
                {
                    PlayerRotation(-90.0f);
                }

                //後方と斜め後ろ=>横移動のとき速度を0
                //斜め後ろ移動から後ろ=>横キーの順番で離すとすぐに止まってしまうので、Mathf.Abs(sub_rot_angle) < 15.0fで回避している
                if (velocity < 0.0f && Mathf.Abs(sub_rot_angle) < 15.0f)
                {
                    velocity = 0.0f;
                }

                velocity = Acceleration(velocity, 1.0f);
            }
            //前後移動
            else if (v != 0)
            {
                PlayerRotation(0.0f);

                if (v < 0)
                {
                    //前、斜め前及び横移動=>後ろ移動のとき速度0
                    if(velocity > 0.0f)
                    {
                        velocity = 0.0f;
                    }
                }

                velocity = Acceleration(velocity, v);
            }
            else
            {
                //移動キーを離した後に生じる慣性
                velocity = Deceleration(velocity);

            }

            if (velocity < 0.0f)
            {
                speed = back_speed;
            }
            move_dir = new Vector3(0.0f, 0.0f, velocity);
        }

        rot_player *= Quaternion.AngleAxis(angle_x, Vector3.up);

        move_dir.y -= gravity * Time.deltaTime;
        Vector3 t_dir = trf_player.TransformDirection(move_dir) * speed;
        controller.Move(t_dir * Time.deltaTime);

        //前に押されていたキーを保存
        if (h != 0)
        {
            tmp_h = h;
        }
        if (v != 0)
        {
            tmp_v = v;
        }

        anim.SetFloat("unity_chan", Vector3.Magnitude(new Vector3(t_dir.x, 0.0f, t_dir.z)));
    }

    private void LateUpdate()
    {
        /* カメラの中心を移動 */
        Vector3 v3_temp_offset_h = v3_camera_offset_h;
        Vector3 v3_temp_offset_v = v3_camera_offset_v;
        v3_camera_offset_h += Input.GetAxisRaw("Camera_Horizontal") * trf_main_camera.right * offset_speed * Time.deltaTime;
        v3_camera_offset_v -= Input.GetAxisRaw("Camera_Vertical") * trf_main_camera.up * offset_speed * Time.deltaTime;

        /* カメラの移動範囲を限定する */
        //offsetのyが限界値でもカメラが下を向いているときに↑キーで動かせるように
        if (trf_main_camera.up.y < 0.3f)
        {
            v3_camera_offset_v.y = Mathf.Clamp(v3_camera_offset_v.y, camera_offset_range.y, camera_offset_range.x);
        }

        float offset_h_magnitude = Vector3.Magnitude(new Vector3(v3_camera_offset_v.x, 0.0f, v3_camera_offset_v.z));

        if (Vector3.Magnitude(v3_camera_offset_h) > camera_offset_range.x || offset_h_magnitude > camera_offset_range.x
                            || v3_camera_offset_v.y > camera_offset_range.x || v3_camera_offset_v.y < camera_offset_range.y)
        {
            v3_camera_offset_h = v3_temp_offset_h;
            v3_camera_offset_v = v3_temp_offset_v;
        }

        v3_camera_offset_h = Quaternion.AngleAxis(angle_x, Vector3.up) * v3_camera_offset_h;
        v3_camera_offset_v = Quaternion.AngleAxis(angle_x, Vector3.up) * v3_camera_offset_v;
        Vector3 v3_center = trf_player.position + v3_camera_offset_h + v3_camera_offset_v;

        /* カメラのズームイン/アウト */
        if (Input.GetAxis("Mouse ScrollWheel") != 0)
        {
            sub_radius -= Input.GetAxis("Mouse ScrollWheel") * radius_speed;
            sub_radius = Mathf.Clamp(sub_radius, radius_min, radius_max);
            radius = sub_radius;
        }

        /* プレイヤーとカメラのの間に障害物があった場合の処理 */
        RaycastHit hit;
        Vector3 v3_camera_pos;
        v3_right_dir = Quaternion.AngleAxis(angle_x, Vector3.up) * v3_right_dir;  //マウス操作による右方向を示すベクトルの回転
        v3_camera_pos = Quaternion.AngleAxis(angle_y, v3_right_dir) * Vector3.up * radius;   //ズームイン/アウト後のカメラ位置
        v3_camera_pos += v3_center;

        /* radius = hit.distanceだと、次フレームでレイが当たらなくなりradistが大きくなる。
            さらに次フレームではレイが当たりradius = hit.distanceとなる。これを繰り返してしまうため
            カメラがガタガタ震える。そのためhit.distance + αとし、これを防いでいる。  */
        if (Physics.Linecast(v3_center, v3_camera_pos, out hit, layer_mask))
        {
            radius = hit.distance + 0.1f;
        }
        else if (radius < sub_radius)
        {
            radius += 10.0f * Time.deltaTime;
            radius = Mathf.Min(radius, sub_radius); //上記処理でradius > subradiusとなったときradius = subradiusとする
        }

        v3_camera_pos = Quaternion.AngleAxis(angle_y, v3_right_dir) * Vector3.up * radius;   //障害物検知後のカメラ位置
        trf_main_camera.position = v3_camera_pos + v3_center;
        trf_main_camera.LookAt(v3_center);
    }
}

このScriptを使用するには、Edit→Project Settings→Input ManagerからHorizontal_2(Negative Button→a、Positive Button→d)、 Vertical_2(Negative Button→s、Positive Button→w) 、 Camera_Horizontal(Negative Button→left、Positive Button→right)及びCamera_Vertical(Negative Button→up、Positive Button→down) を新しく作成する必要があります。また、Charactor Controllerも必要となります。

移動方向の変更

 Input.GetAxisRawから得られる値によって、キャラクターの移動方向を決定しています。

int h = (int)Input.GetAxisRaw("Horizontal_2");
int v = (int)Input.GetAxisRaw("Vertical_2");
・・・
if (h * v != 0)
{
    //斜め移動
    if (h * v > 0.0f)
    {
・・・
    }
・・・
}
//横移動
else if (h != 0)
{
    if (h > 0)
    {
・・・
    }
・・・
}
//前後移動
else if (v != 0)
{
・・・
}
else
{
・・・
}

また、マウス移動による移動方向の変更は

angle_x = Input.GetAxis("Mouse X") * mouse_speed.x;
rot_player *= Quaternion.AngleAxis(angle_x, Vector3.up);

で行っています。そして、移動方向を変更したとき、変更する移動方向に応じて速度を0にする処理を加えています。

//方向転換時に速度を0へ(前後)
if (v * tmp_v < 0)
{
    velocity = 0.0f;
}
//方向転換時に速度を0へ(左右)
if (h * tmp_h < 0 && v == 0)
{
    velocity = 0.0f;
}
・・・
    if (v < 0.0f)
    {
        //横移動=>斜め後ろ移動のとき速度0
        if (velocity > 0.0f)
        {
            velocity = 0.0f;
        }
    }
・・・
   //後方と斜め後ろ=>横移動のとき速度を0
   if (velocity < 0.0f && Mathf.Abs(sub_rot_angle) < 15.0f)
   {
       velocity = 0.0f;
   }
・・・
if (v < 0)
{
    //前、斜め前及び横移動=>後ろ移動のとき速度0
    if(velocity > 0.0f)
    {
        velocity = 0.0f;
    }
}

斜め後ろ→横へ移動方向を変える際、速度が0となるように処理されています。そのため、斜め後ろ移動から停止しようとキーを離した際、下キー→横キーの順で離されると速度が0となり、減速してから止まらずに急停止します。よって、if文に以下の条件を加えることで、これを回避しています。

Mathf.Abs(sub_rot_angle) < 15.0f

同時押し

 Input.GetAxisRawはInput Managerで設定した二つのキーを同時に押すと0を返します。そのため、特に何も処理をしなければ止まってしまいます。そこで、以下のコードを追加し、同時押しがされた際に止まらないようにしました。

先に押したキーを優先

 先に押されていたキーで移動するためのコードです 。

if (Input.GetButton("Vertical_2") && v == 0)
{
    v = tmp_v;
}

if (Input.GetButton("Horizontal_2") && h == 0)
{
    h = tmp_h;
}
・・・
if (h != 0)
{
    tmp_h = h;
}
if (v != 0)
{
    tmp_v = v;
}

後に押したキーを優先

 後から押されたキーで移動するためのコードです。

if (Input.GetButton("Vertical_2") && v == 0)
{
    v = tmp_v;
    if (!flg_simultaneous_v)
    {
        v = -tmp_v;
        flg_simultaneous_v = true;
    }
}
else
{
    flg_simultaneous_v = false;
}

if (Input.GetButton("Horizontal_2") && h == 0)
{
    h = tmp_h;
    if (!flg_simultaneous_h)
    {
        h = -tmp_h;
        flg_simultaneous_h = true;
    }
}
else
{
    flg_simultaneous_h = false;
}
・・・
if (h != 0)
{
    tmp_h = h;
}
if (v != 0)
{
    tmp_v = v;
}

実行結果

 実行結果は以下の通りです。コントロールキーを押すとカーソルがロックされます。もう一度、コントロールキーを押すと解除されます。


このコンテンツはユニティちゃんライセンス条項の元に提供されています