【Unity】LineUtilityで直線データを削減する

こじゃらこじゃら

直線データを簡単に削減する方法はないの?

このはこのは

LineUtilityクラスのSimplifyメソッドが使えるわ。LineRendererにも同様の機能がサポートされているわ。

Unityでは線に関する機能をまとめたLineUtilityクラスが提供されています。これは、直線データを削減(リダクション)するSimplifyメソッドただ一つを備えたクラスです。

LineUtility.Simplifyメソッドを使えば、次のように複数の点を直線で結んだデータを単純化することが可能です。

また、直線を描画するコンポーネントであるLineRendererコンポーネントでも同様の機能がサポートされており、内部的にはLineUtilityクラスのSimplifyメソッドが使われています。

本記事では、LineUtilityクラスを用いて直線データを削減する方法について解説します。また、LineRendererの同様の機能についても触れます。

動作環境
  • Unity 2022.2.1f1

スポンサーリンク

LineUtilityクラス

直線データを削減するSimplifyメソッドを持つクラスです。

Simplifyメソッドは、与えられた頂点配列データから単純化された頂点配列データを返すstaticメソッドです。次のような4種類のオーバーロードされたものが用意されています。

public static void Simplify(
    List<Vector3> points,
    float tolerance,
    List<int> pointsToKeep
);
public static void Simplify(
    List<Vector3> points,
    float tolerance,
    List<Vector3> simplePoints
) ;
public static void Simplify(
    List<Vector2> points,
    float tolerance,
    List<int> pointsToKeep
);
public static void Simplify(
    List<Vector2> points,
    float tolerance,
    List<Vector2> simplePoints
) ;

2次元3次元の点データ用のメソッドがそれぞれ2つずつあります。

第1引数pointsには、単純化したい点データの配列を指定します。このデータはメソッドからは直接編集されません。

第2引数toleranceには、点を削除するための評価に使われる許容範囲の値で、この値が大きくなるほど線が単純になります。0の時は全く削減を行いません。

第3引数は2種類あり、単純化された点データを受け取るためのリストを指定します。指定される引数に応じて次のような情報が格納されます。

  • List<Vector*> simplePoints単純化された点の座標
  • List<int> pointsToKeep単純化された点のインデックス

なお、線の単純化には、Ramer Douglas Peuckerアルゴリズムが使用されています。

参考:Ramer–Douglas–Peucker algorithm – Wikipedia

サンプルスクリプト

LineUtility.Simplifyを使って実際に点データを削減する例です。

SimplifyExample.cs
using System.Collections.Generic;
using UnityEngine;

public class SimplifyExample : MonoBehaviour
{
    [Header("入力")]
    
    // 単純化対象の点の座標リスト
    [SerializeField] private List<Vector2> _inputPoints;
    // 許容範囲
    [SerializeField, Range(0, 10)] private float _tolerance = 0;

    [Header("出力結果")]
    
    // 単純化された点の座標リスト
    [SerializeField] private List<Vector2> _outputPoints;
    // 単純化された点のインデックスリスト
    [SerializeField] private List<int> _outputIndices;

    // インスペクタ上で情報が編集されたら出力結果を更新
    private void OnValidate()
    {
        // 単純化された点の座標リストを取得する
        LineUtility.Simplify(_inputPoints, _tolerance, _outputPoints);

        // 単純化された点のインデックスリストを取得する
        LineUtility.Simplify(_inputPoints, _tolerance, _outputIndices);
    }
}

上記スクリプトをSimplifyExample.csという名前でUnityプロジェクトに保存し、適当なゲームオブジェクトにアタッチすると機能するようになります。

インスペクタよりInput Pointsに点の座標リスト、Toleranceに許容範囲を設定すると、出力結果に即座に反映されます。

実行結果

実際にインスペクタから入力値を編集した様子です。

例えばToleranceの値を大きくするほど、出力結果の点データがたくさん削減されていくことが確認できます。

LineRendererの単純化機能

線を描画するコンポーネントであるLineRendererコンポーネントでも、同様の機能がサポートされています。

エディタから事前に削減する方法実行時にスクリプトから削減する方法の2通りが存在します。

インスペクタから単純化する

LineRendererコンポーネントのインスペクタには、次のような単純化用の項目が存在します。

これらの項目から、事前にLineRenderer内の点データを削減することができます。

使い方の詳細は以下マニュアルで解説されています。

Tolerance項目の値を大きくすると、より多くのデータが削減されます。この挙動はLineUtility.Simplifyメソッドと一緒です。

また、SImplify Preview項目にチェックを入れると、単純化後の線がプレビュー表示されます。

そして、Simplifyボタンをクリックすると、単純化処理が実行されてデータが更新されます。

注意

Simplifyボタンを押してデータが削減されたあとは、アンドゥを使用しない限りデータを元に戻せないのでご注意ください。

スクリプトから単純化する

LineRendererの点データは、LineRenderer.Simplifyメソッドにより実行時に単純化できます。

public void Simplify(float tolerance);

引数toleranceには、単純化に使用する許容範囲の値を指定します。これは、LineUtility.Simplifyメソッドの第2引数toleranceと同じ意味の値です。

内部的にはLineUtility.Simplifyメソッドが使われており、LineRendererの単純化に限ってはこちらのメソッドを使用する方が簡単です。

サンプルスクリプト

スクリプトからLineRendererの直線を単純化する例です。開始時に単純化処理を一度だけ実行します。

LineRendererSimplifyExample.cs
using UnityEngine;

public class LineRendererSimplifyExample : MonoBehaviour
{
    // 単純化対象のLineRenderer
    [SerializeField] private LineRenderer _lineRenderer;
    
    // 許容範囲
    [SerializeField, Range(0, 10)] private float _tolerance;

    private void Start()
    {
        // 開始時にLineRendererを単純化
        _lineRenderer.Simplify(_tolerance);
    }
}

上記をLineRendererSimplifyExample.csという名前でUnityプロジェクトに保存し、適当なゲームオブジェクトにアタッチし、インスペクタより必要項目を設定すると機能します。

実行結果

ゲームを実行すると、LineRendererの直線が単純化されることが確認できます。

さいごに

LineUtilityは線に関する機能をまとめたクラスで、Unity 2022.2現在では線データを単純化するSimplifyメソッドのみを実装しています。

大量の点データを削減しないといけない場面で活躍できるでしょう。

また、LineRendererでも同様の機能がサポートされており、事前または実行時に同様の操作を行えます。内部的にはLineUtility.Simplifyメソッドが使われているので、必要に応じて使い分けると良いでしょう。

参考サイト

スポンサーリンク