Help us understand the problem. What is going on with this article?

uGUIとParticleSystemでタップエフェクトを作る

More than 1 year has passed since last update.

はじめに

uGUIのOverlay設定のCanvasを使ってタップエフェクトを表示したかったので、サンプルを作りました。

今回のコードを下記に置きました。
https://github.com/jnhtt/uGUI-TapEffect

こんな感じになります。
赤はタップエフェクトより手前で、緑はタップエフェクトより後ろに設定しています。
CanvasSortOrderで設定できます。

TapEffect.gif

OverlayのCanvasでタップエフェクトを表示できると表示順番の制御が簡単で助かります。

概要

Canvas.sortingOrderで表示順番を設定します。
大きい値を設定するほど手前に表示されます。
タップエフェクトは4個をプールして使い回します。

準備

LayerにUITapEffectを追加し、タップエフェクト(下のParticleSystem)のレイヤーとエフェクト用カメラ(TapCamera)のMaskCullingをUITapEffectに設定します。

スクリーンショット 2018-11-12 15.00.48.png

処理の流れ

タップエフェクトは
処理の流れは単純です。

  1. RenderTextureを生成して、TapCameraのTargetTextureに設定
  2. 画面を覆うRawImageTextureに 1で生成したRenderTextureを設定
  3. Input.mousePositionorInput.GetTouch(0).positionでクリックした位置をScreen座標で取得
  4. ワールド座標でのエフェクト表示位置を計算し、エフェクトを表示
  5. RawImageに反映

1と4を説明します。

RenderTextuer

SCALERenderTextureのサイズを用途に合わせて調節します。
カメラとRawImageRenderTextureをすることで画面に表示されます。

RenderTexture生成
        public void Init()
        {
            renderTexture = new RenderTexture((int)(Screen.width * SCALE), (int)(Screen.height * SCALE), 0);
            renderTexture.name = "TapEffectRenderTexture";
            tapCamera.targetTexture = renderTexture;
            rawImage.texture = renderTexture;
        }
RenderTexture破棄
        private void OnDestroy()
        {
            if (renderTexture != null) {
                Destroy(renderTexture);
            }
        }

生成と破棄は対にして実装します。

エフェクトの位置

SCALEを掛けてターゲットサイズに合わせます。
EFFECT_Zでカメラとの距離を調整できます。near/farの範囲内になるようにします。

エフェクトの表示位置
var pos3 = tapCamera.ScreenToWorldPoint(pos * SCALE + tapCamera.transform.forward * EFFECT_Z);

コード

今回はコードが少ないので全部載せします。

TapEffectController.cs
using UnityEngine;
using UnityEngine.UI;


public class TapEffectController : MonoBehaviour
{
    private const float SCALE = 0.5f;
    private const float EFFECT_Z = 500f;
    private const float INV_SCALE = ((float)1 / SCALE);

    public Canvas canvas;
    public CanvasGroup canvasGroup;
    public Camera tapCamera;
    public TapEffect tapEffect;
    public RawImage rawImage;

    private RenderTexture renderTexture;

    public RenderTexture RT { get { return renderTexture; } }

    private void Awake()
    {
        GetComponent<Canvas>().sortingOrder = 5000;
        Init();
    }

    public void Init()
    {
        renderTexture = new RenderTexture((int)(Screen.width * SCALE), (int)(Screen.height * SCALE), 0);
        renderTexture.name = "TapEffectRenderTexture";
        tapCamera.targetTexture = renderTexture;
        rawImage.texture = renderTexture;
    }

    public void Play(Vector3 pos)
    {
        tapEffect.Play(pos);
    }

    private void Update()
    {
        Vector2 pos = Vector2.zero;
        bool flag = false;
#if UNITY_EDITOR
        if (Input.GetMouseButtonDown(0)) {
            flag = true;
            pos = Input.mousePosition;
        }
#else
            if (Input.touchCount > 0) {
                flag = true;
                 pos = Input.GetTouch(0).position;
            }
#endif
        if (flag) {
            PlayEffect(pos);
        }
    }

    private void PlayEffect(Vector3 pos)
    {
        var pos3 = tapCamera.ScreenToWorldPoint(pos * SCALE + tapCamera.transform.forward * EFFECT_Z);
        Play(pos3);
    }

    private void OnDestroy()
    {
        if (renderTexture != null) {
            Destroy(renderTexture);
        }
    }
}
TapEffect.cs
using System.Collections.Generic;
using UnityEngine;

public class TapEffect : MonoBehaviour
{
    public Camera tapCamera;
    public List<Effect> effectList;

    public void Play(Vector3 pos)
    {
        var eff = GetEffect();
        if (eff != null) {
            eff.transform.position = pos;
            eff.Play();
        }
    }

    public Effect GetEffect()
    {
        foreach (var eff in effectList) {
            if (!eff.IsPlaying()) {
                return eff;
            }
        }

        return null;
    }
}
Effect.cs
using UnityEngine;

public class Effect : MonoBehaviour
{
    [SerializeField]
    private ParticleSystem particleEffect;
    [SerializeField]
    private bool isBillboard;

    public void Play()
    {
        if (particleEffect != null) {
            particleEffect.Play();
        }
    }

    public void Stop()
    {
        if (particleEffect != null) {
            particleEffect.Stop();
        }
    }

    public bool IsPlaying()
    {
        if (particleEffect != null) {
            return particleEffect.isPlaying;
        }
        return false;
    }

    private void Update()
    {
        if (isBillboard) {
            transform.rotation = Camera.main.transform.rotation;
        }
    }
}

さいごに

タップエフェクトを表示することができたと思います。
Effect.csをアタッチして再生できるエフェクトであればタップエフェクトを入れ替えることができます。

OverlayのCanvasを使うことで画面の一番手前に持ってくることも、UIの間に入れることもできます。
RenderTextureのサイズ分だけメモリを食ったり、タップエフェクト用カメラを管理する必要があったりデメリットもありますが、個人的には、分かりやすさと使いやすさでお釣りが来ると思います。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした