コガネブログ

平日更新を目標にUnityやC#などのゲーム開発アレコレを書いていきます

【Unity】HasFlag 関数を Unity でも使用できるようにする拡張メソッド

[Flags]
private enum JobType // ジョブの種類
{
    NONE      = 1 << 0, // 無効
    SOLDIER   = 1 << 1, // 王国兵士
    SORCERER  = 1 << 2, // 魔法使い
    HUNTER    = 1 << 3, // 狩人
    MERCENARY = 1 << 4, // 傭兵
}

private void Awake()
{
    var jobs = JobType.SOLDIER;
    
    // フラグを立てる
    jobs |= JobType.SORCERER;
    
    // フラグが立っているかどうかを確認する
    if ( ( jobs & JobType.SOLDIER  ) == JobType.SOLDIER &&
         ( jobs & JobType.SORCERER ) == JobType.SORCERER )
    {
        Debug.Log( "王国兵士かつ魔法使いです" );
    }
}

C#では Flags 属性を列挙型に適用することで
その列挙型でビット演算によるフラグ管理を行えるようになります

ただし、フラグが立っているかどうかを確認するための処理が冗長になりがちです

// フラグが立っているかどうかを確認する
if ( ( jobs & JobType.SOLDIER  ) == JobType.SOLDIER &&
     ( jobs & JobType.SORCERER ) == JobType.SORCERER )
{
    Debug.Log( "王国兵士かつ魔法使いです" );
}

.NET Framework 4.0 以降であれば HasFlag 関数を使用して

// フラグが立っているかどうかを確認する
if ( jobs.HasFlag( JobType.SOLDIER | JobType.SORCERER ) )
{
    Debug.Log( "王国兵士かつ魔法使いです" );
}

このように書くことができますが、
Unity は .NET Framework 3.5 までの機能しか使用できないため HasFlag 関数は使用できません

なので僕は Unity + C# でゲーム開発する時に HasFlag 関数を使用したい場合は
下記のような拡張メソッドを用意して使っています

using System;

/// <summary>
/// Enum 型の拡張メソッドを管理するクラス
/// </summary>
public static class EnumExtensions
{
    /// <summary>
    /// 現在のインスタンスで 1 つ以上のビット フィールドが設定されているかどうかを判断します
    /// </summary>
    public static bool HasFlag( this Enum self, Enum flag )
    {
        if ( self.GetType() != flag.GetType() )
        {
            throw new ArgumentException( "flag の型が、現在のインスタンスの型と異なっています。" );
        }
    
        var selfValue = Convert.ToUInt64( self );
        var flagValue = Convert.ToUInt64( flag );

        return ( selfValue & flagValue ) == flagValue;
    }
}
[Flags]
private enum JobType // ジョブの種類
{
    NONE      = 1 << 0, // 無効
    SOLDIER   = 1 << 1, // 王国兵士
    SORCERER  = 1 << 2, // 魔法使い
    HUNTER    = 1 << 3, // 狩人
    MERCENARY = 1 << 4, // 傭兵
}

private void Awake()
{
    var jobs = JobType.SOLDIER;
    
    // フラグを立てる
    jobs |= JobType.SORCERER;
    
    // フラグが立っているかどうかを確認する
    if ( jobs.HasFlag( JobType.SOLDIER | JobType.SORCERER ) )
    {
        Debug.Log( "王国兵士かつ魔法使いです" );
    }
}

c# - Generic extension method to see if an enum contains a flag - Stack Overflow
Enum HasFlag method extension for < 4.0 Framework