丸い爪でも立ててみる

投稿日:2024/01/02 更新日:2024/01/02

回 Unityで調べられるアイテムを強調表示したい【UnityFx Outline】

初めまして、すらです。新年あけましておめでとうございます。

 

現在一人称のホラーゲームを作成していますが、今回はプレイヤーが視点を合わせた際に、オブジェクトにアウトラインをつけてみたいと思います。

調べられるものに対してはアウトラインを表示させたり画面にアイコンを出してあげたり、何かしらのアクションをしてあげるのがユーザーに優しい実装だと思いますので、

今回のゲームではアウトラインにしてみたいと思います。

アウトラインの表示に関してはパッケージをインストールして簡単に出来ました。

私の環境がURPなので、申し訳ないですがURP環境での解説しかできません。ご了承ください。

 

環境

Unity : 2022.3.14f1

project : URP

 

 

パッケージのインストール

まずはUnityFx Outlineというパッケージをインストールしましょう。

編集→プロジェクト設定で設定窓を出します。

 

パッケージマネージャーの項目を選択してください。

名前 Arvtesh

URL https://registry.npmjs.org

Scope(s) com.unityfx

それぞれ入力して保存をしてください。

 

続いてパッケージマネージャーを開きます。

 

マイレジストリへ変更してください。

 

先ほどプロジェクト設定で入力したことにより、Outline toolkitというものが追加されています。

Outline toolkit(URP)を選択してインストールしてください。

(私はインストール済みなので削除ボタンが表示されています。)

Outline toolkit(URP)をインストールすると自動的にOutline toolkitもインストールされます。

 

アイテムにアウトラインを表示させる

続いてレイヤーの設定をしていきます。

このあと追加するレイヤーをオブジェクトに設定することによってアイテムが強調表示(アウトライン)されるようになります。

右上のレイヤーをクリックしてレイヤー編集画面に移動します。

 

好きな場所にレイヤーを登録してください。

私はOutlineという名前で登録しました。

 

登録できたら再びプロジェクト設定を開いてください。

グラフィックスという項目を選択してください。

スクリプタブルレンダーパイプライン設定に登録されているものをクリックすると設定されているデータが格納されているフォルダへ移動してくれるので、

設定されているものと同じ名前のものを選択してください。

 

Add Renderer Featureをクリックして、Outline Featureを選択すると、Outlineコンポーネントが追加されます。

(画像すでに追加済み)

 

コンポーネントの設定をしていきます。

Nameではコンポーネント名を変更できます。

Outline Filter SettingをUse Layer Maskにして、Layer Maskを先ほど登録したレイヤーにしてください。

Outline Settingではアウトラインの色や太さを設定できます。

 

ここまでできたら、あとはアウトラインを表示させたいオブジェクトのレイヤーを、

登録したレイヤーにしてあげればアウトラインを表示することができます。

 

左2つの戸棚がアウトライン表示されるようになりました!

もし常にアウトラインを表示したい場合であれば、これでOKですね。

 

 

プレイヤーが見ているときにだけアウトラインを表示させたい

私の場合はプレイヤーが戸棚を見ているときにアウトラインを表示させたいので、さらに設定をしていく必要があります。

ここから先はスクリプトを作成して動作を実装していきます。

私の実装コードを載せますが、各々環境が違ってくると思いますのであくまでも参考程度に。

スクリプトは自分の環境に合わせて適宜書き換えてください。

 

まず、プレイヤーが見ているときというのはRaycastを使って実装しました。

Raycastは透明なビームのようなものでビームに当たったオブジェクトを取得できるといったものです。

プレイヤーカメラ(ゲームカメラ)の中心からビームを飛ばして、「当たったオブジェクト=プレイヤーが見ているオブジェクト」といったわけですね。

シーンに空のオブジェクト「RayManager」を作成して以下のスクリプトをアタッチしました。

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


public class RayManager : MonoBehaviour
{
    private Vector3 mousePosition;
    public Camera playerCamera;
    private float maxRaycastDistance = 1.5f;
    private Ray ray;
    private RaycastHit hitObj;
    public GameObject hitObject;
    
  void Start()
    {
       
    }

    void Update()
    {
        // マウスの位置を取得
        mousePosition = Input.mousePosition;
        // マウスの位置(画面中央)からRayを発射
        ray = playerCamera.ScreenPointToRay(mousePosition);

        // 今見ているものを取得(maxRaycastDistanceの範囲内で)
        if(Physics.Raycast(ray, out hitObj, maxRaycastDistance))
        {
            hitObject = hitObj.collider.gameObject;
        }
        else
        {
            hitObject = null;
        }
    }
}
public Camera playerCamera;

こちらはインスペクターからプレイヤーのカメラをアタッチします。

コードの内容ですが、Raycastを使用してhitObjectにプレイヤーが見ているオブジェクトが格納されるようになっています。

Raycast型で渡すよりもGameObject型で渡す方が使いやすいと思ったのでこのスクリプト内で変換しています。

続いてシーンに空のオブジェクト「InteractManager」を作成して以下のスクリプトをアタッチしました。

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


public class InteractManager : MonoBehaviour
{
    public GameObject currentDrawer;
    public RayManager _rayManager;
    private GameObject raycastHit;
    

    void switchLayer(GameObject obj, string layerName)
    {
        obj.layer = LayerMask.NameToLayer(layerName);
    }

    void resetCurrentDrawer()
    {
        if (currentDrawer != null)
        {
            switchLayer(currentDrawer, "Default");
            currentDrawer = null;
        }
    }
    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
        raycastHit = _rayManager.hitObject;
        if (raycastHit != null && raycastHit.tag == "drawers")
        {
            resetCurrentDrawer();
            currentDrawer = raycastHit;
            switchLayer(currentDrawer, "Outline");
        }
        else
        {
            resetCurrentDrawer();
        }  
    }
}
public RayManager _rayManager;

こちらはインスペクターからRayManagerがアタッチされているゲームオブジェクトをアタッチします。

(さきほど追加した空のオブジェクト「RayManager」ですね)

こうすることによって、RayManagerで宣言した変数の値を参照することができます。

コードの内容としてはRayManagerで取得したゲームオブジェクト(raycastHit)のタグが”drawers”だったら、currentDrawerにゲームオブジェクトを代入。

currentDrawerが空じゃなかったらcurrentDrawerのレイヤーをOutlineに変更。

currentDrawerが空、もしくは取得したゲームオブジェクトのタグがdrawersじゃなかったらレイヤーをDefaultに戻しています。

アウトラインを表示させたいオブジェクトのタグをdrawersに設定して、レイヤーをDefaultに戻しておきます。

これで設定は完了です。

この状態でゲームを実行してみます。

無事に見ているときにアウトラインを表示でき、視線を外すとアウトラインが非表示になりました!

あとはクリックやEキーなどを押したときに、プレイヤーが見ているdrawerオブジェクト(currentDrawer)に対して、回転をさせてあげれば扉が開いたようになりますね。

そのあたりの解説は別の機会にしたいと思います。

 

すらでした。

 


記事のコメント

  1. コメント失礼します。
    Outlineに近づくと手に持っているアイテムに被って表示されてしまいます。
    アイテムの上にラインが表示されるのを防ぐことは可能ですか?

    1. こちらの問題に関してはパッケージ配布元のgithubでも取り上げられているようです。
      制作者はOutlineコンポーネント(この記事内ではOutline feature)のRender FlagsをEnable Depth Testingへ変更すると、オブジェクトが重なった際にアウトラインが貫通しないようになると説明していますが、私の環境ではRender Flagsを変更するとレンダリングが壊れました(画面真っ暗)
      対応策としてパッケージに含まれるOutlinePass.csの記述を変更してあげると改善されるという方法が上がっていますが、2022.3.14f1の私の環境ではレンダリングが壊れることはなくなりましたが、アウトラインが表示されず解決には至りませんでした。
      https://github.com/Arvtesh/UnityFx.Outline/issues/74
      こちらが詳細です。

      Matrix64という方がサンプルプロジェクトをアップロードしており、これで確認してみると確かにオブジェクトが重なったときにアウトラインが貫通しなくなっていました。
      ただ、こちらのプロジェクトは2022.3.18f1(以上の)環境が必要です。
      環境の途中変更はリスキーな作業ですが、どうしても貫通するのが気になる場合は検討してみてください。

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)