0
0

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.

ParticleSystemの粒子のアルファをスクリプトで変更

Last updated at Posted at 2023-03-11

2023/03/11 : 初稿
2023/03/13 : コード修正
Unity : 2021.3.15f1

やりたいこと

Unityで再生中のParticleSystemに対して、
スクリプトでアルファを変化させたい。

実現方法

ググると大体
ParticleSystem.main.startColorをいじる記事が出ますが、
どうも新たに放出される粒子には効くものの、
既存の粒子には影響なさそう。

粒子の寿命が短いならそれでもいいんですが、
今すぐ、パーティクル全体をフェードイン/アウトさせたい!
といった場合に対処しづらい。

なので、colorOverLifetimeの方をいじることにしました。
(大体のパーティクルはこれ使ってるでしょうし)

ParticleUtils.cs
///
/// @file   ParticleUtils.cs
/// @author KyYukimoto
/// 
using UnityEngine;

namespace Utils
{
    public class ParticleUtils : MonoBehaviour
    {
        // system
        ParticleSystem System;

        // gradient
        struct GradientData
        {
            Gradient Src;
            int AlphaKeyNum;
            GradientAlphaKey[] StoredAlphaKeys;
            GradientAlphaKey[] AlphaKeys;
            bool IsAlphaModified;

            // Setup
            public void Setup(Gradient src)
            {
                if (src == null || Src != null) {
                    return;
                }
                Src = src;
                StoredAlphaKeys = src.alphaKeys;
                AlphaKeyNum = (StoredAlphaKeys == null) ? 0 : StoredAlphaKeys.Length;
            }

            // Cleanup
            public void Cleanup()
            {
                if (Src == null) {
                    return;
                }
                if (IsAlphaModified) {
                    Src.alphaKeys = StoredAlphaKeys;
                }
                this = default;
            }

            // SetAlphaRate
            public void SetAlphaRate(float alpha)
            {
                if (Src == null || AlphaKeyNum <= 0) {
                    return;
                }
                if (AlphaKeys == null) {
                    AlphaKeys = new GradientAlphaKey[AlphaKeyNum];
                }
                for (int i = 0; i < AlphaKeyNum; i++) {
                    var key = StoredAlphaKeys[i];
                    key.alpha *= alpha;
                    AlphaKeys[i] = key;
                }
                Src.alphaKeys = AlphaKeys;
                IsAlphaModified = true;
            }
        }

        // MinMaxGradientData
        struct MinMaxGradientData
        {
            public ParticleSystem.MinMaxGradient Src;
            public bool HasSetup { get; private set; }
            Color Color;
            Color ColorMin;
            Color ColorMax;
            GradientData Gradient;
            GradientData GradientMin;
            GradientData GradientMax;

            // Setup
            public void Setup(ParticleSystem.MinMaxGradient src)
            {
                if (HasSetup) {
                    return;
                }
                HasSetup = true;
                Src = src;
                Color = src.color;
                ColorMin = src.colorMin;
                ColorMax = src.colorMax;
                Gradient.Setup(src.gradient);
                GradientMin.Setup(src.gradientMin);
                GradientMax.Setup(src.gradientMax);
            }

            // Cleanup
            public void Cleanup()
            {
                if (!HasSetup) {
                    return;
                }
                HasSetup = false;
                Gradient.Cleanup();
                GradientMin.Cleanup();
                GradientMax.Cleanup();
                this = default;
            }

            // SetAlphaRate
            public void SetAlphaRate(float alpha)
            {
                if (!HasSetup) {
                    return;
                }
                Src.color = new Color(Color.r, Color.g, Color.b, Color.a * alpha);
                Src.colorMin = new Color(ColorMin.r, ColorMin.g, ColorMin.b, ColorMin.a * alpha);
                Src.colorMax = new Color(ColorMax.r, ColorMax.g, ColorMax.b, ColorMax.a * alpha);
                Gradient.SetAlphaRate(alpha);
                GradientMin.SetAlphaRate(alpha);
                GradientMax.SetAlphaRate(alpha);
            }
        }
        MinMaxGradientData StartColor;
        MinMaxGradientData LifeColor;

        // アルファ
        float Alpha = 1;

        // Start
        void Start()
        {
            // パラメータを保持
            StoreOriginalColor();
        }

        // OnDestroy
        void OnDestroy()
        {
            // パラメータをもとに戻す
            RestoreOriginalColor();
        }

        // 元の色を保存
        void StoreOriginalColor()
        {
            // すでに保存済み
            if (System != null) {
                return;
            }

            // 保存
            System = GetComponent<ParticleSystem>();
            if (System == null) {
                return;
            }

            // life or main
            var life = System.colorOverLifetime;
            if (life.enabled) {
                LifeColor.Setup(life.color);
            } else {
                var main = System.main;
                StartColor.Setup(main.startColor);
            }

            // 既にリクエストされていたらここで適用
            Apply(Alpha, true);
        }

        // 元の色に戻す
        void RestoreOriginalColor()
        {
            // alpha
            Alpha = 1;

            // 復元
            StartColor.Cleanup();
            LifeColor.Cleanup();
        }

        // アルファ変更
        public void SetAlphaRate(float alpha)
        {
            // check
            if (System == null) {
                Alpha = alpha;
                return;
            }

            // 変更
            Apply(alpha, false);
        }

        // アルファ戻す
        public void ResetAlphaRate() { SetAlphaRate(1); }

        // Apply
        void Apply(float alpha, bool force)
        {
            // alpha
            if (!force && Mathf.Approximately(alpha, Alpha)) {
                return;
            }
            Alpha = alpha;

            // life
            if (LifeColor.HasSetup) {
                var life = System.colorOverLifetime;
                if (life.enabled) {
                    LifeColor.SetAlphaRate(alpha);
                    life.color = LifeColor.Src;
                }
            }

            // main
            if (StartColor.HasSetup) {
                var main = System.main;
                StartColor.SetAlphaRate(Alpha);
                main.startColor = StartColor.Src;
            }
        }
    }
}

このスクリプトをParticleSystemのあるgameObjectにAddComponentして、
必要に応じてSetAlphaRate()に0から1の値を入れてあげれば、
即座に粒子のアルファが変化するはず。

注意点として、
少なくともGradient.alphaKeysをreadするとGC.Allocがよばれてしまう。
上の実装では、write時にはよばれてなさそうなんだけどよばれてたら残念なことに
結局のところ、専用のシェーダー作ってそっちで対応したほうがいいのかも。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?