Unityに関連する記事です

 Sprite 画像は SpriteMask 機能を利用することにより画像の切り抜き(マスク)処理が可能ですが、
この機能を応用して、下記のように、画像のサイズに合わせたシルエットを作成する実装例です。

 またシルエット用の画像の設定により、シルエットの色を変化させることも可能です。


動画ファイルへのリンク

 
 今回は Sprite に対してのマスク処理のため SpriteMask コンポーネントを利用していますが、
Image の場合には Mask コンポーネントを利用することで対応が可能です。



設計



 ゲームオブジェクトの構造は下記の通りです。




 EnemySet ゲームオブジェクトは CreateEmpty で作成している、フォルダ役のゲームオブジェクトです。
この中に SpriteRenderer コンポーネントを持つ画像表示用の Body ゲームオブジェクトがあり、
さらに今回作成する ReverseSprite ゲームオブジェクト、 SpriteMask ゲームオブジェクトがあります。



Body ゲームオブジェクトの作成


 通常の画像の設定用です。
シルエットにしたい元のゲームオブジェクトを作成します。

 SpriteRenderer コンポーネントをアタッチし、画像の優先順位を設定します。

 画像のサイズは、親のオブジェクトではなく、子オブジェクトである Body で調整します。

 また Mask Interaction の設定を行います。
Visible Outside Mask に設定してください。この設定にすることで画像を切り抜き、マスクする(隠す)ことができます。


 下記はサンプル画像です。


Scene ビュー画像



インスペクター画像




ReverseSprite ゲームオブジェクトの作成


 通常マスク処理をすると画像をくりぬく形になるため、その画像のあった背景には何も映りません。






 画像にシルエットを作って逆マスクをかけるには、専用のゲームオブジェクトが必要になります。

 このゲームオブジェクトは 2D → Square で四角いゲームオブジェクトを作成するか、
空のゲームオブジェクトに SpriteRenderer コンポーネントをアタッチして作成します。

 ゲームオブジェクトのサイズは、マスク処理する画像を覆うことが可能なサイズにします。

 画像には任意の1色の画像を設定します。
このときの画像の色がシルエットの色になりますので、赤い画像を設定すれば、赤いシルエットになります。

 黒のシルエットについては、赤などの元の色を黒くすることで対応可能であるため、
なるべく黒以外の色の画像で設定しておくと2色に変化可能です。

 また画像をマスクした際に表示されるようにしたいので、元の画像よりも表示の優先順位を低く設定しておきます。

 最も重要なのは Mask Interaction の設定です。
Visible Inside Mask に設定してください。この設定にすることでシルエットをつけることができます。

 下記はサンプル画像です。ここでは赤の画像を利用しています。







 色を黒くすると、黒い画像になります。








 元の画像よりも優先順位が低くなっていること、元の画像よりも大きなサイズになっていることを確認しておきましょう。


SpriteMask ゲームオブジェクトの作成


 マスク処理をかけるためのゲームオブジェクトを作成します。
Create Empty で空の子オブジェクトを作成し、SpriteMask コンポーネントをアタッチします。

 なお、マスク処理する画像用のゲームオブジェクトの子オブジェクトにする必要はありません。







 この時点で、先に作成している2つのゲームオブジェクトの SpriteRenderer 内の Mask Interaction の設定が正しい場合
自動的にシルエットがかかる形でマスク処理が実行されます。

 うまくいかない場合には、前に作った2つのゲームオブジェクトの SpriteRenderer の設定を見直してください。




 基本的にはゲーム内で動的にシルエットを出すケースが多いと思われますので、
最初は SpriteMask コンポーネントの Enabled を false に設定してマスク処理を停止させておくとよいでしょう。

 逆にシルエット状態から利用したい場合には、そのまま Enebled を入れておきましょう。






動作確認を行う


 以上で完成です。

 下記はサンプルコードです。
ボタンを押すたびに SpriteMask の Enebled が切り替わり、シルエットのオンオフを確認することができます。


SpriteMaskController.cs
using UnityEngine;

public class SpriteMaskController : MonoBehaviour
{
    [SerializeField]
    private SpriteMask maskComponent;

    private void Update()
    {
        // null チェックを行い、maskComponentがアサインされていない場合は処理を行わない
        if (maskComponent == null)
        {
            Debug.LogError("SpriteMaskコンポーネントがアサインされていません。");
            return;
        }

        // スペースキーが押されたら
        if (Input.GetKeyDown(KeyCode.Space))
        {
            // SpriteMaskコンポーネントのenabledプロパティをトグル
            ToggleSpriteMask();
        }
    }

    /// <summary>
    /// SpriteMaskコンポーネントのenabledプロパティの切り替え
    /// </summary>
    private void ToggleSpriteMask()
    {
        // SpriteMaskのenabledプロパティを反転
        maskComponent.enabled = !maskComponent.enabled;
    }
}


<サンプルコード 


 正常に動作することを確認できたら、実際にゲーム内の処理をイメージして実装方法を検討してみてください。

 下記はサンプルコードです。

 この処理では指定したボタンを押すことにより、マスクをかけてシルエット状態にします。
その後、シルエット状態のまま、シルエットの色を一定時間ごとに元の色と黒色とで交互に入れ替えることで点滅状態を作ります。
点滅後、マスクを解除して元の画像に戻します。

 例えば、ダメージを受けた際の処理などが想定されるケースです。


SilhouetteEffectController.cs
using System.Collections;
using UnityEngine;

public class SilhouetteEffectController : MonoBehaviour
{
    [SerializeField]
    private SpriteMask spriteMask;

    [SerializeField]
    private SpriteRenderer spriteRenderer;

    [SerializeField]
    private KeyCode activationKey = KeyCode.Space;

    [SerializeField]
    private float blinkInterval = 0.2f;   // 1回あたりの点滅の時間

    [SerializeField]
    private int blinkCount = 2;   // 点滅回数

    private bool isProcessing = false; // 点滅中かどうかの確認フラグ。true なら点滅中


    private void Update()
    {
        // 点滅中でない場合で、かつ指定したキーが押されたら
        if (!isProcessing && Input.GetKeyDown(activationKey))
        {
            // シルエット処理を開始
            StartCoroutine(ActivateSilhouette());
        }
    }

    /// <summary>
    /// シルエット処理
    /// </summary>
    /// <returns></returns>
    private IEnumerator ActivateSilhouette()
    {
        // 点滅中のフラグをたてる
        isProcessing = true;

        // シルエット用のマスクを有効化
        spriteMask.enabled = true;

        // 点滅処理。yield return 付きの StartCoroutine とすることで、点滅処理が終わるまで次の処理に移らないようにしている
        yield return StartCoroutine(BlinkEffect());

        // 点滅処理が終わったのでシルエット用のマスクを無効化
        spriteMask.enabled = false;

    // 点滅が終わったのでフラグをおろす
        isProcessing = false;
    }

    /// <summary>
    /// 点滅処理
    /// </summary>
    /// <returns></returns>
    private IEnumerator BlinkEffect()
    {
        for (int i = 0; i < blinkCount; i++)
        {
            // 元の色から黒色に変更
            spriteRenderer.color = Color.black;
            yield return new WaitForSeconds(blinkInterval);

            // 黒色から元の色に変更
            spriteRenderer.color = Color.white;
            yield return new WaitForSeconds(blinkInterval);
        }
    }

  /// <summary>
  /// シルエット効果を外部から実行可能なラップ用メソッド
  /// </summary>
  public void StartSilhouetteEffect()
  {
      if (!isProcessing)
      {
          StartCoroutine(ActivateSilhouette());
      }
  }
}
 
 外部クラスからは直接コルーチンメソッドを実行しないようにします
コルーチンメソッドは、実行元のゲームオブジェクトがゲーム内に残っている必要があるため、
万が一、実行後に実行元のゲームオブジェクトが破棄されてしまうと、コルーチンメソッドが停止してしまいます。

 よって外部クラスからはラップ用のメソッドを実行し、そのクラス内で自分でコルーチンメソッドを実行させます。

 このような設計にしておくことで安全にコルーチンメソッドを実行することが可能になります。


<サンプルコード◆


 上記のサンプルコード,鬟灰襦璽船鵐瓮愁奪匹任呂覆、
UniTask と async / await を利用した非同期処理に置き換えてリファクタリングしたものです。


SilhouetteEffectController.cs
using Cysharp.Threading.Tasks;
using System.Threading;
using UnityEngine;

public class SilhouetteEffectController : MonoBehaviour
{
    [SerializeField]
    private SpriteMask spriteMask;

    [SerializeField]
    private SpriteRenderer spriteRenderer;

    [SerializeField]
    private KeyCode activationKey = KeyCode.Space;

    [SerializeField]
    private float blinkInterval = 0.2f;   // 1回あたりの点滅の時間

    [SerializeField]
    private int blinkCount = 2;   // 点滅回数

    private CancellationTokenSource cts;
    private bool isProcessing = false; // 点滅中かどうかの確認フラグ。true なら点滅中


    private void Start()
    {
        cts = CancellationTokenSource.CreateLinkedTokenSource(this.GetCancellationTokenOnDestroy());
    }

    private async void Update()
    {
        // 点滅中でない場合で、かつ指定したキーが押されたら
        if (!isProcessing && Input.GetKeyDown(activationKey))
        {
            // シルエット処理を開始
            await ActivateSilhouetteAsync();
        }
    }

    /// <summary>
    /// シルエット処理
    /// </summary>
    /// <returns></returns>
    public async UniTask ActivateSilhouetteAsync()
    {
        // 点滅中のフラグをたてる
        isProcessing = true;

        // シルエット用のマスクを有効化
        spriteMask.enabled = true;

        // 点滅処理
        await BlinkEffectAsync();

        // 点滅処理が終わったのでシルエット用のマスクを無効化
        spriteMask.enabled = false;

        // 点滅が終わったのでフラグをおろす
        isProcessing = false;
    }

    /// <summary>
    /// 点滅処理
    /// </summary>
    /// <returns></returns>
    private async UniTask BlinkEffectAsync()
    {
        for (int i = 0; i < blinkCount; i++)
        {
            // 元の色から黒色に変更
            spriteRenderer.color = Color.black;
            await UniTask.Delay((int)(blinkInterval * 1000), cts.Token);

            // 黒色から元の色に変更
            spriteRenderer.color = Color.white;
            await UniTask.Delay((int)(blinkInterval * 1000), cts.Token);
        }
    }

    private void OnDestroy()
    {
        cts?.Cancel();
        cts?.Dispose();
    }
}

 UniTask を利用した非同期処理は外部クラスから直接実行しても問題ないため、ラップ用のメソッドが不要になっています。
代わりに UniTask による非同期処理はゲームオブジェクトの生存期間と紐づかないため、ゲームオブジェクトが破棄されたタイミングでも処理は停止しません。

 このような特性により、手動でキャンセル処理に対応する必要があるため、CancellationTokenSource を利用してキャンセル用のトークンを作成し、
OnDestroy メソッドにて非同期処理を停止しています。


<参考サイト>


テラシュールブログ 様
【Unity】SpriteMaskでSpriteにマスクを掛ける演出が色々と面白い
https://tsubakit1.hateblo.jp/entry/2017/07/21/0127...

コメントをかく


「http://」を含む投稿は禁止されています。

利用規約をご確認のうえご記入下さい

Menu



技術/知識(実装例)

2Dおはじきゲーム(発展編)

2D強制横スクロールアクション(発展編)

3Dダイビングアクション(発展編)

2Dタップシューティング(拡張編)

レースゲーム(抜粋)

2D放置ゲーム(発展編)

3Dレールガンシューティング(応用編)

3D脱出ゲーム(抜粋)

2Dリアルタイムストラテジー

2Dトップビューアドベンチャー(宴アセット使用)

3Dタップアクション(NavMeshAgent 使用)

2Dトップビューアクション(カエルの為に〜、ボコスカウォーズ風)

VideoPlayer イベント連動の実装例

VideoPlayer リスト内からムービー再生の実装例(発展)

AR 画像付きオブジェクト生成の実装例

AR リスト内から生成の実装例(発展)

private



このサイト内の作品はユニティちゃんライセンス条項の元に提供されています。

管理人/副管理人のみ編集できます