カスタム アセット タイプを編集する

プロジェクト内のリソース ファイルにデータを保存するためのプラグインが必要な場合は、エディタに付属している多数の標準編集機能およびツールを利用して、他の Stingray リソースと連携することができます。たとえば、Property Editor で編集可能なプラグインのリソース ファイルを作成して、元に戻す/やり直しなどを自動実行することができます。

そのためには、アセットの各インスタンスが保持できる情報の種類を記述するカスタムの SJSON 形式を使用して、このアセットの新しい「タイプ」を定義します。タイプを定義したら、エディタに組み込まれたメカニズムを使用してカスタム タイプのリソースを作成および編集することができます。プラグインはリソース内のデータと連携することができるため、ユーザがリソースのインスタンスを作成、編集、および保存したメカニズムをエディタが考慮するように設定できます。

これにより、比較的簡単な方法でエディタ専用のプロジェクトに新しいタイプのアセットを追加し、他の種類のプロジェクト データを作成できるようになります。たとえば、ブラシが散乱させるユニット、頻度、散乱の方向といった、散乱ブラシに関するデータをリソース ファイルに保存する場合に、このメカニズムが使用されます。ただし、この情報がエディタによって参照されるのは、ブラシがアクティブなときにレベル内にユニットを配置する方法や場所を定義する場合のみです。新しいタイプのアセットをエンジン内でランタイムに使用できるようにする必要がある場合は、新しいアセット タイプをランタイム用にコンパイルする方法をプラグイン内で定義し、このプラグインを使用してエンジンを拡張する必要があります。「エンジンを拡張する」も参照してください。

このページでは、カスタム SJSON リソースでエディタの既存のアセット編集フレームワークを再利用する方法について説明します。

手順 1. type ファイルを作成する

最初に、新しいリソース ファイルに保存する必要があるデータの .type ファイルを作成します。このファイルは、一連の組み込みタイプ、および構築ブロックとして定義されたその他のタイプを使用して、データの構造を定義します。詳細については、「Stingray タイプ システム」を参照してください。

定義したタイプを定義済みデータ構造のインスタンスが格納されているリソース ファイルにリンクするには、使用するタイプに extension フィールドが含まれていることを確認する必要があります。たとえば、scatter_brush.type ファイルのタイプを .scatter_brush 拡張子にリンクする方法は、次のようになります。

export = "#scatter_brush"
extension = "scatter_brush"
types = {
    scatter_brush = {
        ...
    }

Property Editor でリソースを編集可能にする

新しいリソース タイプを Property Editor 内で編集できるようにする場合は、.type ファイルから書き出されたタイプに対して editor メタデータ ブロックも追加する必要があります。このブロック内に generic_selector キーを追加します。このキーの値が true の場合、使用するタイプのリソースを Asset Browser で選択すると、Property Editor が応答してこのアセット内にデータを表示し、ユーザはデータ値を編集できるようになります。

// from scatter_brush.type

export = "#scatter_brush"
extension = "scatter_brush"
types = {
    scatter_brush = {
        ...
        editor = {
            label = "Scatter Brush"
            // This specifies that .scatter_brush files should be editable using the Property Editor:
            generic_selector = true
            ...
        }
    }
    ...

手順 2. type ファイルをマウントする

.type ファイルがプロジェクト内でマウントされていることを確認する必要があります。そのためには、通常、resources 拡張機能に type ファイルのパスを設定します。この詳細については、「プロジェクト コンテンツを拡張する」を参照してください。

手順 3. 新しいアセットの作成方法を設定する

エディタを使用すると、Asset Browser でカスタム タイプの新しいリソースを作成できます。

エディタにこの機能を設定する方法は 2 つあります。

一般的な作成ツール

使用するタイプに一般的な作成ツールが使用されている場合は、使用するタイプの新しいアセットを Asset Browser で作成するたびに、新しいアセットのすべてのフィールドに対し、.type ファイルに設定されている既定値が使用されます。使用するタイプのフィールドに既定値が指定されていない場合は、このフィールドの基本タイプの既定値が使用されます。

一般的な作成ツールを使用するには、書き出したタイプの editor メタデータ セクションに generic_creator キーを追加し、この値を true に設定します。

たとえば、散乱ブラシはこのメカニズムを次のように使用します。

// from scatter_brush.type

export = "#scatter_brush"
extension = "scatter_brush"
types = {
    scatter_brush = {
        type = ":struct"
        fields = {
            components = {
                key = ":guid"
                type = ":dict"
                value = "#scatter_component"
                editor = {
                    control = "ResourceCarousel"
                    carouselResourceProperty = "unit"
                }
            }
        }
        editor = {
            label = "Scatter Brush"
            // This specifies that scatter brush should have a creation menu item:
            generic_creator = true

            // This specifies that .scatter_brush files should be editable using the Property Editor:
            generic_selector = true
            version = "0.1.0"
        }
    }
    ...

テンプレート作成

使用するタイプの新しいアセットを、.type ファイル内の既定値に対応していない特定の値セットを使用して作成する必要がある場合は、プラグイン用の template 拡張機能を設定する必要があります。こうすると、Asset Browser に上記の一般的な作成ツールのメカニズムと同じアセット作成メニュー オプションが追加されますが、新しいアセットは異なる方法で作成されます。新しいアセットを作成するには、通常、プラグインによって提供される既存リソースをコピーします。ただし、必要に応じてプラグイン内の JavaScript モジュールの付属関数を呼び出すように template 拡張機能で実行されるアクションを設定することにより、JavaScript 内で新しいアセットをプラグラムで作成することができます。

templates 拡張機能の詳細については、「新しいアセットのテンプレートを定義する」を参照してください。

たとえば、capture_frames プラグインではテンプレート システムを使用して、新しい .capture_settings アセットを作成します。

// From capture_frames.stingray_plugin
templates = [{
        type = "asset"
        name = "Capture Settings"
        default_name = "capture_settings"
        extension = "capture_settings"
        create = [
            "create-frame-capture \"$name.capture_settings\" \"$project/$output_dir\""
        ]
    }]

template 拡張機能を使用して新しいアセットを作成する場合は、リソースの generic_creator キーを false に設定する必要があります。

// Exerpt from capture_settings.type:
 editor = {
            label = "Capture Settings"
            generic_creator = false // No generic creator
            generic_selector = true // but do use Property Editor
            version = "1.0.0"
        ...
 }

キャプチャ設定には generic_selector 設定が保持されるため、これらのアセットを Property Editor で編集することができます。

アセットに対する変更内容をリッスンする

プラグインは、ユーザが Property Editor でカスタム アセットに行った変更内容をリッスンすることができます。リッスン後、ユーザはエディタ環境内でこれらの変更に直ちに対応して、フィードバックを即座に提出することができます。たとえば、スライダをドラッグしたときに、Asset Preview または Level Viewport を更新するなどです。

この操作を行うには、使用するプラグインで、object-editing-service から発行された DataObjectsConsensusChanged イベントをリッスンする必要があります。

例については、editor¥plugins¥blend_shapes¥blend_shapes¥preview-blend-shapes.js ファイルを参照してください。次に示すスクリプトは、ブレンド シェイプに対する変更を処理するイベント リスナーを追加し、Asset Preview に表示されるブレンド ウェイトを更新して応答します。

// From preview-blend-shapes.js

setup: function (config, asset, viewportName) {
    objectEditingService.on("DataObjectsConsensusChanged", function (args) {
            // Each time a Custom asset changes, a change descriptor notification is sent.
            var changeDesc = args[0];
            if (this._previewedAsset !== changeDesc.AssetsModified[0] || changeDesc.Type !== 'ValueChanged') {
                return;
            }

            var path = changeDesc.PropertyPath;
            var separatorIndex = path.lastIndexOf('.');
            var channel = path;
            if (separatorIndex !== -1) {
                channel = path.substr(separatorIndex + 1);
            }

            previewUtils.sendToViewport(this._viewportName, 'set_blend_shape_channel_weight_percentage', luaUtils.toSyntax(channel), luaUtils.toSyntax(changeDesc.Value));
        }));

発行されたイベントには変更記述子が付随していて、その中に、Property Editor でユーザが行った変更の種類に関する情報が含まれています。この記述子は、次のプロパティを持つオブジェクトです。

{
    Type: string,                 // One of: ValueAdded, ValueRemoved, or ValueChanged
    PropertyPath: string,         // JSON property path that was modified in the custom asset
    Value: object,                // The value that was set or added
    AssetsModified: Array<string> // The list of assets that were modified (in case of consensus editing)
}

カスタム アセット データを取得する

asset-service を使用すると、エディタがプロジェクトからメモリにロードしたカスタム アセットの JSON 値をクエリーすることができます。

// From preview-blend-shapes.js

// Get the managed (in memory representation) of a particular blend shape.
assetService.getManagedAsset(assetName + ".blend_shapes").then(function (dataObject){
    if (dataObject) {
        // Fetches the Json representation of this asset (extract Json from the DataObject).
        return dataObject.invokeMethod('ToJson').then(function (blendShapeJsonData) {
            ...
        })
    } else {
        console.error("Cannot find data for: ", assetName + ".blend_shapes");
    }
});

JavaScript コードでカスタム アセット タイプのファイルをプラグラムで操作する場合は、他のエディタ サービスを利用できます。特に、file-system-servicereadJSON() および writeJSON() 関数を提供します。ユーザはこれらを使用して、データ ファイルからのアセットのインスタンスの読み取り、およびプロジェクト フォルダ内のファイルへの書き込みを処理することができます。

カスタム エディタ アセットの例

Stingray エディタには、上記のように .type ファイルで定義されたカスタム アセット タイプがいくつか付属しています。これらの例を使用すると、組み込みのアセット編集システムでリソースの作成と編集を簡易化する方法を確認できます。

ブレンド シェイプ

ブレンド シェイプ プラグインは、ユーザがエディタ内でブレンド シェイプを操作およびプレビューする方法を定義します。Stingray でブレンド シェイプを操作する方法の背景情報については、メイン Stingray ヘルプを参照してください。

ブレンド シェイプ プラグインに関する注意事項:

このエディタ拡張機能は、ランタイムにシェイプ間のブレンドを処理するエンジン拡張機能によって補完されることにもご注意ください。

// From blend_shape.type

export = "#blend_shapes"
extension = "blend_shapes"
types = {
    blend_shapes = {
        type = ":struct"
        fields = {
            blend_shape_meshes = {
                type = ":value"  // :value means an arbitrary JSON value. The type system won't try to reason about it (or to validate it).
            }
            blend_shape_editor_channels = {
                type = ":value"
            }
        }
        editor = {
            label = "Blend shapes"
            generic_creator = false
            generic_selector = true
            version = "0.1.0"
            control = "BlendShapes"  // Use a custom blend-shape control in the Property Editor, defined in editor/core/components/properties/property-blend-shape.js.
        }
    }
}

散乱ブラシ

散乱ブラシは、別のユニットまたは地表にペイントする(「拡散」させる)ユニットのリストをカプセル化するエディタ専用のリソースです。Stingray で散乱ブラシを操作する方法の背景情報については、メインの Stingray ヘルプを参照してください。

散乱ブラシに関する注意事項:

上記の「アセットに対する変更内容をリッスンする」に記載されているように、levelEditingService は散乱ブラシに関する変更内容をリッスンして、Lua 散乱データ モデルを再ロードすることにもご注意ください。

objectEditingService.on("DataObjectsConsensusChanged", function (args) {
    if (this.SelectedToolName !== "scatter" ||
        !this.SelectedScatterBrush) {
        return;
    }

    var changeDesc = args[0];
    if (changeDesc.AssetsModified.indexOf(this.SelectedScatterBrush) === -1) {
        return;
    }

    this.invoke('UpdateLuaScatterBrushComponents');
}.bind(this));

キャプチャ設定

キャプチャ設定は、ビューポートからのイメージのストリームをキャプチャし、一連の .exr ファイルとして結果を保存するためのパラメータを指定する、エディタ専用のリソースです。この使用方法では、カスタム アセット タイプを使用して Property Editor を専用編集ツールのように機能させる方法を示します。Stingray で散乱ブラシを操作する方法の背景情報については、メインの Stingray ヘルプを参照してください。

キャプチャ設定に関する注意事項:

// From capture_settings.type
export = "#capture_settings"
extension = "capture_settings"
types = {
    capture_settings = {
        type = ":struct"
        fields = {
            ...
            start_capture = {
                type = ":bool"
                default = true
                editor = {
                    // Specification of the Action button that kicks in the Capture process.
                    control = "Action"
                    label = "Render Frames to Disk"
                    text = "Start Capturing"
                    color = "#CC3D3D"
                    trigger = {
                        type = "js"
                        module = "capture_frames/capture_frames_actions"
                        function_name = "handleCapture"
                    }
                }
            }
        }
        editor = {
            label = "Capture Settings"
            generic_creator = false
            generic_selector = true
            version = "1.0.0"
            init = {
                type = "js"
                module = "capture_frames/capture_frames_actions"
                function_name = "startCaptureSettingsEditing"
            }
            cleanup = {
                type = "js"
                module = "capture_frames/capture_frames_actions"
                function_name = "endCaptureSettingsEditing"
            }
        }
    }
}