カスタムアクターの実装
アクターは、名前、外観、可視性、およびトランスフォーム(位置、回転、スケール)によって定義されるシーンエンティティです。外観、可視性、およびトランスフォームを時間の経過とともに非同期に変更できます。アクターの例には、キャラクター、背景、テキストプリンター、選択肢ハンドラーなどがあります。
アクターは IActor インターフェースとその派生インターフェースによって表されます。
ICharacterActorIBackgroundActorITextPrinterActorIChoiceHandlerActor
各アクターインターフェースは複数の実装を持つことができます。たとえば、キャラクターアクターには現在、11個の組み込み実装があります:プレースホルダー、スプライト、ダイススプライト、ユニバーサル、レイヤー、汎用、ビデオ、Live2D、Spine、ナレーター、一時的。
アクターの実装は、Naninovel -> Configuration エディターメニューからアクセスできる構成マネージャーで選択できます。すべてのアクターに使用されるデフォルトの実装を変更したり、アクターごとに特定の実装を設定したりできます。デフォルトの実装を変更するには Default Metadata プロパティを使用し、個別の実装を設定するにはアクターの構成の Implementation ドロップダウンを使用します。
Implementation ドロップダウンには、特定のアクターインターフェースを実装するすべての型が含まれています。独自のカスタム実装を追加することもでき、それらもリストに表示されます。独自のアクター実装を作成する際の参考として、Naninovel/Runtime/Actor スクリプトを参照してください。アクターをシーンにスポーンすることを想定している場合は、基本インターフェースの要件の大半を満たす組み込みの抽象実装 MonoBehaviourActor の使用を検討してください。
カスタムアクター実装を作成するときは、互換性のあるパブリックコンストラクターがあることを確認してください。
public ActorImplementationType (string id, ActorMetadata metadata) { }— ここで id はアクターのIDであり、metadata はアクターのメタデータ(リソースにアクターレコードが存在する場合)またはデフォルトのメタデータのいずれかです。特定のアクターインターフェースを実装する場合、対応する特定のメタデータ(たとえば、ICharacterActor 実装の場合は CharacterMetadata)を要求することができます。
例
すべての組み込みアクター実装は同じアクターAPIに基づいて作成されているため、独自の実装を追加する際の参考にすることができます。ソースはNaninovelパッケージの Runtime/Actor ディレクトリにあります。
アクターリソース
実装の型に ActorResources 属性を適用して、カスタムアクターのリソースとして使用できるアセットと、エディターメニューで複数のリソースを割り当てることができるかどうかを指定します。複数のリソースが許可されていない場合(デフォルト)、アクターIDのみを指定して、利用可能な唯一のリソースをロードできます。例:
var resource = await resourceLoader.Load(actorId);複数のリソースが許可されている場合は、フルパスを指定します。たとえば、CubeBackground という名前のリソースを割り当てたとします。
— リソースをロードするには、次を使用します。
var resource = await resourceLoader.Load($"{actorId}/CubeBackground");カスタムメタデータ
組み込み実装とカスタム実装のどちらでも、アクターメタデータに独自のデータを追加できます。
カスタムデータを注入するには、新しいC#クラスを作成し、CustomMetadata<TActor> を継承します。ここで TActor は、データを関連付けるアクター実装の型です。以下は、CustomCharacterImplementation のキャラクターにカスタムデータを追加する例です。
using Naninovel;
using UnityEngine;
public class MyCharacterData : CustomMetadata<CustomCharacterImplementation>
{
public int MyCustomInt;
[Range(0f, 100f), Tooltip("Tooltip for my custom range.")]
public float MyCustomRange = 50f;
[ColorUsage(false, true)]
public Color MyCustomColor = Color.white;
}対応する実装のアクターを選択すると、作成したカスタムデータクラスのシリアル化可能なフィールドが、Naninovelエディターメニューに自動的に表示されます。
実行時にカスタムデータにアクセスするには、ActorMetadata インスタンスの GetCustomData<TData>() メソッドを使用します。ここで TData はカスタムデータクラスの型です。例:
var charsConfig = Engine.GetConfiguration<CharactersConfiguration>();
var myCharMeta = charsConfig.GetMetadataOrDefault("CharId");
var myCharData = myCharMeta.GetCustomData<MyCharacterData>();
Debug.Log(myCharData.MyCustomInt);カスタムメタデータエディター
プロパティドロワー を介してカスタムメタデータエディターをカスタマイズすることが可能です。以下は、編集対象のフィールドの上に追加のラベルを挿入するプロパティドロワーを追加する例です。
// シリアル化されたフィールドに適用する属性を作成します。
// `PropertyAttribute` から継承することを忘れないでください。
public class ExtraLabelAttribute : PropertyAttribute
{
public readonly string LabelText;
public ExtraLabelAttribute (string labelText)
{
LabelText = labelText;
}
}
// 対象のフィールドを描画するときに使用されるカスタムエディターを作成します。
// `UnityEditor` APIを使用するため、スクリプトは「Editor」フォルダーに置いてください。
[CustomPropertyDrawer(typeof(ExtraLabelAttribute))]
public class ExtraLabelPropertyDrawer : PropertyDrawer
{
public override void OnGUI (Rect rect, SerializedProperty prop, GUIContent label)
{
var extraLabelAttribute = attribute as ExtraLabelAttribute;
rect.height = EditorGUIUtility.singleLineHeight;
EditorGUI.LabelField(rect, extraLabelAttribute.LabelText);
rect.y += EditorGUIUtility.singleLineHeight +
EditorGUIUtility.standardVerticalSpacing;
EditorGUI.PropertyField(rect, prop);
}
public override float GetPropertyHeight (SerializedProperty prop, GUIContent label)
{
return EditorGUIUtility.singleLineHeight * 2 +
EditorGUIUtility.standardVerticalSpacing;
}
}
// これで、属性がシリアル化されたフィールドに追加のラベルを適用できます。
public class MyCharacterData : CustomMetadata<CustomCharacterImplementation>
{
[ExtraLabel("Text from my custom property drawer")]
public string MyCustomProperty;
}上記の実装により、カスタムキャラクターデータは次のように描画されます。
ヒント
組み込み構成エディター全体をオーバーライドすることも可能です。詳細と例については、カスタム構成 ガイドを参照してください。
カスタム状態
カスタムアクターの状態の型をオーバーライドまたは拡張するには、アクターのマネージャーもオーバーライド する必要があります。状態のシリアル化と管理対象アクターへの適用は、マネージャーで行われるためです。
注意
これは、組み込みの IActor 派生インターフェース(キャラクター、背景、テキストプリンター、選択肢ハンドラー)のいずれかを実装するカスタムアクターに当てはまります。カスタムアクターを IActor から直接継承した場合は、カスタム状態を使用するために組み込みマネージャーをオーバーライドする必要はありません。カスタム状態の型を指定した独自のマネージャーを作成するだけで済みます。
他のシステム(例:Naninovel外部のさまざまなゲームメカニクスのためのUI、ゲームオブジェクト、またはコンポーネント)にカスタム状態を追加する場合は、状態管理ガイド を参照してください。
以下は、最後に選択肢が追加された時刻を格納する LastChoiceTime フィールドを追加して選択肢ハンドラーの状態を拡張する例です。この時刻は、カスタム選択肢ハンドラーが表示されたときにコンソールに出力されます。
// 最後の選択肢の時刻をシリアル化する拡張状態。
public class MyChoiceHandlerState : ChoiceHandlerState
{
// このフィールドはシリアル化可能であり、ゲームのセーブ・ロードをまたいで保持されます。
public string LastChoiceTime;
// このメソッドはゲームをセーブするときに呼び出されます。
// アクターから必要なデータを取得し、シリアル化可能なフィールドに保存します。
public override void OverwriteFromActor (IChoiceHandlerActor actor)
{
base.OverwriteFromActor(actor);
if (actor is MyCustomChoiceHandler myCustomChoiceHandler)
LastChoiceTime = myCustomChoiceHandler.LastChoiceTime;
}
// このメソッドはゲームをロードするときに呼び出されます。
// シリアル化されたデータを取り出し、アクターに適用します。
public override async Awaitable ApplyToActor (IChoiceHandlerActor actor)
{
await base.ApplyToActor(actor);
if (actor is MyCustomChoiceHandler myCustomChoiceHandler)
myCustomChoiceHandler.LastChoiceTime = LastChoiceTime;
}
}
// 最後の選択肢の時刻を使用するカスタム選択肢ハンドラー実装。
public class MyCustomChoiceHandler : UIChoiceHandler
{
public string LastChoiceTime { get; set; }
public MyCustomChoiceHandler (string id, ChoiceHandlerMetadata meta,
EmbeddedAppearanceLoader<GameObject> loader) : base(id, meta, loader) { }
public override void AddChoice (Choice choice)
{
base.AddChoice(choice);
LastChoiceTime = DateTime.Now.ToShortTimeString();
}
public override Awaitable ChangeVisibility (bool visible, Tween tween,
AsyncToken token = default)
{
Debug.Log($"Last choice time: {LastChoiceTime}");
return base.ChangeVisibility(visible, tween, token);
}
}
// 拡張状態を使用するように組み込み選択肢ハンドラーマネージャーをオーバーライドします。
// 重要なのは、ジェネリック型に `MyChoiceHandlerState` を指定することです。
// 他の変更は、インターフェースの要件を満たすためだけのものです。
[InitializeAtRuntime(@override: typeof(ChoiceHandlerManager))]
public class MyChoiceHandlerManager : ActorManager<IChoiceHandlerActor,
MyChoiceHandlerState, ChoiceHandlerMetadata,
ChoiceHandlersConfiguration>, IChoiceHandlerManager
{
public MyChoiceHandlerManager (ChoiceHandlersConfiguration config)
: base(config) { }
public Awaitable<IChoiceHandlerActor> AddActor (string actorId,
ChoiceHandlerState state)
{
return base.AddActor(actorId, state as MyChoiceHandlerState);
}
ChoiceHandlerState IActorManager<IChoiceHandlerActor,
ChoiceHandlerState, ChoiceHandlerMetadata,
ChoiceHandlersConfiguration>.GetActorState (string actorId)
{
return base.GetActorState(actorId);
}
}これで、カスタム選択肢ハンドラーは最後に選択肢が追加された時刻を保持し、その選択肢がセーブスロットからロードした以前のゲームセッションで追加されたものであっても、コンソールにログ出力するようになります。この方法で、組み込みのアクター状態に加えて任意の量のカスタムデータを保存できます。サポートされているシリアル化可能なデータ型については、Unityのシリアル化ガイド を参照してください。