Page 1 of 1

カスタム常駐ステートから始まる一連のStateフロー実行中に、他の常駐ステートを無効化する方法について

Posted: 2019/06/26 04:12
by thorikawa
現在、TapTransitionという、オブジェクトのタップに反応する常駐ステート用StateBehaviourを作成し、キャラクターをタップするとある一連のイベント(State)が開始されるというStateMachineを作っています。シーン上にこのようにタップに反応するオブジェクトが複数あり、あるオブジェクトに対してTapから始まるStateフローを実行中に、他のオブジェクトに対するTapからのStateフローが発生すると、Stateフローが混在してしまうため、これを改善する方法がないか模索しています。

具体的には、TapTransitionから始まる一連のStateを実行中には他のTapTransition常駐ステートを無効化する方法があればよいなと思っているのですが、この「TapTransitionから始まる一連のStateを実行中に」というのを判定する方法に悩んでいまして、よい方法があればご教授いただきたいです。たとえば常駐State(TapTransition)->StateA->StateBというフローになっているときに、StateBで行うべきことが全て完了しているかどうかなどをAPIで判定する方法はありますでしょうか?なお、StateMachineを組む際の負担をなるべく減らしたいので、StateMachineは変えずに、内部的なスクリプトの変更で対応できるのがベストだと考えています。

質問が伝わりづらいかもしれず大変申し訳ないのですが、ご確認頂けますと幸いです。
よろしくお願い致します。

Re: カスタム常駐ステートから始まる一連のStateフロー実行中に、他の常駐ステートを無効化する方法について

Posted: 2019/06/26 04:50
by caitsithware
まず、その仕様を見る限り、常駐ステートである必要性をあまり感じないのですが、TapTransitionからの挙動とは別に同FSM内には通常の状態遷移も行われている、ということで良いのでしょうか。
(例えば、敵AIがパトロール中にオブジェクトをタップすると現在の動作に関係なく割り込みでタップに反応する動作を開始する等であれば、
常駐ステートを使う必要はありますが、割り込みが発生しないのであれば使う利点はあまりありません。)

もし、タップからの状態遷移のみでしたら、開始ステートに各オブジェクトのタップに対するTapTransitionを置き、完了し次第開始ステートに戻る、という組み方を検討してみてください。

何らかの理由で常駐ステートを使わざるを得ない状況でしたら、こちらではひとまず以下の2点の方法を思いつきました。
  1. ParameterContainerや普通のクラス変数などでTapTransitionをロックするbool値を用意しておき、遷移してから完了するまでロックを有効にする。
    TapTransitionスクリプトではTransition()呼び出しする前に、ParameterContainerもしくはクラス変数の値を見て判定。
  2. TapTransitionをロックしたいステート名の先頭に"[TapLock]"と付けておき、ステート名によりロック中か判定する。
    TapTransitionスクリプトではTransition()呼び出しする前に、stateMachine.currentState.name.StartsWith("[TapLock]")を見て判定。
注意点:

Arborそのものには「タップ後のステートの何をもって完了とするか」が分からず、thorikawaさまのプロジェクト固有の仕様上の問題であるため、
これらの案はあくまで今回聞いた内容からわかる範囲での一般的な回答となっています。
参考程度に見ていただけると幸いです。

Re: カスタム常駐ステートから始まる一連のStateフロー実行中に、他の常駐ステートを無効化する方法について

Posted: 2019/06/26 06:46
by thorikawa
ご返信ありがとうございます。

TapTransitionが常駐ステートである件ですが、他に通常の状態遷移も行うことはほぼないので、おっしゃるように常駐ステートである必要はありません。ただ開始ステートに設定したとしても、「Object1に対するTapTransitionが開始ステートであるFSM」と「Object2に対するTapTransitionが開始ステートであるFSM」の2つのFSMが存在する場合、Object1のタップで開始される一連のStateが実行されている最中に、Object2がTapされると同時に両イベントが同時実行される問題は依然解決されないと思っています。

> Arborそのものには「タップ後のステートの何をもって完了とするか」が分からず、
の件ですが、各ステートに(ちょっと曖昧な言い方になりますが)自分の役割を果たしたタイミングがあると思っていて、たとえば「AgentFollow」だったら目的地に到達した瞬間、「TimeTransition」だったら指定した時間が経過した瞬間を越えたStateかどうかを汎用的なAPIで判別できたら、「一連のState」が完了したかどうかを自動判断できて便利だなと考えていました。(ずっと実行され続けるStateもあると思いますが、そういったものはとりあえず除いて)もちろん明示的に後続Stateをおいてパラメータをセットしたりしてもよいのですが、人手の作業だとどうしても抜け漏れが発生してしまうので、システムで自動判別できたらよいなという願望でした。

ご提示の方法で、ステート名に接頭辞をつける方法は、人手の作業が必要ですが、ぱっと見でわかりやすいよい方法だなと思いました。こちらを元に検討してみます!

Re: カスタム常駐ステートから始まる一連のStateフロー実行中に、他の常駐ステートを無効化する方法について

Posted: 2019/06/26 06:58
by thorikawa
>> Arborそのものには「タップ後のステートの何をもって完了とするか」が分からず、
> の件ですが、各ステートに(ちょっと曖昧な言い方になりますが)自分の役割を果たしたタイミングがあると思っていて、たとえば「AgentFollow」だったら目的地に到達した瞬間、「TimeTransition」だったら指定した時間が経過した瞬間を越えたStateかどうかを汎用的なAPIで判別できたら、「一連のState」が完了したかどうかを自動判断できて便利だなと考えていました。(ずっと実行され続けるStateもあると思いますが、そういったものはとりあえず除いて)もちろん明示的に後続Stateをおいてパラメータをセットしたりしてもよいのですが、人手の作業だとどうしても抜け漏れが発生してしまうので、システムで自動判別できたらよいなという願望でした。

上記について補足ですが、より具体的には
StateBehaviour.isTaskFinished() みたいな共通のAPIが定義されていて、それを叩くとステートの実質的な作業は完了しているかどうかを取得できる(実装はStateによるが呼び出し側は意識しなくてよい)と便利だなと思いました。

Re: カスタム常駐ステートから始まる一連のStateフロー実行中に、他の常駐ステートを無効化する方法について

Posted: 2019/06/26 08:01
by caitsithware
thorikawa wrote: 2019/06/26 06:46 TapTransitionが常駐ステートである件ですが、他に通常の状態遷移も行うことはほぼないので、おっしゃるように常駐ステートである必要はありません。ただ開始ステートに設定したとしても、「Object1に対するTapTransitionが開始ステートであるFSM」と「Object2に対するTapTransitionが開始ステートであるFSM」の2つのFSMが存在する場合、Object1のタップで開始される一連のStateが実行されている最中に、Object2がTapされると同時に両イベントが同時実行される問題は依然解決されないと思っています。
タップを処理するFSMが複数ある、ということなのですね。
単一のFSMで複数のオブジェクトからのタップを処理していて、各オブジェクトごとのタップ用処理中に常駐ステートからの遷移をロックしたい、という話かと思っていました。

複数FSMとなると、実行中の関連FSMすべてのcurrentState.nameをチェックしないといけないので、案②も若干面倒そうですね。
グラフ上での視認性を考慮しない&設計上の細部を気にしないのであれば、TapTransitionに static bool tapLocked; などのstatic変数でやってしまったほうが楽かもしれません。

例えば以下のような感じ。
  • 常駐ステートをやめて、開始ステートでTapTransitionの形式にする。
  • TapTransitionでTransition呼び出すときの条件としてロックされていないかどうか判定
  • Transition呼び出したらロックする。
  • OnStateBeginに戻ってきたときやロックを解除したいタイミングなどでロック解除
コード的にはこんな感じ。

Code: Select all

public static bool tapLocked= false;

public override OnStateBegin() 
{
    tapLocked = false;
}

public override OnStateUpdate()
{
    if( タップした && !tapLocked ) {
        Transition(stateLink);
        tapLocked = true;
    }
}
どちらにしても、ロック解除をいつ行うかは仕様次第なので、ロックを意識したFSM設計にせざるを得ないとは思います。

長くなってしまうので続きます。

Re: カスタム常駐ステートから始まる一連のStateフロー実行中に、他の常駐ステートを無効化する方法について

Posted: 2019/06/26 08:15
by caitsithware
thorikawa wrote: 2019/06/26 06:58 >> Arborそのものには「タップ後のステートの何をもって完了とするか」が分からず、
> の件ですが、各ステートに(ちょっと曖昧な言い方になりますが)自分の役割を果たしたタイミングがあると思っていて、たとえば「AgentFollow」だったら目的地に到達した瞬間、「TimeTransition」だったら指定した時間が経過した瞬間を越えたStateかどうかを汎用的なAPIで判別できたら、「一連のState」が完了したかどうかを自動判断できて便利だなと考えていました。(ずっと実行され続けるStateもあると思いますが、そういったものはとりあえず除いて)もちろん明示的に後続Stateをおいてパラメータをセットしたりしてもよいのですが、人手の作業だとどうしても抜け漏れが発生してしまうので、システムで自動判別できたらよいなという願望でした。

上記について補足ですが、より具体的には
StateBehaviour.isTaskFinished() みたいな共通のAPIが定義されていて、それを叩くとステートの実質的な作業は完了しているかどうかを取得できる(実装はStateによるが呼び出し側は意識しなくてよい)と便利だなと思いました。
こちらについては、ひとまずArborの動作としては
  • もしかしたら各種挙動が役割を完了した「瞬間」に遷移する可能性がある
    (元から遷移しないタイプもあれば、遷移するタイプでも接続していなければステートに残り続ける場合もある)
  • “動作が完了した状態”は一種のステートと言えるので、グラフ上でも必要であればステートで表現する
という想定になっています。

AgentFollowもTimeTransitionも、完了したら遷移する構造になっているのは、完了した後はもうその時点で「別の状態」だからです。

「完了した瞬間」は各種組み込みスクリプトには確かにありますが、それ=ステートが完了しているとは言い難く、また今回の例でのタップ後の動作の完了は一つの状態(ステート)と言えるので、素直に「タップ動作完了ステート」を作成するのをお勧めしたいです。

もし、タップ後の動作用ステートが増えていった場合も完了ステート前に追加していくだけで済みますし、
スクリプトで決め打ちで書いたとしても仕様変更の度に判定文を変えていく必要があり、そこもミスにつながりやすいですし、
ステート名などに決め事を付けるとその時点で完了ステート作成とそんなに変わりませんよね。
まあ、どのケースが一番かはプロジェクトに関わってない私が言えることではないので判断はお任せしますが。

isTaskFinishedについては、今のところ各種組み込みスクリプトについても個別に用意しているだけなので、統一的なAPIにより完了したことを伝える機能は提供していない感じです。

また新規にisTaskFinishedなどを追加するとしても、既存ユーザーさんのスクリプトの設計にまで影響を与えてしまうので、StateBehaviour直に追加するというのもやらないかと思います。
今回の件を元に今後何らかの形で対応するかもしれませんが、特殊な事例かと思いますので期待しないでおいてください。

こちらからはこのような感じとなります。
今回の件については、ひとまず機能的にも存在しないので、何かしらthorikawa様のプロジェクト内で対応するようにお願いいたします。

Re: カスタム常駐ステートから始まる一連のStateフロー実行中に、他の常駐ステートを無効化する方法について

Posted: 2019/06/27 11:45
by thorikawa
丁寧に回答頂きありがとうございます。

Arborの設計思想理解致しました。

今回の件ですが、ご提案頂いた内容を鑑みて、
1. TapTransitionにstatic変数を追加し、他のTapTransitionを有効にするかどうかを制御。
2. 新たにEnableTapTransitionというStateBehaviourを作成し、TapTransitionのstatic変数を変更できるようにする。それを有効にしたいタイミング(主にTapTransitionから始まる一連のフローの最後)に設置。
3. EnableTapTransitionの設置し忘れを防止するため、すべてのFSMのフローの流れをチェックするEditor拡張を作成
という形にまとめました。

アドバイス頂きまして大変ありがとうございました。