Unity 2DでBスプラインを描画する

超お久しぶりです。
今回はUnityでcanvasに線を描画する記事があまり見当たらなかったので書いてみました。
今回はBスプライン曲線の描画をしてみたいと思います。

今回はこんなイメージで実装していきます。
汚い絵でごめんなさい...
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/895813/cc60b646-b4aa-db64-ce0c-f3b7efe23b6f.png)

大体こんな感じです。
赤点：ポリゴンの座標
紫点：曲線の座標(ポイント)

よくある曲線の描画方法ですね。

```C#:LineDraw.cs
    [SerializeField]
    private Vector2 _startPos;
	[SerializeField]
	private Vector2 _controlPos;
	[SerializeField]
    private Vector2 _endPos;
	[SerializeField]
    private float _weight;
	[SerializeField]
	private int _pointNum = 5;
```

とりあえず変数たち
ラインの開始座標、コントロール座標、終了座標とラインの幅、ポイント数になります。

```C#:LineDraw.cs
	protected override void OnPopulateMesh(VertexHelper vh)
    {
        vh.Clear();
        var start_to_end = _endPos - _startPos;
		var verticalVector = CalcurateVerticalVector(start_to_end);

		//座標設定
		for (int i = 0; i <= _pointNum; i++)
		{
			float t = (float)i / (float)_pointNum;
			var posTop = B_SplineCurveY(_startPos, _controlPos, _endPos, t) + verticalVector * -_weight / 2;
			var posBottom = B_SplineCurveY(_startPos, _controlPos, _endPos, t) + verticalVector * _weight / 2;
			AddVert(vh, posTop);
			AddVert(vh, posBottom);

            Debug.Log("ポイント : " + t.ToString() + "\n" +
                "座標 : " + B_SplineCurveY(_startPos, _controlPos, _endPos, t) + "\n" +
                "幅 : " + verticalVector.magnitude + "\n");
        }

		int renderNum = (_pointNum * 2);
		// メッシュに貼る
		for (int i = 0; i < renderNum; i++) 
        {
			vh.AddTriangle(i, i + 1, i + 2);
		}
	}
```

ラインの描画になります。
先ほどの画像で説明した通りポイント座標から幅を計算してポリゴンを描画するといった形です。

それでできたスクリプトがこちらです。
```C#:LineDraw.cs
using UnityEngine;
using UnityEngine.UI;
public class LineDraw : Graphic
{
    [SerializeField]
    private Vector2 _startPos;
	[SerializeField]
	private Vector2 _controlPos;
	[SerializeField]
    private Vector2 _endPos;
	[SerializeField]
    private float _weight;
	[SerializeField]
	private int _pointNum = 5;

    //　ラインの設定
    public void SetLinePos(Vector2 strat, Vector2 control, Vector2 end)
    {
        _startPos = strat; _controlPos = control; _endPos = end;
        SetVerticesDirty();
    }

    // B-スプライン曲線における X 座標を返す
    static float B_SplineCurveX(float x1, float x2, float x3, float t)
    {
        return (1 - t) * (1 - t) * x1 + 2 * t * (1 - t) * x2 + t * t * x3;
    }

    // B-スプライン曲線における Y 座標を返す
    static float B_SplineCurveY(float y1, float y2, float y3, float t)
    {
        return (1 - t) * (1 - t) * y1 + 2 * t * (1 - t) * y2 + t * t * y3;
    }

    // B-スプライン曲線における 2 次元座標を返す
    static Vector2 B_SplineCurveY(Vector2 p1, Vector2 p2, Vector2 p3, float t)
    {
        return new Vector2(
            B_SplineCurveX(p1.x, p2.x, p3.x, t),
            B_SplineCurveY(p1.y, p2.y, p3.y, t));
    }

	protected override void OnPopulateMesh(VertexHelper vh)
    {
        vh.Clear();
        var start_to_end = _endPos - _startPos;
		var verticalVector = CalcurateVerticalVector(start_to_end);

		//座標設定
		for (int i = 0; i <= _pointNum; i++)
		{
			float t = (float)i / (float)_pointNum;
			var posTop = B_SplineCurveY(_startPos, _controlPos, _endPos, t) + verticalVector * -_weight / 2;
			var posBottom = B_SplineCurveY(_startPos, _controlPos, _endPos, t) + verticalVector * _weight / 2;
			AddVert(vh, posTop);
			AddVert(vh, posBottom);

            Debug.Log("ポイント : " + t.ToString() + "\n" +
                "座標 : " + B_SplineCurveY(_startPos, _controlPos, _endPos, t) + "\n" +
                "幅 : " + verticalVector.magnitude + "\n");
        }

		int renderNum = (_pointNum * 2);
		// メッシュに貼る
		for (int i = 0; i < renderNum; i++) 
        {
			vh.AddTriangle(i, i + 1, i + 2);
		}
	}
    void AddVert(VertexHelper vh, Vector2 pos)
    {
        var vert = UIVertex.simpleVert;
        vert.position = pos;
        vert.color = color;
        vh.AddVert(vert);
    }

    Vector2 CalcurateVerticalVector(Vector2 vec)
    {
        // 0除算の防止
        if (vec.y == 0)
        {
            return Vector2.up;
        }
        else
        {
            var verticalVector = new Vector2(1.0f, -vec.x / vec.y);
            return verticalVector.normalized;
        }
    }
}
```

そしてこんな感じになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/895813/943423c7-44fb-d6df-3180-85757324b748.png)


こんな感じで実装できます。


その他
参考サイト
https://baba-s.hatenablog.com/entry/2014/06/23/202044