Page 1 of 1

弱い接続について

Posted: 2018/08/07 07:23
by kudou
わたしは村人の会話の処理をArborで作成しております。
プレイヤーが話かけると村人の状態に応じて反応するシステムです。
動作するFSMはつくれたのですが、いくつかアドバイスを頂けると助かります。

基本処理と特殊処理にわかれており、まず特殊処理が行われるか判定します。
特殊処理が行われた場合は基本処理をスキップし、逆に特殊処理を行わなかった場合は基本処理を行います。
この処理が下記のFSMになります。
●FSM1
Image
このFSMは村人それぞれがコンポーネントで持っております。
工夫した点として動的にStateMachineを切り替えられるようにSubStateMachineReferenceのFlexible版の『FlexibleSubStateMachineReference』を作成しました。

下記のFSMは『FlexibleSubStateMachineReference』で実行されるキャラごとの特殊処理内の一例です。
●FSM2
Image
会話している村人を取得するための方法として、参照元のParameterContainerからデータを取得できるようにしました。
『OwnerBehaviourGameObjectCalculator』で参照元のGameObjectの取得、
『DynamicParamaterContainerCalculater』で文字列からパラメータを取得できるようにしました。
特にアドバイスを頂きたいのはこの部分なのですが、見ての通りひとつのパラメータに対して複数のoutputを定義しています。
今回はGameObjectだけ取得できればいいのですが、後々の汎用性を考えて他の型でもOutputできるようにと考えての実装です。

上記のことを踏まえて
①今回のような制約の弱い接続がもしすでに実装されていたら教えていただけますか?
もし、ない場合に要望できますでしょうか?
Ⅰ、どんなInputSlotにでも接続できるOutputSlot
Ⅱ、ParameterContainerのパラメータを文字列で取得できる。また、設定できる

②もし他にもっとよさそうな方法があればアドバイス頂けないでしょうか
FSM2では呼び出し元に想定しているParameterContainerがあるという前提をもとに作成したものですが、
逆に呼び出し先に想定しているParameterContainerがあるという前提に作ったものも試しに作ってみました。
●FSM3
Image
●FSM4
Image
呼び出し先で制約の強い接続ができたのは良いですが、呼び出し元のoutputの部分が微妙な感じです。
どちらももう少しうまくできそうな気がしているのですが、私の力ではここまでが限界でした。

長々と申し訳ございません。
どうぞよろしくお願いいたします。

Re: 弱い接続について

Posted: 2018/08/07 09:30
by caitsithware
ご質問ありがとうございます。

要約すると、
  • ParameterContainerのパラメータ値を出力するCalculatorを作ろうとしている。
  • どのParameterContainerのなんてパラメータかは実行時までわからない。
  • 実行時に判明した型別にそれぞれOutputSlot用意するのはなんか微妙。
ということですね。
kudou wrote: 2018/08/07 07:23 Ⅰ、どんなInputSlotにでも接続できるOutputSlot
こちらについてですが、ご希望に添えるSlotは現在ありません。
OutputSlotAnyという似たようなクラスはありますが、型が実行時までわからない場合はInputSlotAnyとしか接続できません。
これはデータの接続を行う際に接続可能な型かを判定しているArborの仕様となっています。
スクリプトリファレンス: OutputSlotAny
スクリプトリファレンス: InputSlotAny
kudou wrote: 2018/08/07 07:23 Ⅱ、ParameterContainerのパラメータを文字列で取得できる。また、設定できる
こちらは、Arborの機能としてはありませんがUnityのJsonUtilityを介せば可能かもしれません。
ただ、本来想定していない方法なため、これ以上のサポートは難しそうです。
kudou wrote: 2018/08/07 07:23 ②もし他にもっとよさそうな方法があればアドバイス頂けないでしょうか
まず、実行時にしか型が決まらないというのが非常に難しい問題なので、本当に必要かどうか整理してみてください。
汎用性を捨ててでも実装しないといけない時も場合によってはあるので。

整理したうえでどうしても必要でしたら、OutputSlotを自作する方法があります。
また、安全性も考えるとパラメータと出力したい型が一致しているかもチェックしたいですね。
なので、
  • OutputSlotの型を指定できるMyOutputSlot(仮名)を自作する。
  • MyOutputSlotには、ClassTypeReference型のフィールドを持たせ、dataTypeプロパティのoverrideでその型を返す。
  • DynamicParamaterContainerCalculaterでは、MyOutputSlotを持たせて、出力する型とパラメータの型が一致するかどうかをチェックしつつ出力。
という感じが良いかと思います。

例 : MyOutputSlot.cs
型を自由に指定できるOutputSlot

Code: Select all

using UnityEngine;
using System.Collections;

using Arbor;

[System.Serializable]
public class MyOutputSlot : OutputSlotBase
{
	[SerializeField]
	private ClassTypeReference _Type = new ClassTypeReference();

	public override System.Type dataType
	{
		get
		{
			return _Type.type;
		}
	}

	public void SetValue(object value)
	{
		if (nodeGraph == null)
		{
			return;
		}

		int idCount = branchIDs.Count;
		for (int idIndex = 0; idIndex < idCount; idIndex++)
		{
			int branchID = branchIDs[idIndex];
			DataBranch branch = nodeGraph.GetDataBranchFromID(branchID);
			if (branch != null)
			{
				branch.value = value;
			}
		}
	}
}
例: Editor/MyOutputSlotPropertyDrawer.cs
MyOutputSlotの追加フィールドを表示するためのエディタ拡張。
エディタ拡張なのでEditorフォルダ以下に作成する必要があります。

Code: Select all

using UnityEngine;
using UnityEditor;
using System.Collections;

using Arbor;
using ArborEditor;

[CustomPropertyDrawer(typeof(MyOutputSlot))]
public class MyOutputSlotPropertyDrawer : DataSlotPropertyDrawer
{
	protected override float GetFieldsHeight(SerializedProperty property)
	{
		SerializedProperty typeProperty = property.FindPropertyRelative("_Type");
		return EditorGUI.GetPropertyHeight(typeProperty, new GUIContent(typeProperty.displayName));
	}

	protected override void OnFieldsGUI(Rect position, SerializedProperty property)
	{
		SerializedProperty typeProperty = property.FindPropertyRelative("_Type");
		EditorGUI.PropertyField(position, typeProperty, new GUIContent(typeProperty.displayName));
	}
}
例: DynamicParamaterContainerCalculater.cs

Code: Select all

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Arbor;

[AddComponentMenu("")]
public class DynamicParamaterContainerCalculater : Calculator
{
	[SerializeField]
	private ParameterContainer _ParameterContainer;

	[SerializeField]
	private string _ParameterName;

	[SerializeField]
	private MyOutputSlot _Output;

	// Use this for calculate
	public override void OnCalculate() {
		Parameter param = _ParameterContainer.GetParam(_ParameterName);
		if (param.valueType == _Output.dataType)
		{
			_Output.SetValue(param.value);
		}
		else
		{
			Debug.LogError("The type is different.");
		}
	}
}
コード例としてはこんな感じです。
厳密にはデバッグしていませんので、問題があったらすみません。

こちらは、必要性が十分にあるようでしたらArbor本体にも導入するか検討したいと思います。

Re: 弱い接続について

Posted: 2018/08/08 00:42
by kudou
ご返答ありがとうございます。
caitsithware wrote: 2018/08/07 09:30 こちらについてですが、ご希望に添えるSlotは現在ありません。
OutputSlotAnyという似たようなクラスはありますが、型が実行時までわからない場合はInputSlotAnyとしか接続できません。
これはデータの接続を行う際に接続可能な型かを判定しているArborの仕様となっています。
了解いたしました。
また、後出のMyOutputSlotでやりたかったことができそうです。
caitsithware wrote: 2018/08/07 09:30 こちらは、Arborの機能としてはありませんがUnityのJsonUtilityを介せば可能かもしれません。
ただ、本来想定していない方法なため、これ以上のサポートは難しそうです。
書き方が拙くてすみません。
上のFSMでいうところのDynamicParamaterContainerCalculatorやDynamicCalcParamaterのようなもののことです。(Paramaterは誤スペルで、本当はParameterですね)
ここでは文字列でParameterを取得して、InputSlotAnyで取得したものをobject型で設定しています。
型が違った場合の危険性がありますが、代わりに自由なアクセスができるようなパラメータの組み込みの挙動・演算があったりするのかという質問でした。

MyOutputSlotの件、ありがとうございます。
実際に作ってつかってみましたが、今回の実装でいく場合にoutputを複数定義する必要がなくなりました。

今回、SubStateMachineを実行時に切り替えられる場合のパラメータ受け渡し方法を考えていました。
プログラムでいうところのインターフェースと実装の関係をSubStateMachineReferenceでできると安全にパラメータを受け渡せるのかもしれません。(勝手なこと言ってすみません)
今回は上記のようなFSMで思ったとおりに動作しているので、これで様子を見ようと思います。

いろいろ無理を言ってお手数かけてすみません。
ありがとうございました。

Re: 弱い接続について

Posted: 2018/08/08 01:56
by caitsithware
ご確認ありがとうございます。
kudou wrote: 2018/08/08 00:42 MyOutputSlotでやりたかったことができそうです。
できそうとのことでよかったです。
kudou wrote: 2018/08/08 00:42 上のFSMでいうところのDynamicParamaterContainerCalculatorやDynamicCalcParamaterのようなもののことです。
パラメータ名を文字列で指定しての取得や設定するCalculatorやStateBehaviourについてですね。
Parameterを文字列化して型の違いに1クッション置く話かと勘違いしてしまいました。

パラメータ名を文字列で指定しての取得や設定に関しても現在ありません。
CalcParameterなどでのParameterContainerの参照をFlexibleComponentに置き換えて、パラメータ名の文字列指定にも対応できないかなど検討したいと思います。
kudou wrote: 2018/08/08 00:42 プログラムでいうところのインターフェースと実装の関係をSubStateMachineReferenceでできると安全にパラメータを受け渡せるのかもしれません。
最終的に、すでに要望が上がっている「SubStateMachineに引数と戻り値が欲しい」と似た案件になりますね。
合わせて良い方法が取れないか検討いたします。