FungusのLocalizatioのExport Standard TextのパスをInspector上から指定する【Unity】【Fungus】

目的

Fungusで localizationなどを想定する際にテキストデータを外部に出力することがあります。Exportするときに以下のように毎回 保存場所を聞かれるため、Default値をInspetorから指定したい

Demo

以下のように InspectorからDefault Pathを指定して、Export Standart Text To Default Path を押すことで Inspectorのパスに保存されます。

開発環境

  • Unity 2022.2.0f1
  • Fungus v3.13.8

追加・変更点

(画像は変更差分)

まず Localization.cs に Inspector上に入力するための SerializeField を追加します。

次に Editor側で Buttonの処理やパスの処理を追加していきます。

Inspectorの値を取得するために、 SerializedProperty で変数のプロパティを取得します。

SerializedPropertyの詳細は公式ドキュメントを確認ください

docs.unity3d.com

        protected SerializedProperty activeLanguageProp;
        protected SerializedProperty localizationFileProp;
        protected SerializedProperty savePathProp;

        protected virtual void OnEnable()
        {
            activeLanguageProp = serializedObject.FindProperty("activeLanguage");
            localizationFileProp = serializedObject.FindProperty("localizationFile");
            savePathProp = serializedObject.FindProperty("savePath");
        }

        public override void OnInspectorGUI()
        {
            serializedObject.Update();

            Localization localization = target as Localization;

            EditorGUILayout.PropertyField(activeLanguageProp);
            EditorGUILayout.PropertyField(localizationFileProp);
            EditorGUILayout.PropertyField(savePathProp);

追加のButtonを定義します。

            if (GUILayout.Button(new GUIContent("Export Standard Text At Default Path")))
            {
                ExportStandardText(localization, savePathProp.stringValue);
            }

また追加機能用にパスを引数から指定できる関数を追加します

        public virtual void ExportStandardText(Localization localization, string path)
        {
            if (path.Length == 0)
            {
                return;
            }

            localization.ClearLocalizeableCache();

            string textData = localization.GetStandardText();
            File.WriteAllText(path, textData);
            AssetDatabase.Refresh();

            ShowNotification(localization);
        }

UnityのSplinesを使って動的に生成される曲線の道を作る【Unity】【Splines】

はじめに

前回の以下の記事で Splinesのサンプルプロジェクトをいろいろ確認したり、Editor拡張を作ったりしました。
今回は、これらから実際に Splineを使用したゲームを作っていきたいと思います。

ayousanz.hatenadiary.jp

デモ

開発環境

  • Unity 2022.2.0f1
  • Splines v2.1.0

Splinesとは

以下のようなサンプルとしてあるように曲線とパスを使用することでいままで標準ではできなかった軌跡を作ることが可能です。詳しくは、公式ドキュメントからの引用をご覧ください

Unity公式 Splines 2.1.0より (deeplにより日本語訳)

曲線とパスで作業する スプラインパッケージを使うと、パスに沿ってオブジェクトや動作を生成したり、軌跡を作成したり、図形を描いたりすることができます。

Splines パッケージには以下が含まれます。

  • Unity エディタでスプラインを作成および操作するためのツール。
  • このパッケージの標準的なスプライン編集ツールをカスタマイズするためのフレームワーク
  • 一般的に使用されるスプラインのための標準データ形式と保存モデル。
  • 道路の作成、スプラインに沿った GameObject の位置と回転のアニメーション、スプラインに沿ったプレハブのインスタンス化による環境の作成など、一般的なスプラインの使用例に対処する実装のサンプル。

Splines 2.0.0以上に含まれている機能

今回の実装でも用いている v1には含まれていない v2に含まれている Splinesの機能は以下になります

  • SplinePath、SplineSlice、SplineRange タイプを追加しました。これらのタイプは、離散スプラインの部分的または完全なセクションの補間と評価を可能にします。

その他の変更点 → com.unity.splines changelog

実装方法

今回は Splinesの Sampleシーンを参考にして実装を行っています。

準備

1 . Unityに Splinesを導入する

2 . Sampleの追加

SplinesのMeshの更新

今回 Splinesに追加の点を追加した際のMesh周りの処理は、サンプルシーンにすでにあるもの以下のスクリプトを改変します。

Assets/Samples/Splines/2.1.0/Spline Examples (requires Shader Graph package)/Runtime/MultipleRoadBehaviour.cs

新たに MultipleRoad.cs を作成して、以下を追加します。

using System.Collections.Generic;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Serialization;
using UnityEngine.Splines;


namespace _SplineTrain
{
    [DisallowMultipleComponent]
    [RequireComponent(typeof(SplineContainer), typeof(MeshRenderer), typeof(MeshFilter))]
    public class MultipleRoad : MonoBehaviour
    {
        [FormerlySerializedAs("m_Spline")] [SerializeField]
        private SplineContainer mSpline;

        [FormerlySerializedAs("m_SegmentsPerMeter")] [SerializeField]
        private int mSegmentsPerMeter = 1;

        [FormerlySerializedAs("m_Mesh")] [SerializeField]
        private Mesh mMesh;

        [FormerlySerializedAs("m_TextureScale")] [SerializeField]
        private float mTextureScale = 1f;

        private IEnumerable<Spline> RoadSplines
        {
            get
            {
                if (mSpline == null) mSpline = GetComponent<SplineContainer>();
                if (mSpline == null) return null;
                return mSpline.Splines;
            }
        }

        private Mesh RoadsMesh
        {
            get
            {
                if (mMesh != null)
                    return mMesh;

                mMesh = new Mesh();
                GetComponent<MeshRenderer>().sharedMaterial = Resources.Load<Material>("Road");
                return mMesh;
            }
        }

        private int SegmentsPerMeter => Mathf.Min(10, Mathf.Max(1, mSegmentsPerMeter));

        private readonly List<Vector3> _mPositions = new List<Vector3>();
        private readonly List<Vector3> _mNormals = new List<Vector3>();
        private readonly List<Vector2> _mTextures = new List<Vector2>();
        private readonly List<int> _mIndices = new List<int>();

        public void OnEnable()
        {
            //Avoid to point to an existing instance when duplicating the GameObject
            if (mMesh != null)
                mMesh = null;

            CreateRoads();
        }

        public void OnDisable()
        {

            if (mMesh != null)

#if UNITY_EDITOR
                DestroyImmediate(mMesh);
#else
                Destroy(mMesh);
#endif
        }

        public void CreateRoads()
        {
            RoadsMesh.Clear();
            _mPositions.Clear();
            _mNormals.Clear();
            _mTextures.Clear();
            _mIndices.Clear();

            foreach (var spl in RoadSplines)
            {
                CreateRoad(spl);
            }

            RoadsMesh.SetVertices(_mPositions);
            RoadsMesh.SetNormals(_mNormals);
            RoadsMesh.SetUVs(0, _mTextures);
            RoadsMesh.subMeshCount = 1;
            RoadsMesh.SetIndices(_mIndices, MeshTopology.Triangles, 0);
            RoadsMesh.UploadMeshData(false);

            GetComponent<MeshFilter>().sharedMesh = mMesh;
        }

        private void CreateRoad(Spline roadSpline)
        {
            if (roadSpline == null || roadSpline.Count < 2)
                return;

            var length = roadSpline.GetLength();

            if (length < 1)
                return;

            var segments = (int)(SegmentsPerMeter * length);
            int vertexCount = segments * 2, triangleCount = (roadSpline.Closed ? segments : segments - 1) * 6;

            var prevVertexCount = _mPositions.Count;
            _mPositions.Capacity += vertexCount;
            _mNormals.Capacity += vertexCount;
            _mTextures.Capacity += vertexCount;
            _mIndices.Capacity += triangleCount;

            for (var i = 0; i < segments; i++)
            {
                var index = i / (segments - 1f);
                var control = SplineUtility.EvaluatePosition(roadSpline, index);
                var dir = SplineUtility.EvaluateTangent(roadSpline, index);
                var up = SplineUtility.EvaluateUpVector(roadSpline, index);

                var scale = transform.lossyScale;
                //var tangent = math.normalize((float3)math.mul(math.cross(up, dir), new float3(1f / scale.x, 1f / scale.y, 1f / scale.z)));
                var tangent = math.normalize(math.cross(up, dir)) * new float3(1f / scale.x, 1f / scale.y, 1f / scale.z);

                var w = 1f;

                _mPositions.Add(control - (tangent * w));
                _mPositions.Add(control + (tangent * w));
                _mNormals.Add(Vector3.up);
                _mNormals.Add(Vector3.up);
                _mTextures.Add(new Vector2(0f, index * mTextureScale));
                _mTextures.Add(new Vector2(1f, index * mTextureScale));
            }

            for (int i = 0, n = prevVertexCount; i < triangleCount; i += 6, n += 2)
            {
                _mIndices.Add((n + 2) % (prevVertexCount + vertexCount));
                _mIndices.Add((n + 1) % (prevVertexCount + vertexCount));
                _mIndices.Add((n + 0) % (prevVertexCount + vertexCount));
                _mIndices.Add((n + 2) % (prevVertexCount + vertexCount));
                _mIndices.Add((n + 3) % (prevVertexCount + vertexCount));
                _mIndices.Add((n + 1) % (prevVertexCount + vertexCount));
            }
        }
    }
}

サンプルスクリプトをそのまま使う際は、[ExecuteInEditMode] が付いていることに注意してください。

ExecuteInEditMode は以下のように 呼ばれる際に制限がかかっている状態になっています。

  • Updateは、シーンに何か変化があったときだけ呼ばれます。
  • OnGUIは、Game ViewがEventを受け取ったときに呼び出されます。
  • OnRenderObject と他のレンダリングコールバック関数は、シーンビューやゲームビューが再描画されるたびに呼び出されます。

詳細は以下のドキュメントをご覧ください。 docs.unity3d.com

選択したSplinesの取得

ここで Splnies v2から新しく追加された SplineSliceSplineRange を使用します。

使用している SplineContainerから任意の長さのSplineのポイントを取得して返します。

public SplinePath GetNextPath()
        {
            return new SplinePath(new[]
            {
                new SplineSlice<Spline>(CurrentSpline, new SplineRange(0, CurrentSplineCount), _containerTransform),
            });
        }

Splineが更新されたときに SplineのMeshの更新

今回のプロジェクトでは、動的にSplineを増やしているためSplineにポイントを追加するたびにMeshを更新する必要があります。

        private void Start()
        {
            Spline.Changed += OnSplineOnChanged;
        }

        private void OnSplineOnChanged(Spline spline, int i, SplineModification splineModification)
        {
            if (spline == _editRoadModel.CurrentSpline)
            {
                _multipleRoad.CreateRoads();
            }
        }

サンプルコードから引用してきたコードの中にある CreateRoads を Splineが更新されるたびに呼び出します。
Spline には以下の Actionsが存在しているので、今回はこちらを使用します。今回は上記のコードにもあるように Start関数内でeventを発火して追加されるごとにMeshを更新しています。

public static event Action<Spline, int, SplineModification> Changed;

Simple Sample Demo

サンプルコードとして、Splinesを使用したMeshが一定時間ごとに増えていくSimple Sample ProjectのRepositoryも公開しています。

github.com

GitHubを使ってティラノビルダーの開発/テスト環境を構築する【ティラノビルダー】【Netlify】

初めに

インディーズゲームにいろいろ参加している中でADV系のゲームだとティラノビルダーやティラノスクリプトが多く使われているチームを見受けられます。
ティラノ系も触っておきたいと思い、少し触っていたのですが手元でPreviewはできますがWeb上でのテスト環境がないためぱっと見てもらうことはできません。
そこで今回はURLだけ共有すれば見れる環境を作ってみました。

環境

  • GitHub
  • Netlify
  • ティラノビルダー TyranoBuilder 2.0.3.0 standard ja

成果物/デモ

各種サービス関係図

以下のようにGitHubで管理しているティラノビルダーのプロジェクトがあります。 github.com

上記のプロジェクトを以下のURLで起動するようになっています。
639aa9c31983411e7aa6dc03--tyrano-builder-test.netlify.app

準備

GitHubアカウント作成

(すでに持っている人はスキップしてください)

GitHub を開くと以下のように `サインアップがあるので、こちらからアカウントを作成します)

情報を入れていくと、以下のような画面になると思います。

画面が出てきたらアカウントの作成は終了です

GitHub リポジトリの作成

プロジェクト用にGitHubリポジトリを作成します。以下の New を押します

  1. Repository name は任意の名前を入れてください。
  2. Public or Private は素材データなどは公開しないほうがいいので、Private を選びます
  3. その他のファイル設定は、GitやGitHubが分かっている人はチャックを入れても大丈夫ですが、初めは入れなくて大丈夫です

Create Repository を押すと作成されるので、以下のような画面になれば作成完了です

Netlifyアカウント作成

1 . Netlifyにアクセスすると以下のような画面になるので、Sign up を押します。

何でSing upをするか選択ができるため、先ほど作成した GitHubのアカウントを使用するため GitHubを選択します(ほかでSign upしたい場合は、任意のものを選択してください)

こんな感じの画面が出てくればアカウント作成は終了です

プロジェクトの作成とGitHubへの連携

  1. ティラノビルダーでプロジェクトを作成します

2 . フォルダーアイコンをクリックして、プロジェクトのフォルダーを開きます

3 .開いた後に index.html がある 一つ上の階層に移動します

このパスに Git環境を構築します (任意の方法で大丈夫です。また今後こちらのやり方を更新します)

pushまで行うと、作成したRepositoryが以下のような感じになると思います。

Netlifyへデプロイ

  1. Add new site を選択する

2 . Import an existing project を選択

3 . 連携サービスで GitHubを選択

4 . 作成したRepositoryを選択

5 . 必要な情報を記入して公開

通常時は入力するものはないので、そのまま Deploy site を押すだけで大丈夫です。

6 . 公開後、urlが表示されているのでURLを開きゲームが実行できるかどうかを確認する

デプロイ後の注意

www.netlify.com

2022/12/15時点で 帯域が100GM/monthのため、リリース後に多くのユーザーもしくは大きい画像ファイルや音声ファイルを使っている場合は超える可能性があるため注意してください。

UnityのDevelopment buildの有無で出力ファイルの違い【Unity】

はじめに

UnityRoomへのアップロードを試しているときにファイルが登録できないことがあったため、気になりました。そこで圧縮パターンや development buildでの出力ファイルの違いについてまとめました

UnityRoomでの Development buildについて

UnityRoomでは 2022/11/26現在gzファイル以外はアップロードできないとのことでした。

環境

  • Unity 2021.3.14f1

出力ファイルの違い

画像

表形式

normal development normal development normal development normal development
Brotli Buil.data.br Buiuld.data Build.framework.js.br Build.framework.js Build.loader.js Build.loader.js Build.wasm.br Build.wasm
Gzip Build.data.gz Build.data Build.framework.js.gz Build.framework.js Build.loader.js Build.loader.js Build.wasm.gz Build.wasm
Disable Build.data Build.data Build.framework.js Build.framework.js Build.loader.js Build.loader.js Build.wasm Build.wssm

GitHub ActionsのLinux環境でAssetDatabase処理を入れるとエラーになる【Unity】【GitHubActions】

はじめに

ゲームジャムに参加している中で GitHub Actions上でのCD環境を構築していました。あるときからビルドが失敗するようになってしまったので、調査していると環境でのエラーに遭遇したのでメモしておきます。

環境

  • Unity 2021.3.14f1

CD環境の環境ファイル

name: Build

env:
  UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}

# Controls when the action will run. 
on: 
  workflow_dispatch: {}
  push:
    branches:
      - main
    paths-ignore:
      - '.github/**'
  # Triggers the workflow on push or pull request events but only for the main branch

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        projectPath:
          - .
        unityVersion:
          - 2021.3.14f1
        targetPlatform:
         - WebGL
    steps:
    - name: Checkout
      uses: actions/checkout@v2
      with:
        lfs: true
        clean: false
        
#     Cache
    - uses: actions/cache@v2
      with:
        path: Library
        key: Library

    # Build
    - name: Build project
      uses: game-ci/unity-builder@v2.0.4
      with:
        unityVersion: ${{ matrix.unityVersion }}
        targetPlatform: ${{ matrix.targetPlatform }}
        
    - name: Deploy to GitHub Pages
      uses: JamesIves/github-pages-deploy-action@v4.4.0
      with:
        branch: gh-pages
        folder: build
  

    # Output
    - uses: actions/upload-artifact@v2
      with:
        name: Build-${{ matrix.targetPlatform }}
        path: build/${{ matrix.targetPlatform }}

現象

GitHub Actions(Linux)上でビルドするとエラーになって止まってしまう。

以下は Github actions上のエラーの一部です。

Serialized binary data for shader Hidden/Universal Render Pipeline/StencilDeferred in 0.00s
    gles3 (total internal programs: 0, unique: 0)
Shader warning in 'Hidden/Universal Render Pipeline/StencilDeferred': Hidden/Universal Render Pipeline/StencilDeferred shader is not supported on this GPU (none of subshaders/fallbacks are suitable)
Compiling shader "Hidden/Universal Render Pipeline/LutBuilderHdr" pass "LutBuilderHdr" (vp)
    3 / 3 variants left after stripping, processed in 0.00 seconds
    starting compilation...
    finished in 0.00 seconds. Local cache hits 3 (0.00s CPU time), remote cache hits 0 (0.00s CPU time), compiled 0 variants (0.00s CPU time), skipped 0 variants
    Prepared data for serialisation in 0.00s
Serialized binary data for shader Hidden/Universal Render Pipeline/LutBuilderHdr in 0.00s
    gles3 (total internal programs: 3, unique: 3)
Compiling shader "TextMeshPro/Sprite" pass "Default" (vp)
    4 / 4 variants left after stripping, processed in 0.00 seconds
    starting compilation...
    finished in 0.00 seconds. Local cache hits 4 (0.00s CPU time), remote cache hits 0 (0.00s CPU time), compiled 0 variants (0.00s CPU time), skipped 0 variants
    Prepared data for serialisation in 0.00s
Serialized binary data for shader TextMeshPro/Sprite in 0.00s
    gles3 (total internal programs: 4, unique: 4)
Asset has disappeared while building player to 'sharedassets0.assets' - path '', instancedID '15416'
UnityEngine.StackTraceUtility:ExtractStackTrace () (at /home/bokken/build/output/unity/unity/Runtime/Export/Scripting/StackTrace.cs:37)
UnityEditor.BuildPipeline:BuildPlayerInternal (string[],string,string,UnityEditor.BuildTargetGroup,UnityEditor.BuildTarget,int,UnityEditor.BuildOptions,string[]) (at /home/bokken/build/output/unity/unity/Editor/Mono/BuildPipeline.bindings.cs:449)
UnityEditor.BuildPipeline:BuildPlayer (string[],string,string,UnityEditor.BuildTargetGroup,UnityEditor.BuildTarget,int,UnityEditor.BuildOptions,string[]) (at /home/bokken/build/output/unity/unity/Editor/Mono/BuildPipeline.bindings.cs:348)
UnityEditor.BuildPipeline:BuildPlayer (UnityEditor.BuildPlayerOptions) (at /home/bokken/build/output/unity/unity/Editor/Mono/BuildPipeline.bindings.cs:322)
UnityBuilderAction.Builder:BuildProject () (at Assets/Editor/Editor/UnityBuilderAction/Builder.cs:71)

原因と解決方法

いろいろ試している中で、以下の部分を変更することでビルドが通るようになりました。

            var instance = ScriptableObject.CreateInstance<GameParameterData>();
            instance.isDevelop = true;
            AssetDatabase.CreateAsset(instance, $"Assets/_Project/Scripts/GameParameter/GameParameter.asset");
            AssetDatabase.Refresh();

上記のコードを #if UNITY_EDITOR_WIN で囲むことによって、ローカルビルドの際のみ実行するようにしました。

#if UNITY_EDITOR_WIN
            var instance = ScriptableObject.CreateInstance<GameParameterData>();
            instance.isDevelop = true;
            AssetDatabase.CreateAsset(instance, $"Assets/_Project/Scripts/GameParameter/GameParameter.asset");
            AssetDatabase.Refresh();
#endif

goでprotobufを生成する【go】

初めに

goでgRPCを試す際に protoファイルからgoファイルを生成するときにはまったので、メモしておきます

環境

  • go version go1.17.13 darwin/arm64

問題

以下のコマンドでprotoファイルから生成しようとしたが、エラーが出て生成されない

生成コマンド

protoc --go_out=. --go_opt=paths=source_relative \
    --go-grpc_out=. --go-grpc_opt=paths=source_relative \
    user.proto

エラー文

rotoc-gen-go: unable to determine Go import path for "user.proto"

Please specify either:
    • a "go_package" option in the .proto source file, ora "M" argument on the command line.

See https://developers.google.com/protocol-buffers/docs/reference/go-generated#package for more information.

--go_out: protoc-gen-go: Plugin failed with status code 1.

解決方法

protoファイル側に以下の一文を追加する

option go_package = "github.com/ayutaz/protoc-go-experiments/helloworld";

参考サイト

zenn.dev

UnityのSplinesをEditorで操作する【Unity】

はじめに

Unity 2022.1から入っている新機能 Splines を使ってみたかったので、とりあえず 触ってみることにしました。これからゲーム等に活用していければ嬉しいですね

Demo

Runtime及び Editor上で Splinesのknot(各ポジション)を増やせるクラスの作成や調査を行いました。

環境

  • Unity 2022.2.0b13
  • Splines 2.0.0
  • Odin

Splineとは

公式サイトより

パスに沿ってオブジェクトと動作を生成し、軌跡を作成し、形状を描画します。

UnityのSplines デモシーンの確認

どんなものなのかデモを見てみないと分からないので、確認してみます。

サンプルの導入方法

  1. Unity Package Managerを開く
  2. Packgesの Unity Registry を選択 (画像の1)
  3. 検索欄で Splinesで検索して出てきたものを選択
  4. タブの中から Sample を選択 (画像の2)
  5. サンプルデータをimportする(画像の3)

サンプルシーンの動作確認

  1. Loft Road

道が今までできなかった曲線を描いている静止デモ

2 . Spline Animate

1の道路の上にCubeを載せてアニメーションで動くようにしているデモ

3 . ExtrudeSpline

Meshデータを Splineを使って動かすデモ

4 . SplineInstantiate

Treeや Grassなども曲線に配置している 静止デモ

5 . SplineData and Generation

Spline周りのAPIを使用して生成するデモ

ReadMeより

plineDataSplineUtilitySplineEditorUtility API を使って、シーン上にスプラインベースのオブジェクトをプロシージャルに生成する

6 . Area Generation

5とほぼ同様?のデモ

7 . Nearest Point

任意の場所からスプライン上の最も近い点を見つけて移動するデモ

8 . Curvature

スプライン曲率の可視化しているデモ

9 . Embedded Spline

スプラインをコンポーネントに埋め込んで動かしているデモ

10 . GPU Evaluation

GPU上で Splineの計算を行うデモ

Spline関連クラスについて

クラスイメージは以下のようになっています。

SplineContainer

Inspectorに表示されている Splineは SplineContainer クラスになっています。

基本これが親クラスです。

Spline

Spline0 と書かれている部分は Spline になっていて、画面に表示されるポイントの箱(リスト)です

BezierKnot

各ポイントを Knot と呼ばれる位置や回転情報などの情報です。

SplineのKnotの操作

追加

それでは Editor上やRuntime上で Knotを増やしていけるようにしたいと思います。 上記のクラス関係から 何もない状態からでも追加できるようにします。

まず SplineContainerが追加されているかを判断するために、以下のif文を入れます。

if (splineContainer.Splines.Count != 0) return;

次に 追加したい SplineContainerを指定して、以下の処理で Splineを追加していきます

splineContainer.AddSpline();

最後に Knotを追加します

splineContainer.Spline.Add(knot);

最終的なスクリプトは以下のようになりました。

    public class SplineKnotExtra : MonoBehaviour
    {
        [SerializeField] private List<SplineContainer> movePointList;

        #region EditorからSplineを操作する

        /// <summary>
        /// Knotを一つランダム値を一つ追加
        /// </summary>
        private static void AddKnotAtRandomValue(SplineContainer splineContainer, int addSplines)
        {
            for (var splineCount = 0; splineCount < addSplines; splineCount++)
            {
                var knot = new BezierKnot(Random.Range(-50f, 50f), Random.Range(-50f, 50f), Random.Range(-50f, 50f), Random.rotation);
                splineContainer.Spline.Add(knot);
            }
        }

        private static void AddKnotAt2DLineRandomValue(SplineContainer splineContainer, int addSplines)
        {
            for (var splineCount = 0; splineCount < addSplines; splineCount++)
            {
                var knot = new BezierKnot(Random.Range(-50f, 50f));
                splineContainer.Spline.Add(knot);
            }
        }

        /// <summary>
        /// SplineのNodeを任意の数を追加
        /// </summary>
        private static void AddSplineKnot(SplineContainer splineContainer, int addSplineCount)
        {
            if (splineContainer.Splines.Count != 0) return;
            for (var addSplineCountIndex = 0; addSplineCountIndex < addSplineCount; addSplineCountIndex++)
            {
                splineContainer.AddSpline();
            }
        }

        #endregion

        [Button("Spline Knowのランダム追加")]
        private void AddRandomKnot(int addKnowCount)
        {
            foreach (var splineContainer in movePointList)
            {
                AddSplineKnot(splineContainer, addKnowCount);
                AddKnotAtRandomValue(splineContainer, addKnowCount);
            }
        }

        [Button("2D line Knowのランダム追加")]
        private void AddRandom2DLineKnot(int addKnowCount)
        {
            foreach (var splineContainer in movePointList)
            {
                AddSplineKnot(splineContainer, addKnowCount);
                AddKnotAt2DLineRandomValue(splineContainer, addKnowCount);
            }
        }

        [Button("Spline Know Reset")]
        private void RemoveAllKnot()
        {
            foreach (var splineContainer in movePointList)
            {
                splineContainer.RemoveSpline(splineContainer.Spline);
            }
        }
    }

取得

以下の画像のように任意のSplineの最後のknot情報を取得したいときは以下のようにして取得します。

_splineContainer.Splines[_splineIndex].Knots.Last();

参考サイト

nekojara.city

docs.unity3d.com