【Unity】一人称視点でオブジェクトを掴んで移動させる

投稿者: | 2020-05-08

一人称視点でオブジェクトを掴んで移動させます。掴んでいる間も、物理演算で他のアイテムや床と衝突するようにします。

前のスクリプトに追記して、近くでアイテムにレイがあたっているときにマウスの左クリックをすると、アイテムを掴んだ状態に遷移するようにしました。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityStandardAssets.CrossPlatformInput;
using UnityEngine.UI;



public class ItemScript3 : MonoBehaviour
{
    [SerializeField] float turnSpeed = 3f; // アイテムを回転させる速さ
    [SerializeField] GameObject item; // アイテム
    [SerializeField] GameObject image; // 手のマーク
    [SerializeField] float d = 1f; // 持ったアイテムのカメラからの距離
    [SerializeField] float armLength = 2f; // レイの長さ
    [SerializeField] Text text;
    [SerializeField] PhysicMaterial itemPM_hold; // 持った時のアイテムの物理マテリアル

    RaycastHit hit;    
    Vector3 itemPos; // アイテムのデフォルトの位置
    Quaternion itemRot; // アイテムのデフォルトの回転
    bool hold = false; // アイテムを持ち上げているかどうか
    int state = 0;
    UnityStandardAssets.Characters.FirstPerson1.FirstPersonController1 sc_camera;
    Rigidbody itemRb;
    float itemDist = 0f;
    Collider itemColl;
    PhysicMaterial itemPM; // アイテムの元の物理マテリアル




    // Start is called before the first frame update
    void Start()
    {
        sc_camera = transform.parent.GetComponent();
    }

    // Update is called once per frame
    void Update()
    {        
        // アイテムを持ち上げている時
        if (state == 1)
        {
            // ドラッグでアイテムを回転
            if (CrossPlatformInputManager.GetButton("Fire1"))
            {
                var x = CrossPlatformInputManager.GetAxis("Mouse X");
                var y = CrossPlatformInputManager.GetAxis("Mouse Y");

                // シフトキーを押している時
                if (CrossPlatformInputManager.GetButton("Fire3"))
                {
                    d += y / 5f;
                    d = Mathf.Clamp(d, 0.5f, 4f);
                    // アイテムの距離を変える
                    item.transform.position = transform.position + transform.forward * d;

                }
                // シフトキーを押してない時
                else
                {
                    // アイテムを回転
                    item.transform.localRotation *= Quaternion.Euler(item.transform.InverseTransformDirection(transform.up) * x * -turnSpeed);
                    item.transform.localRotation *= Quaternion.Euler(item.transform.InverseTransformDirection(transform.right) * y * turnSpeed);

                }
            }
                  
            // Eキーでアイテムを置く
            if (Input.GetKeyDown(KeyCode.E))
            {
                // デフォルトの位置・回転に戻す
                item.transform.position = itemPos;
                item.transform.rotation = itemRot;

                itemRb.useGravity = true;
                itemRb.isKinematic = false;

                //hold = false;
                state = 0;
                sc_camera.LockCamera(false); // カメラのロックを解除

            }

        }
        // アイテムを持ち上げていない時
        else if (state == 0)
        {
            if (Physics.Raycast(transform.position, transform.forward, out hit, armLength, ~(1 << 10)))
            {
                if (hit.collider.tag == "Item")
                {
                    image.SetActive(true); // 手のマークを出す

                    // Eキーでアイテムを持ち上げる
                    if (Input.GetKeyDown(KeyCode.E))
                    {
                        item = itemColl.gameObject;
                        itemRb = item.GetComponent();
                        itemRb.useGravity = false;
                        itemRb.isKinematic = true;

                        // アイテムの位置と回転を保存
                        itemPos = item.transform.position;
                        itemRot = item.transform.rotation;

                        // 目の前にアイテムを移動
                        d = 1f;
                        Vector3 itemNewPos = transform.position + transform.forward * d;
                        item.transform.position = itemNewPos;

                        // アイテムを持ち上げる
                        state = 1;

                        image.SetActive(false); // 手のマークを消す
                        sc_camera.LockCamera(true); // カメラをロック
                    }

                    // アイテムを持って移動
                    if (CrossPlatformInputManager.GetButtonDown("Fire1"))
                    {
                        itemColl = hit.collider;

                        // アイテムの物理マテリアルを変更
                        itemPM = itemColl.material;
                        itemColl.material = itemPM_hold;

                        item = itemColl.gameObject;

                        itemRb = item.GetComponent();
                        itemRb.useGravity = false;

                        itemRot = item.transform.rotation;
                        itemPos = transform.InverseTransformPoint(item.transform.position);
                        itemDist = itemPos.magnitude;

                        state = 2;
                    }
                }
                else
                {
                    image.SetActive(false);
                }
            }
            else
            {
                image.SetActive(false);
            }
        }
        // アイテムを移動させる
        else if (state == 2)
        {
            itemRb.velocity = (transform.forward * itemDist + transform.position - item.transform.position) * 10f;

            itemRb.angularVelocity = transform.up * Vector3.Dot(item.transform.forward, transform.forward) * turnSpeed;
            //itemRb.angularVelocity = Vector3.Cross(item.transform.forward, transform.forward) * Vector3.Dot(item.transform.forward, transform.forward) * turnSpeed;


            if (CrossPlatformInputManager.GetButtonUp("Fire1"))
            {
                // アイテムの物理マテリアルをもとに戻す
                itemColl.material = itemPM;

                itemRb.useGravity = true;
                itemRb.isKinematic = false;
                state = 0;
            }
        }
    }
}

アイテムの物理演算をやめてトランスフォームで移動させると、床や他のアイテムにぶつからなくなるので、Rigidbodyで移動させます。

velocityにアイテムの現在の位置から、プレイヤーの視線の少し先の位置へのベクトルを入れることで常にアイテムがカメラの真ん中へ移動します。

itemRb.velocity = (transform.forward * itemDist + transform.position - item.transform.position) * 10f;

また、アイテムの回転する量にカメラの正面方向とアイテムの正面方向の内積を使うと常にアイテムがプレイヤーに同じ側を向けるようになりました。2つの方向のなす角が大きくなれば回転速度が上がり、小さくなると下がるはずです。

回転させる軸にはカメラの上方向を使っているので、カメラを上下に振っても回転しません。

itemRb.angularVelocity = transform.up * Vector3.Dot(item.transform.forward, transform.forward) * turnSpeed;

※アイテムを回転させる部分を修正しました【Unity】一人称視点でオブジェクトを掴んで移動させる #2

衝突させると暴れる

このままだと、掴んだアイテムが他のオブジェクトに衝突したときに小刻みに震えます。これは、デフォルトの摩擦や弾力のせいだと思います。

なので、摩擦や弾力が0のフィジックマテリアルを用意して、アイテムを掴んだ時にアイテムのフィジックマテリアルと入れ替えます。

Physic Material

Friction CombineとBounce CombineをMinimumにしているので、他のオブジェクトの物理マテリアルに0より大きい摩擦や弾力が設定されていても0が使われます。

itemColl = hit.collider;

// アイテムの物理マテリアルを変更
itemPM = itemColl.material;
itemColl.material = itemPM_hold;

// ---
// アイテムの物理マテリアルをもとに戻す
itemColl.material = itemPM;

アイテムに元々物理マテリアルが設定されていなくても問題ないようです。

これでアイテムを掴んで移動したり放り投げることができます。同じアイテムを回転させて観察することもできます。

コメントを残す

メールアドレスが公開されることはありません。