4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

扇形の当たり判定をつけて、リアルな視界を作る

Last updated at Posted at 2022-05-08

今回はFPSでの敵キャラの設定などで使えそうな、扇形の当たり判定でリアルな人間の視界を作る実装について記事を書いていきます!

使用PC: MacBook Pro (13-inch, M1, 2020)
OSバージョン: 11.5.2
Unityバージョン:2020.3.30f

完成形

画面収録-2022-05-07-18.28.36 (1).gif

動画は若干わかりにくいですが、視界の一定範囲にターゲットが入った場合にDebug.Logを出力するような機能になっています。視界範囲内に障害物があった場合は判定されません。

概要

qiita111.png

グレーのような扇形の部分を視界の範囲とし、ターゲットとなるGameObjectがそこに入ったら当たり判定を判別する仕組みを作ろうと思います!

構成内容は

①視界の範囲内にいるかどうかの判定
②視界の角度内にいるかどうかの判定
③視界の範囲内にターゲットが存在するかどうかの判定

の3つの構成です!

ちょこっと詳しく説明

①視界の範囲内にいるかどうかの判定

探す人の直線距離内、つまりどこまで遠くまで見れるかどうかを判別します。
この中に入らないとターゲットは見つけられないということになります。
⏬この画像では黄色の範囲内ですね!
uiuiui.png

②視界の角度内にいるかどうかの判定

探す人の視界の角度を扇形で指定することで、見える範囲に存在するかどうかの判定を行います。
Rotationが変われば、それに応じて判定される扇も回転します。
ううう.png

③視界の範囲内にターゲットが存在するかどうかの判定

途中で障害物などがあったときに、視界やその角度の範囲内にいたとしても直線上に他のオブジェクトで遮られていた場合は判定することができません。

下準備

オブジェクトの配置

Player, Ground, Wall, Targetの4つのオブジェクトを作成します。
今回はPlayerをCyllinder、GoundをPlane、WallをCube、TargetをSphereで作りました。

スクリーンショット 2022-05-07 20.06.29.png

わかりやすいようにMaterialで色もつけました。

MaterialはProjectビューCreateMaterialで作成します。

スクリーンショット 2022-05-07 20.40.00.png

コライダーの設定

①Player, Wall, TargetにはRigidbodyをAdd Componentしてください。
スクリーンショット 2022-05-08 16.16.26.png

②PlayerにSphereColliderをつけて、Radiusなどをいじっていい感じに大きさや位置を調整します。
この後このSphereColliderが自分の視界の範囲内にいるかどうかを判定するエリアになります。
画面収録-2022-05-07-18.28.36_1.gif

③Sphere Colliderのis Triggerにチェックを入れます。
スクリーンショット 2022-05-08 16.26.14.png

Targetにタグ付けを行う

①TargetのオブジェクトにInspectorビューからAdd Tagを選択します。
スクリーンショット 2022-05-08 16.28.34.png

②+ボタンからtargetと入力してsave。
スクリーンショット 2022-05-08 16.28.53.png

③もう一度TargetオブジェクトのInspectorビューに戻り、さっき加えたtargetタグを付与します。
スクリーンショット 2022-05-08 16.29.10.png

スクリプトを作成

ProjectビューCreateC# Scriptでスクリプトを作成します。
スクリーンショット 2022-05-08 16.33.51.png

②名前をFun Searchとか好きな名前にします。
スクリーンショット 2022-05-08 16.34.05.png

③Playerオブジェクトにスクリプトを紐付け。
スクリーンショット 2022-05-08 16.36.03.png

コードを書く

👇コードの全文はこちら

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

public class FunSearch : MonoBehaviour
{
    public float angle = 45f;

    private void OnTriggerStay(Collider other)
    {
        if (other.gameObject.tag == "target") //視界の範囲内の当たり判定
        {
            //視界の角度内に収まっているか
            Vector3 posDelta = other.transform.position - this.transform.position;
            float target_angle = Vector3.Angle(this.transform.forward, posDelta);

            if (target_angle < angle) //target_angleがangleに収まっているかどうか
            {
                if(Physics.Raycast(this.transform.position, posDelta, out RaycastHit hit)) //Rayを使用してtargetに当たっているか判別
                {
                    if (hit.collider==other)
                    {
                        Debug.Log("range of view");
                    }
                }
            }
        }
    }
}

コードの解説

概要の部分の①、②、③の順番で条件のコードを記述していきます。

変数宣言

public float angle = 45f;

視界の範囲を45度として、変数として宣言しておきます。

視界の角度内に収まっているかどうかの判定については後述のコードの中で行います。

視界の範囲を設定

 private void OnTriggerStay(Collider other)
    {
        if (other.gameObject.tag == "target")

先ほどSphere Colliderのis Triggerにチェックを入れているので、Triggerモードで当たり判定を取れているものを教えてくれる関数を書きます。
今回はタグを利用するので、引数にあるother、つまり「当たった側の情報であるtagがtargetだった場合」という条件をif文で記述します。

角度の範囲内か確認

 Vector3 posDelta = other.transform.position - this.transform.position;
 float target_angle = Vector3.Angle(this.transform.forward, posDelta);

if (target_angle < angle)
            {

Playerの正面に対して、Targetの位置を取得してそれが角度が45度以内かどうか算出します。

qiita111.png

まずは位置のベクトルを引き算したものを表します。これを出すことでTargetに対するベクトルを出すことができます。

 Vector3 posDelta = other.transform.position - this.transform.position;

相手側のポジションと自分のポジションの差分をposDeltaとします。
中学数学のベクトルの考え方と同じですね!
Frame 4.pngFrame 3.png

float target_angle = Vector3.Angle(this.transform.forward, posDelta);

ここでPlayerの正面とposDeltaのなす角度をここで測ります。
Vector3.Angle()は角度取得を簡単にできるメソッドです。自分の正面のベクトルをとるthis.transform.forwardに対して、第2引数にposDeltaを入れることで正面に対して何度の角度かを得ることができます!

if (target_angle < angle)

if文の中に「算出したtarget_angleが変数宣言で指定しているangleに収まっているかどうか」という条件文を書きます。

障害物を判定する

PlayerからTargetに対してRayを飛ばし、その間に「別のものが当たり判定で取られないかどうか」確認する動作を行なっていきます。

Ray とは、直訳すると「光線」という意味です。
シーン上の指定した地点から光線を飛ばし、光線上にあるコライダーの情報を取得するのに用いられます。

Physics.Raycast(Rayの開始位置, Rayの方向, 衝突したオブジェクトの情報, Rayの長さ);

といったように使います。いくつか要素がありますが、必要な要素はRayの開始位置Rayの方向のみです。

今回は以下のように記しました。

 if(Physics.Raycast(this.transform.position, posDelta, out RaycastHit hit))

第1引数にはthis.transform.position、第2引数にはposDeltaを入れます。
すでにVector3でベクトルを作っているので、今回はposDeltaを使用します。

また、out RaycastHit hithitは何があたったかという情報を持っています。

if (hit.collider==other)
     {
        Debug.Log("range of view");
     }    

RaycasthitのColliderを使って、あたった相手側である引数otherに当たったかどうかのチェックを行います。

以上!!

実際にunityを動かしてみると、前方向一定の角度内に入っている場合のみにDebug.Logが表示されるはずです。

参考

https://www.youtube.com/watch?v=sM5eB4I1JwA
http://physics.thick.jp/Physical_Mathematics/Section3/3-1.html
https://nekojara.city/unity-vector-angle
https://rightcode.co.jp/blog/become-engineer/unity-raycast-ray
https://docs.unity3d.com/ja/current/ScriptReference/Physics.Raycast.html

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?