Tag / Layer / LayerMask とは?
Unityでオブジェクトを扱っていると、「これは敵かどうか判定したい」「カメラに映すものを制限したい」「Raycastで特定の対象だけ拾いたい」といった場面が出てきます。
これらを実現するために用意されているのがTag、Layer、LayerMaskの3つです。
役割を一言でまとめると以下のようになります。
| 機能 | 役割 | 主な用途 |
|---|---|---|
Tag | オブジェクトを文字列で識別する | プレイヤーや敵の判定、Find系メソッド |
Layer | オブジェクトを整数で分類する | Cameraの描画制御、Physicsの衝突制御 |
LayerMask | 複数のLayerをビット表現で扱う | Physics.RaycastなどのAPI引数 |
どれも似ているように見えますが、それぞれ得意な領域があります。
どの場面でどれを使うのか、順を追って見ていきます。
Tagの使い方
TagはGameObjectを識別するための文字列です。
GameObject1つにつき1つのTagしか設定できません。
Tagの設定
TagはEdit → Project Settings → Tags and Layersから定義します。

定義したTagは、GameObjectのインスペクタ上部にあるドロップダウンから選択できます。

スクリプトからはgameObject.tagプロパティで取得できます。
using UnityEngine;
public class TagSample : MonoBehaviour
{
private void Start()
{
// Tagの値を取得
Debug.Log(gameObject.tag);
}
}
CompareTagでGCを回避する
Tagの比較で見かける典型的なコードがこちらです。
private void OnTriggerEnter(Collider other)
{
// 文字列比較。内部で文字列を取得しているのでGCが発生する
if (other.gameObject.tag == "Enemy")
{
Debug.Log("敵に触れた");
}
}
一見問題なさそうに見えますが、gameObject.tagはプロパティアクセスのたびにマネージド側の文字列を返すため、参照のたびにアロケーションが発生する可能性があります。
一方、CompareTag()は内部で最適化されていて、文字列を生成せずに比較してくれます。
private void OnTriggerEnter(Collider other)
{
// CompareTagを使うとアロケーションが発生しない
if (other.CompareTag("Enemy"))
{
Debug.Log("敵に触れた");
}
}
UpdateやOnTriggerStayのように毎フレーム呼ばれる箇所では、CompareTag()を使う癖をつけておくと安心です。
なお、CompareTag()はComponentにも実装されているので、other.CompareTag("Enemy")のようにColliderから直接呼び出せます。
Find系メソッドとの組み合わせ
TagはGameObject.FindWithTag()やGameObject.FindGameObjectsWithTag()の引数としても使えます。
// "Enemy"タグの付いたGameObjectを1つ取得
var enemy = GameObject.FindWithTag("Enemy");
// "Enemy"タグの付いたGameObjectをすべて取得
var enemies = GameObject.FindGameObjectsWithTag("Enemy");
ただしFind系の処理コストは高めなので、AwakeやStartで一度だけ実行する、SerializeFieldで参照を渡すなど、Update内での呼び出しは避けるのが基本です。
Layerの使い方
LayerはGameObjectを**整数(0〜31)**で分類する仕組みです。
文字列のTagと違い、CameraやPhysicsといったUnity内部の機能と密に連携しています。
Layerの設定
Tagと同じくEdit → Project Settings → Tags and LayersのLayersセクションから定義します。
定義可能なレイヤーは最大32個で、0〜7はDefault、TransparentFX、Ignore Raycast、Water、UIなどUnityが予約しています。
残りの枠(8〜31)に独自のレイヤーを登録していくことになります。
スクリプトからはgameObject.layerで取得・設定できます。
using UnityEngine;
public class LayerSample : MonoBehaviour
{
private void Start()
{
// レイヤー名からインデックスを取得して設定
gameObject.layer = LayerMask.NameToLayer("Enemy");
}
}
カメラのCulling Mask
CameraコンポーネントにはCulling Maskという項目があり、特定のLayerだけを描画対象にできます。

例えばミニマップ用のカメラには「ミニマップ用のアイコンだけ映す」「メインカメラには逆にそれを映さない」といった切り分けが可能です。
UI専用のカメラを別途用意するときも、Culling MaskをUIだけに絞るのがよくあるパターンです。
Physicsの衝突マトリクス
Project Settings → PhysicsのLayer Collision Matrixでは、レイヤー同士の衝突可否を一括設定できます。

- プレイヤーの攻撃判定は敵にだけ当てたい
- 味方同士はすり抜けたい
- アイテムは地形とだけ衝突させたい
このような「どのレイヤーとどのレイヤーが衝突するか」をスクリプトを書かずに制御できるのが強みです。
物理演算の負荷軽減にも効果があるので、レイヤー設計は早めにやっておくと後が楽になります。
LayerMaskの使い方
LayerMaskは複数のLayerを1つのintに詰め込んだもの**です。
Physics.RaycastやPhysics.OverlapSphereなど、Unityの多くのAPIがLayerMaskを引数に取ります。
ビットマスクの考え方
Layerは0〜31の整数なので、ちょうどint型(32bit)の各ビットに1対1で対応させられます。
例えばEnemyレイヤーが8番目だった場合、LayerMaskとしては以下のように表現します。
// 8番目のビットだけが1になる
int enemyMask = 1 << 8;
// 2進数で表すと: 0000_0000_0000_0000_0000_0001_0000_0000
この「1 << レイヤー番号」がビットシフト記法です。
ビットシフトでマスクを作る
レイヤー名から動的にマスクを作るには、LayerMask.NameToLayer()と組み合わせます。
using UnityEngine;
public class LayerMaskSample : MonoBehaviour
{
private void Start()
{
// Enemyレイヤーのビットだけが立ったマスク
int enemyMask = 1 << LayerMask.NameToLayer("Enemy");
// EnemyとBoss、両方のレイヤーを含むマスク(OR演算)
int targetMask = (1 << LayerMask.NameToLayer("Enemy"))
| (1 << LayerMask.NameToLayer("Boss"));
// Player以外すべてを対象にしたマスク(NOT演算)
int notPlayerMask = ~(1 << LayerMask.NameToLayer("Player"));
}
}
|(OR) で複数レイヤーを組み合わせる~(NOT) で指定レイヤー以外をすべて含める
この2つを押さえておけば大体のケースに対応できます。
LayerMask.GetMaskで楽に作る
文字列からマスクを直接組み立てたいときはLayerMask.GetMask()が便利です。
// EnemyとBossの両方を含むマスクを一発で取得
int targetMask = LayerMask.GetMask("Enemy", "Boss");
LayerMask.NameToLayer()は レイヤーのインデックス (存在しない場合は-1)を返すのに対し、LayerMask.GetMask()は マスク値(ビット列) を返します。
両者は返り値が全く違うので、混同しないよう注意しましょう。
Physics.Raycastでの活用
LayerMaskの代表的な使い道はPhysics.Raycastの対象フィルタリングです。
using UnityEngine;
public class RaycastSample : MonoBehaviour
{
[SerializeField] private LayerMask _targetMask;
private void Update()
{
// 前方に向かってRaycastし、targetMaskに含まれるLayerだけを対象にする
if (Physics.Raycast(transform.position, transform.forward, out RaycastHit hit, 10f, _targetMask))
{
Debug.Log($"ヒット: {hit.collider.name}");
}
}
}
LayerMask型のフィールドをSerializeFieldにすると、インスペクタ上ではチェックボックス形式のドロップダウンで指定できます。
これでコードに書かずインスペクタからフィルタ対象を変えられます。
あるGameObjectがマスクに含まれるか判定する
OnTriggerEnterなどで、相手のLayerがマスクに含まれているかをチェックしたい場合は、AND演算(&)を使います。
using UnityEngine;
public class TriggerSample : MonoBehaviour
{
// インスペクタから対象レイヤーを設定
[SerializeField] private LayerMask _targetMask;
private void OnTriggerEnter(Collider other)
{
// 相手のlayerをビット位置に変換し、マスクとAND演算
if ((_targetMask.value & (1 << other.gameObject.layer)) != 0)
{
Debug.Log("対象レイヤーに含まれる");
}
}
}
LayerMask型は内部でintのvalueを持っているので、mask.valueとしてアクセスします。
なお、Unity 6ではLayerMaskにIncludes()のような便利メソッドはまだ提供されていないので、上記のAND演算が定番の判定方法です。
使い分けの判断基準
どんなときに何を使うか、ざっくり表にまとめます。
| やりたいこと | 使うもの |
|---|---|
| 「これはプレイヤーか?」など種類の識別 | Tag |
| カメラの描画対象をまとめて制御 | Layer + Culling Mask |
| 物理衝突をレイヤー単位で制御 | Layer + Layer Collision Matrix |
RaycastやOverlapSphereで対象を絞り込む | LayerMask |
| 複数のレイヤーを1つの値で扱いたい | LayerMask |
判断の順番はこんな感じです。
- 「衝突や描画をまとめて制御したい」→
Layerを設計 - 「
RaycastなどのAPIにレイヤーを渡したい」→LayerMaskを作る - 「個別のオブジェクトをスクリプトで判定したい」→
Tagを使う
TagとLayerは併用できるので、「LayerはEnemy、TagはBoss」のように分けると、衝突制御と個別判定を両立できます。
気をつける点
Tagの文字列指定はタイプミスに弱い
Tagは文字列で指定するため、"Enemy"と"enemy"を間違えるとコンパイルエラーにならず、実行時まで気付けません。
定義されていないTagを指定するとUnityExceptionが発生します。
対策として、Tag名を定数化しておく方法があります。
public static class Tags
{
public const string Player = "Player";
public const string Enemy = "Enemy";
}
// 使用例
if (other.CompareTag(Tags.Enemy))
{
// ...
}
こうしておけば、タイプミスはコンパイル時に気付けます。
Layerは最大32個まで
Layerはint型のビットで管理されているため、32個が上限です。
そのうちUnityの予約レイヤー(Default、UI、Ignore Raycastなど)があるので、実質ユーザーが使えるのは24個前後となります。
規模の大きなプロジェクトでは早めにレイヤー設計を済ませて、無計画に追加しないようにしましょう。
NameToLayerとGetMaskを混同しない
この2つは返り値が違います。
// レイヤーのインデックス(例: 8)
int layerIndex = LayerMask.NameToLayer("Enemy");
// マスク値(例: 256 = 1 << 8)
int maskValue = LayerMask.GetMask("Enemy");
Physics.Raycastの引数にはマスク値が必要なので、NameToLayer()の結果をそのまま渡すと意図しないレイヤーが対象になります。
NameToLayer()の結果は必ず1 << ...でシフトしてから使うか、最初からGetMask()を使うようにしましょう。
存在しないレイヤー名を渡すと-1
LayerMask.NameToLayer()に存在しないレイヤー名を渡すと、例外ではなく**-1**が返ります。
これに気付かずビットシフトすると意図しないマスクになるので、初期化時にチェックしておくと安全です。
int layer = LayerMask.NameToLayer("Enemy");
if (layer < 0)
{
Debug.LogError("Enemyレイヤーが定義されていません");
}
まとめ
今回はTag、Layer、LayerMaskの役割と使い分けを見てきました。
Tagはオブジェクトを文字列で識別する。CompareTag()でGC回避が基本Layerはオブジェクトを整数で分類して、CameraやPhysicsと連携するLayerMaskは複数のLayerをビット表現でAPIに渡すための型
似た名前で混乱しがちですが、それぞれ得意な領域は別物です。
「識別ならTag、衝突や描画ならLayer、APIに渡すならLayerMask」と覚えておくと、迷う場面はかなり減ります。
最初にTagとLayerの設計を決めておくと、後のコードがぐっと書きやすくなります。プロジェクトの立ち上げ時に意識してみてください。




