Unityで物を持つ #1

投稿者: | 2020-02-20


他のオブジェクトの表面に沿ってオブジェクトを移動させるスクリプトを変更して、物を持ち上げたり置いたりできるようにします。

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

public class moveItemScript2 : MonoBehaviour
{
    public GameObject player;
    public Text itemText;

    LayerMask mask;
    GameObject item1;
    RaycastHit hit;

    CubeScript2 sc_item1;
    bool grab;
    Vector3 item_up;
    Rigidbody rb_item1;
    Collider col_item1;

    // Start is called before the first frame update
    void Start()
    {

        mask = ~(1 << 8 | 1 << 9);
        grab = false;
        itemText.text = "";
    }

    // Update is called once per frame
    void Update()
    {
        if (Physics.Raycast(player.transform.position, player.transform.transform.forward, out hit, Mathf.Infinity, mask))
        {

            if (grab) // ものを持っているとき
            {
                sc_item1.ray = true;

                Debug.DrawRay(player.transform.position, player.transform.transform.forward * hit.distance, Color.yellow);

                item1.transform.position = hit.point; // アイテムをレイの当たったところに移動
                //item1.transform.rotation = Quaternion.FromToRotation(item_up, hit.normal);
                item1.transform.rotation = Quaternion.FromToRotation(Vector3.up, hit.normal); // アイテムの上方向をレイが当たったところの表面の法線方向にする
                item1.transform.position += item1.transform.localScale.y / 1.98f * hit.normal; // アイテムが埋まらないように、表面方向に少し動かす

                if (Input.GetKeyDown(KeyCode.F) && sc_item1.ray && !sc_item1.trigStay && sc_item1.grab) // アイテムを置く
                {
                    grab = false;
                    sc_item1.grab = false;
                    col_item1.isTrigger = false;
                    rb_item1.useGravity = true;
                    rb_item1.isKinematic = false;
                    item1.layer = LayerMask.NameToLayer("Default"); //「Default」レイヤーをつける
                }

            }
            else { // 持ってない時

                itemText.text = hit.collider.name; // オブジェクトの名前を表示

                if (Input.GetKeyDown(KeyCode.F) && hit.collider.tag == "Item") // アイテムを持ち上げる
                {
                    grab = true;             
                    item1 = hit.collider.gameObject;
                    sc_item1 = item1.GetComponent<CubeScript2>();
                    sc_item1.grab = true;

                    sc_item1.transform.rotation = Quaternion.Euler(Vector3.zero); // 回転をリセット

                    // リジッドボディ
                    rb_item1 = item1.GetComponent<Rigidbody>();
                    rb_item1.useGravity = false;
                    rb_item1.isKinematic = true;

                    // コライダー
                    col_item1 = item1.GetComponent<Collider>();
                    col_item1.isTrigger = true;

                    //item_up = item1.transform.up; // 持ったときのアイテムの上方向を記憶
                    item1.layer = LayerMask.NameToLayer("Cube"); //「Cube」レイヤーをつける

                    itemText.text = "";

                }
            }
        }
        else
        {
            if (grab)
            {
                    sc_item1.ray = false;
            }
        }
    }
}

プレイヤーが物を持っているときと持っていないときで処理を分けました。「Item」タグのついたオブジェクトにプレイヤーからのレイが当たっているときにFキーを押すと、そのオブジェクトを持ちます。

物を持っていないときは、レイが当たったオブジェクトの名前を画面に表示します。

コライダーとRigidbodyの設定

「Item」タグを付けて、持てるようにするオブジェクトには、コライダーとRigidbodyをつけます。

デフォルトでは、コライダーのIs TriggerとRigidbodyのIs Kinematicをオフにして、Use Gravityをオンにしますが、アイテムを持ったときにスクリプトでこれらを切り替えます。

// リジッドボディ
rb_item1 = item1.GetComponent<Rigidbody>();
rb_item1.useGravity = false;
rb_item1.isKinematic = true;

// コライダー
col_item1 = item1.GetComponent<Collider>();
col_item1.isTrigger = true;

これで、アイテムを持っているときは、そのアイテムをTransformで動かして、離すと物理演算で動かすことができます。

アイテムに付けるスクリプト

アイテムのスクリプトも変更しました。

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

public class CubeScript2 : MonoBehaviour
{
    MeshRenderer meshRenderer;
    Color defaultColor;
    Color defaultColor_Transparent;

    public bool ray = false;
    public bool trigStay  = false;

    public bool grab = false;

    // Start is called before the first frame update
    void Start()
    {
        meshRenderer = GetComponent<MeshRenderer>();
        defaultColor = meshRenderer.material.GetColor("_BaseColor");
        defaultColor_Transparent = defaultColor;
        defaultColor_Transparent.a = 0f;       
    }

    // Update is called once per frame
    void Update()
    {
        if ((grab & !ray) || (grab && ray && trigStay))
        {
            meshRenderer.material.SetColor("_BaseColor", Color.red);
            //meshRenderer.material.SetColor("_BaseColor", defaultColor_Transparent);           
        }
        else {
            meshRenderer.material.SetColor("_BaseColor", defaultColor);

        }    
    }

    private void OnTriggerStay(Collider other)
    {
        trigStay = true;
    }

    private void OnTriggerExit(Collider other)
    {
        trigStay = false;
    }
}

真偽値の変数を増やして条件を変えました。3つがすべてTrueのときと、アイテムを持っていて、レイがどこにも当たっていないときに赤くすれば良いと思います。

Fキーを押した時にアイテムをおけるのは、レイがどこかに当たっていて、OnTriggerStayが実行されていなくて、アイテムを持っているときだけです。

アイテムの回転値

item_up = item1.transform.up; // 持ったときのアイテムの上方向を記憶
// ---
item1.transform.rotation = Quaternion.FromToRotation(item_up, hit.normal); // アイテムの上方向をレイが当たった場所の法線方向に向ける

アイテムを持ったときの回転を保ったまま、他のオブジェクトの表面に沿って移動・回転させようとすると、落下中に斜めになっているアイテムを持ったときに、常に床や壁に埋まるようになりました。

そこで、アイテムを持った時に回転値をリセットすることにしました。

sc_item1.transform.rotation = Quaternion.Euler(Vector3.zero); // 回転をリセット
// ---
item1.transform.rotation = Quaternion.FromToRotation(Vector3.up, hit.normal);

シリンダーオブジェクト

プリセットのCylinderオブジェクトは、縦長なのにスケールがデフォルトで(1, 1, 1)になっていて常に埋まってしまいます。

コメントを残す

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