読者です 読者をやめる 読者になる 読者になる

Onoty3D

Unityに関するメモとか

Particleの動きを制御して、Trailで文字を書く

Unity5.5から追加されたParticleのTrailsモジュールで遊んでいた時、
Particleの動きを制御すればTrailで文字が書けたりするかな?と思ったのでやってみました。
f:id:onoty3d:20161115195659g:plain

Particleの動きの制御はVelocityOverLifetimeモジュールで行えますが、事前にキーを打ってCurveを作るのは大変です。
そこで、まずマウスで手書きで文字を書き、その時のカーソルのXY座標の変化をVelocityOverLifetimeにセットして文字を書くことにしました。

VelocityOverLifetimeの制御の仕方は以下QAが参考になりました。
answers.unity3d.com

VelocityOverLifetimeでのX,Y各軸の速度変化はMinMaxCurveというクラスのインスタンスをセットすればいいみたいですが、
インスタンス生成時にAnimationCurveを渡してやれば、それがそのままCurveになります。

AnimationCurveのキー追加方などは以下Qiitaの記事が参考になりました。
qiita.com

とりあえず動かせるサンプルを作ってUnityPackageにしてみました。
UnityPackage

Unity5.5b11で作成しています。
新規にシーンを作ってPrefabを投げ込んで実行すれば確認できると思います(背景は暗くしたほうがいいかも)。
実用するにはカメラの位置、角度なんかも考慮する必要がありそうですが、今回は簡易な実装になっています。

ただ文字を書くだけならLineRendererなんかで十分ですが、Particleにすれば設定によっては複数発生させたり、
変形操作もできたりして楽しい気がします。
f:id:onoty3d:20161115200251g:plain

以下ソース

using System.Collections.Generic;
using UnityEngine;

namespace Onoty3D
{
    public class HandWriteCurve : MonoBehaviour
    {
        //操作するパーティクル
        public ParticleSystem TargetParticle;

        //座標を取得するFrame間隔
        public int CaptureRate = 6;

        //セットするMinMaxCurveのMultiPlier値
        public float CurveMultiPlier = 1.0f;

        //座標のリスト
        private List<Vector3> _posList = new List<Vector3>();

        // Use this for initialization
        private void Start()
        {
        }

        // Update is called once per frame
        private void Update()
        {
            if (Input.GetMouseButtonDown(0))
            {
                this._posList.Clear();
                this._posList.Add(Input.mousePosition);
            }
            else if (Input.GetMouseButton(0))
            {
                if (Time.frameCount % this.CaptureRate == 0)
                {
                    this._posList.Add(Input.mousePosition);
                }
            }
            else if (Input.GetMouseButtonUp(0))
            {
                this._posList.Add(Input.mousePosition);

                this.SetCurve();
                this.TargetParticle.Play();
            }
        }

        private void SetCurve()
        {
            var interval = 1.0f / (this._posList.Count - 1);

            var curveX = new AnimationCurve();
            var curveY = new AnimationCurve();
            curveX.AddKey(new Keyframe(0, 0));
            curveY.AddKey(new Keyframe(0, 0));

            Vector3 delta;
            for (int i = 1; i < this._posList.Count; i++)
            {
                delta = this._posList[i] - this._posList[i - 1];

                curveX.AddKey(new Keyframe(interval * i, delta.x));
                curveY.AddKey(new Keyframe(interval * i, delta.y));
            }

            var module = this.TargetParticle.velocityOverLifetime;
            module.enabled = true;
            module.x = new ParticleSystem.MinMaxCurve(this.CurveMultiPlier, curveX);
            module.y = new ParticleSystem.MinMaxCurve(this.CurveMultiPlier, curveY);
        }
    }
}