波打つ円形メッシュの生成

Twitterにアップした波打つ円形メッシュの生成用コンポーネントを貼っておきます。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Qiitaに埋め込む用の動画貼っておきます。<br>シェーダ芸で色付けできるかな〜と色々試していたけど、結局メッシュ生成にuvを追加してテクスチャを貼れるようにしておいた。<br><br>とりあえず今回はココまで。 <a href="https://t.co/HVVzlR3xgv">pic.twitter.com/HVVzlR3xgv</a></p>&mdash; MIYAKE (@ScreenPocket) <a href="https://twitter.com/ScreenPocket/status/1271840985153073152?ref_src=twsrc%5Etfw">June 13, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

昔はてなブログに書いた円形メッシュ生成関数と、Twitterに貼った波動関数の組み合わせでプニプニする円を作れるようにしました。

はてなブログはこちら↓
[外接円1.0の正多角形メッシュを作る](http://screenpocket.hateblo.jp/entry/2016/08/17/024047)
Twitter水面はこちら↓（実装方法はリプ欄に記載）
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">1次元配列に直したらメッチャ軽くなった！<br>これなら今作ってるゲームの背景でも使えるかも<br>　＆　マテリアル修正。<br><br>後は水面下を屈折できるようにすれば更に見栄えが良くできそうかな。<br>ただ、やりだすとキリがないのでそろそろ違うことするかも。<a href="https://twitter.com/hashtag/IndieGameDev?src=hash&amp;ref_src=twsrc%5Etfw">#IndieGameDev</a> <a href="https://twitter.com/hashtag/gamedev?src=hash&amp;ref_src=twsrc%5Etfw">#gamedev</a> <a href="https://twitter.com/hashtag/indiedev?src=hash&amp;ref_src=twsrc%5Etfw">#indiedev</a> <a href="https://t.co/WMwUz37eyI">pic.twitter.com/WMwUz37eyI</a></p>&mdash; MIYAKE (@ScreenPocket) <a href="https://twitter.com/ScreenPocket/status/1226368472118001664?ref_src=twsrc%5Etfw">February 9, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

コードも貼っておきます。
メッシュ生成関数(GenerateRegularPolygonMeshXY())について、
はてなブログの時のコードにuv指定も追加されています。

```cs:CircleWaterSurface.cs
using System;
using UnityEngine;

namespace ScreenPocket
{

    public sealed class CircleWaterSurface : MonoBehaviour
    {
        private enum Timing
        {
            Future,//未来(次のフレーム)
            Now,//現在(今のフレーム（描画対象）)
            Old//過去（一つ前のフレーム）
        }
        
        /// <summary>
        /// 頂点数
        /// </summary>
        [SerializeField]
        private int _nodeNum = 128;
        /// <summary>
        /// 減衰率
        /// </summary>
        [SerializeField]
        private float _wither = 0.9f;
        
        /// <summary>
        /// 波の高さ
        /// </summary>
        [SerializeField]
        private float _height = 1f;

        [SerializeField]
        private Material _material;
        
        private float[][] _heightArrayArray;
        private Mesh _mesh;
        private int _nodeNumOld;
        private float _waveTimer = 0f;
        
        // Start is called before the first frame update
        private void Start()
        {
            Setup(_nodeNum);
        }

        /// <summary>
        /// 準備
        /// </summary>
        /// <param name="nodeNum">波動の要素数</param>
        private void Setup(int nodeNum)
        {
            //高さ配列を準備
            _heightArrayArray = new []
            {
                new float[nodeNum],//未来
                new float[nodeNum],//現在
                new float[nodeNum]//過去
            };

            //0クリア
            for (int i = 0; i < 3; ++i)
            {
                var heightArray = _heightArrayArray[i];
                for (var j = 0; j < nodeNum; ++j)
                {
                    heightArray[j] = 0.0f;
                }
            }
            
            //メッシュの準備
            _mesh = GenerateRegularPolygonMeshXY(nodeNum);
            _mesh.MarkDynamic();

            //コンポーネントの準備
            if (!gameObject.TryGetComponent<MeshFilter>(out var meshFilter))
            {
                meshFilter = gameObject.AddComponent<MeshFilter>();
            }
            meshFilter.sharedMesh = _mesh;
            
            if (!gameObject.TryGetComponent<MeshRenderer>(out var meshRenderer))
            {
                meshRenderer = gameObject.AddComponent<MeshRenderer>();
            }
            meshRenderer.sharedMaterial = _material;

            _waveTimer = 0f;
            _nodeNumOld = nodeNum;
        }

        // Update is called once per frame
        private void Update()
        {
            _waveTimer += Time.deltaTime;
            //Node数が変わったらメッシュを作り直す
            if (_nodeNumOld != _nodeNum)
            {
                Setup(_nodeNum);
            }
            
            //タップしたらタップした方角の高さを変える
            if (Input.GetMouseButton(0))
            {
                var pos = Input.mousePosition - new Vector3(Screen.width, Screen.height, 0f) * 0.5f;
                
                //角度を求める
                var angleDegree = Mathf.Atan2(-pos.x, -pos.y) * Mathf.Rad2Deg + 180f;
                //角度がノードのどの当たりを差しているかを求める
                var targetNodeId = Mathf.FloorToInt(angleDegree / 360f * _nodeNum);

                //大体ガウシアンな感じで高さをつける
                SetHeight( Timing.Now, targetNodeId-3,0.125f);
                SetHeight( Timing.Now, targetNodeId-2,0.4f);
                SetHeight( Timing.Now, targetNodeId-1,0.45f);
                SetHeight( Timing.Now, targetNodeId,0.5f);
                SetHeight( Timing.Now, targetNodeId+1,0.45f);
                SetHeight( Timing.Now, targetNodeId+2,0.4f);
                SetHeight( Timing.Now, targetNodeId+3,0.125f);
            }
            
            for (int i = 0; i < _nodeNum; ++i)
            {
                float work = 0f;
                if (i == 0)
                {
                    work += _heightArrayArray[(int)Timing.Now][_nodeNum - 1];
                }
                else
                {
                    work += _heightArrayArray[(int)Timing.Now][i-1];
                }
                
                if (i == _nodeNum - 1)
                {
                    work += _heightArrayArray[(int)Timing.Now][0];
                }
                else
                {
                    work += _heightArrayArray[(int)Timing.Now][i + 1];
                }

                _heightArrayArray[(int)Timing.Future][i] = (work * 0.5f - _heightArrayArray[(int)Timing.Old][i]) * _wither;
            }
            
            //頂点更新
            var vertices = _mesh.vertices;
            //頂点
            vertices[0] = Vector3.zero; //中心
            float div = Mathf.PI * 2f / _nodeNum;
            
            for (int i = 0; i < _nodeNum; ++i)
            {
                float length = 1f;
                length += _heightArrayArray[(int)Timing.Now][i] * _height;
                
                float rad = div * i;
                vertices[i + 1] = new Vector3(Mathf.Sin(rad) * length, Mathf.Cos(rad) * length, 0f);
            }
            
            _mesh.vertices = vertices;
            _mesh.RecalculateNormals();
            //現在→過去、未来→現在で進行させる
            Array.Copy(_heightArrayArray[(int)Timing.Now], _heightArrayArray[(int)Timing.Old], _nodeNum);
            Array.Copy(_heightArrayArray[(int)Timing.Future], _heightArrayArray[(int)Timing.Now], _nodeNum);

            _nodeNumOld = _nodeNum;
        }

        /// <summary>
        /// 高さを設定する
        /// </summary>
        /// <param name="timing">どのタイミングの高さか</param>
        /// <param name="index">何番目の高さか</param>
        /// <param name="value">高さ</param>
        private void SetHeight(Timing timing, int index, float value)
        {
            //範囲チェック
            while (index < 0)
            {
                index += _nodeNum;
            }
            while (index >= _nodeNum)
            {
                index -= _nodeNum;
            }
            _heightArrayArray[(int)timing][index] = value;
        }

        /// <summary>
        /// XY平面に外接円半径1.0の多角形面を作る関数
        /// </summary>
        /// <param name="vertexCount"></param>
        /// <returns></returns>
        public static Mesh GenerateRegularPolygonMeshXY(int vertexCount)
        {
            //Meshの作成
            Mesh mesh = new Mesh();

            Vector2[] points = new Vector2[vertexCount];
            float div = 360f / (float) vertexCount;
            for (int i = 0; i < vertexCount; ++i)
            {
                float rad = div * (float) i * Mathf.Deg2Rad;
                points[i] = new Vector2(Mathf.Sin(rad), Mathf.Cos(rad));
            }

            //頂点
            var verticies = new Vector3[points.Length + 1];
            verticies[0] = Vector3.zero; //中心
            for (int i = 0, length = points.Length; i < length; ++i)
            {
                verticies[i + 1] = new Vector3(points[i].x, points[i].y, 0f);
            }

            mesh.vertices = verticies;
            
            //uv
            var uvs = new Vector2[points.Length + 1];
            uvs[0] = new Vector2(0.5f,0.5f); //中心
            for (int i = 0, length = points.Length; i < length; ++i)
            {
                uvs[i + 1] = new Vector2(points[i].x, points[i].y) + new Vector2(1f,1f);
                uvs[i + 1] *= 0.5f;
            }

            mesh.uv = uvs;

            //色
            Color[] colors = new Color[points.Length + 1];
            colors[0] = Color.black; //中心
            for (int i = 1, length = colors.Length; i < length; ++i)
            {
                colors[i] = Color.white;
            }

            mesh.colors = colors;

            //面
            var indicies = new int[vertexCount * 3];
            for (int i = 0; i < vertexCount; ++i)
            {
                indicies[i * 3 + 0] = 0;
                indicies[i * 3 + 1] = i + 1;
                indicies[i * 3 + 2] = ((i + 2 >= verticies.Length) ? 1 : i + 2);
            }

            mesh.triangles = indicies;
            return mesh;
        }
    }
}
```

急いで書いたのでvertex取得するところでAllocしています。最適化、修正などはご自由に！