【Unity】FPSで視線上にあるオブジェクトを検知する方法

Unity

はじめに

FPSカメラを実装していると、カメラに映ったアイテムをピックアップしたいときに、

「目の前にあるアイテムの情報が欲しい!」と思う場面があります。

今回はRayという機能を使い視線の先にあるオブジェクトを検出。

さらに、検出したオブジェクトを取得して操作する方法をご紹介します。

Raycastとは?

イメージで言いますと、指定した場所から、指定した方向へ向けて、レーザーを飛ばして

レーザーが衝突したモノを検出する機能です。

これを利用して、実装していきます。

RaycastはColliderにしか反応しない

Raycastはすべてのオブジェクトに反応してくれるわけじゃありません。

Scene上のColliderがアタッチされているオブジェクトにのみ反応するので、注意しましょう。

スクリプトを作成

「PlayerRay.cs」というスクリプトを作ってみました。スクリプトファイル名は何でもいいです。

スクリプトの中身は以下の通りです。

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

public class PlayerRay : MonoBehaviour
{
    [SerializeField] Camera     fpsCam;             // カメラ
    [SerializeField] float      distance = 0.8f;    // 検出可能な距離

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

    // Update is called once per frame
    void Update()
    {
        // Rayはカメラの位置からとばす
        var rayStartPosition   = fpsCam.transform.position;
        // Rayはカメラが向いてる方向にとばす
        var rayDirection       = fpsCam.transform.forward.normalized;

        // Hitしたオブジェクト格納用
        RaycastHit raycastHit;

        // Rayを飛ばす(out raycastHit でHitしたオブジェクトを取得する)
        var isHit = Physics.Raycast(rayStartPosition, rayDirection, out raycastHit, distance);
        
        // Debug.DrawRay (Vector3 start(rayを開始する位置), Vector3 dir(rayの方向と長さ), Color color(ラインの色));
        Debug.DrawRay(rayStartPosition, rayDirection * distance, Color.red);
        
        // なにかを検出したら
        if (isHit)
        {
            // LogにHitしたオブジェクト名を出力
            Debug.Log("HitObject : " + raycastHit.collider.gameObject.name);
        }
    }
}

各行の解説をします。

どのカメラからRayをとばす?

    [SerializeField] Camera     fpsCam;             // カメラ

実際にFPSカメラとして機能しているカメラの座標をもとにRayを利用したいので、[SerializeField]としてCameraを用意します。

どれだけの距離を調べる?

[SerializeField] float distance = 0.8f; // 検出可能な距離

Rayを飛ばす距離をここで指定します。[SerializeField]にすることで、オブジェクトにアタッチした後もinspectorからの調整を容易にします。

Rayの開始地点

ここからUpdate関数の中身についてです。

 // Rayはカメラの位置からとばす
var rayStartPosition   = fpsCam.transform.position;

ここでは、Rayを利用するために必要な「Rayの開始地点座標」をFPSカメラから取得しています。

Rayの発射方向

// Rayはカメラが向いてる方向にとばす
var rayDirection       = fpsCam.transform.forward.normalized;

ここでは、Rayを開始点からどの方向に飛ばすかのベクトルをFPSカメラから取得しています。

方向だけわかればよいので、nomalizedを利用して正規化しています。

衝突オブジェクト用変数

// Hitしたオブジェクト格納用
RaycastHit raycastHit;

ここではRayが何かオブジェクトに衝突したとき、何に衝突したのかを格納しておく箱を用意しています。

Rayを発射して調べる

var isHit = Physics.Raycast(rayStartPosition, rayDirection, out raycastHit, distance);

Raycastを実際に利用している場所です。

各引数の意味は以下の通りです。

rayStartPosition ・・・ワールド座標でのレイの開始地点
rayDirection   ・・・レイの方向
out raycastHit  ・・・レイが衝突したオブジェクトの情報を取得
distance     ・・・レイの距離

そして、衝突したか否かの結果を「isHit」で受け取っています。

デバッグ用に色付きレーザー発射

// Debug.DrawRay (Vector3 start(rayを開始する位置), Vector3 dir(rayの方向と長さ), Color color(ラインの色));
Debug.DrawRay(rayStartPosition, rayDirection * distance, Color.red);

Raycastは不可視です。確認することができません。

そこで、デバッグ用にRaycastと同じ場所から、同じ方向、同じ距離にむかって色付きのレーザーを発射させて、確認できるようにしています。

もちろん、デバッグ用なので本番リリースの時には、この個所は削除してください。

衝突したオブジェクトを操作

// なにかを検出したら
if (isHit)
{
    // LogにHitしたオブジェクト名を出力
    Debug.Log("HitObject : " + raycastHit.collider.gameObject.name);
}

「isHit」には、このフレームで、RayにオブジェクトがHitしたかどうかが格納されているので、これで判定できます。

Hitしたオブジェクトには「raycastHit.collider.gameObject」から操作しましょう。

スクリプトをアタッチ

作成したスクリプトを、FPSコントローラーにアタッチしましょう。

アタッチしたら、Inspectorの「Fps Cam」にカメラをセットしましょう。

今回は、StandardAssetsのFPSControllerを使用しているので、

「FpsCam」には、Cameraコンポーネントを持っている、「FirstPersonCharacter」をセットしてください。

結果

デバッグ用のレーザーも出ていますし

Logには、Hitしたオブジェクトの名前が出力されています。

正常に動いています。

以上です。

コメント

タイトルとURLをコピーしました