はじめに
前回の続きです
uGUIで線を引く~その2:線の描画編~
線の先端が縦固定になってしまっているため、線の角度によって、変更するようにします
流れ
①実際に引きたい線(以下、本線という)の単位垂直ベクトルを求める
②本線の端座標から、垂直線を引く
③本線の端座標から、垂直線上の双方向にweight/2分ずらした座標を、メッシュ描画時に使用する
#垂直ベクトルを求める
垂直のベクトルは内積が1なので、本線の傾きを表すベクトルを(x,y)、垂直ベクトルを(a,b)とすると
x * a + y * b = 0
が成り立つ。
手順③を考えると、単位ベクトルがほしいので、
a * a + b * b = 1
も成り立つようにする。
上記2つの式から、aとbを求めることができる。
....が!!ただ、実際計算したら、ルートを含むごちゃごちゃした式が出てきたので、シンプルに以下のようなコードにしてみた
①一度単位ベクトルにせず、a = 1として垂直ベクトルを取得
②Vector2のnormalizedプロパティで単位ベクトル取得
private Vector2 CalcurateVerticalVector(Vector2 vec)
{
Vector2 v = new Vector2(1.0f, -vec.x / vec.y);
return v.normalized;
}
これを使って、以下のようなコードになりました
using UnityEngine;
using UnityEngine.UI;
// ↓ugui描画に必須なコンポーネント達
[RequireComponent(typeof(CanvasRenderer))]
[RequireComponent(typeof(RectTransform))]
public class UIOneLine : Graphic
{
[SerializeField]
private Vector2 _position1;
[SerializeField]
private Vector2 _position2;
[SerializeField]
private float _weight;
protected override void OnPopulateMesh(VertexHelper vh)
{
// (1)過去の頂点を削除
vh.Clear();
// (2)垂直ベクトルの計算
var pos1_to_2 = _position2 - _position1;
var verticalVector = CalcurateVerticalVector(pos1_to_2);
// (3)左下、左上のベクトルを計算
var pos1Top = _position1 + verticalVector * -_weight / 2;
var pos1Bottom = _position1 + verticalVector * _weight / 2;
var pos2Top = _position2 + verticalVector * -_weight / 2;
var pos2Bottom = _position2 + verticalVector * _weight / 2;
// (4)頂点を頂点リストに追加
AddVert(vh, pos1Top);
AddVert(vh, pos1Bottom);
AddVert(vh, pos2Top);
AddVert(vh, pos2Bottom);
// (5)頂点リストを元にメッシュを貼る
vh.AddTriangle(0, 1, 2);
vh.AddTriangle(1, 2, 3);
}
private void AddVert(VertexHelper vh, Vector2 pos)
{
var vert = UIVertex.simpleVert;
vert.position = pos;
vert.color = color;
vh.AddVert(vert);
}
private 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;
}
}
}
結果
ランタイムで線を動かす
今回の例ではインスペクターの値をいじって線を動かしていますが、
もしランタイムでスクリプトから変更を加える場合は、毎回ダーティフラグをたてて、システム側に変更があったことを伝える必要があります。
ダーティフラグは、 SetVerticesDirty();
で立てることができるので、忘れないよう注意!
おわりに
気が向いたら、LineRendererみたいに配列でもらった座標をもとに折れ線を作れるようにします。
ここまで読んでくださって、ありがとうございました!