SlideShare ist ein Scribd-Unternehmen logo
1 von 85
Downloaden Sie, um offline zu lesen
わからないまま使っている?
UE4 の AI の基本的なこと
らりほま
2018/02/25 Unreal Engine Meetup Nagoya #6 in 名古屋城
自己紹介
 らりほま (Twitter : @rarihoma)
 株式会社ヒストリアのエンジニア
 UE4 歴 3 年
 エンジニア歴 1 年
 Gray ちゃん 3D モデルデータ配布中
 http://rarihoma.xvs.jp/products/graychan
講演のテーマ『AI』
 注意
 やたらと地味で細かい内容です
 間違っている部分があればご指摘お願いします
 Unreal Engine 4.18.3 で検証しています
 今日話さないこと
 すごく賢い AI の作り方 (← むしろ自分が知りたい)
 Navigation
 Sensing
 EQS
UE4 の AI の資料
 公式ドキュメント
 ビヘイビアツリーのクイックスタート ガイド | Unreal Engine
 https://docs.unrealengine.com/latest/JPN/Engine/AI/BehaviorTrees/QuickStart/ind
ex.html
 動画
 はじめてのAI~ 愛のあるAIを作ろう - YouTube
 https://www.youtube.com/watch?v=gT8uuc0DxWk&t=1225s
 スクウェア・エニックスにおける UNREAL ENGINE 4 を用いた人工知能技術の開発
事例 - YouTube
 https://www.youtube.com/watch?v=BV2GTGbSjq8
 書籍
 Unreal Engine 4 で極めるゲーム開発
 作れる!学べる!Unreal Engine 4 ゲーム開発入門
目次
 UE4 の AI の雑な解説
 UserBlackboard() と RunBehaviorTree() の動作
 Blackboard の Key の初期値
 Behavior Tree Task の通常版と AI 版の Event の違い
UE4 の AI の雑な解説
UE4 の AI の雑な解説
 BehaviorTree
 挙動を定義
 中断処理を
組みやすい
UE4 の AI の雑な解説
 Blackboard
 変数を定義
 変数のことを
Key と呼ぶ
UE4 の AI の雑な解説
 AIController で UseBlackboard() と RunBehaviorTree() を実行
→ AIController が所有 (Possess) している Pawn が
BehaviorTree に従って動作する
UseBlackboard() と
RunBehaviorTree() の動作
UseBlackboard() と
RunBehaviorTree() に関する疑問
 2 つの Node は具体的に何を行っているのか?
 2 つの Node の ReturnValue は何を返しているのか?
UseBlackboard() と
RunBehaviorTree() の動作
Blueprint で検証
検証 :
AIController の Component を見る
 UseBlackboard() は BlackboardComponent を返しているので、
RunBehaviorTree() も何らかの Component を付加しているのでは?
→ AIController に付加された Component を
各 Node の実行前・実行後で比較する
検証 :
AIController の Component を見る
 AIController に最初から付加されている Component
検証 :
AIController の Component を見る
 UseBlackboard() 実行後
 BlackboardComponent が付加される
検証 :
AIController の Component を見る
 UseBlackboard() → RunBehaviorTree() 実行後
 BTComponent が付加される
検証 :
AIController の Component を見る
 UseBlackboard() は実行せずに
RunBehaviorTree() だけ実行後
 !?!?!?
 BTComponent だけでなく
BlackboardComponent も付加される
UseBlackboard() と
RunBehaviorTree() の動作
エンジンソースを読む
UseBlackboard() のソースを読む
 (/Runtime/AIModule/Private/AIController.cpp line:995)
UseBlackboard() のソースを読む
 ソースからわかること
 UBlackboardComponent がなければ AAIController に追加する
 UBlackboardComponent が既にある場合は、
追加せずに既存のものを初期化する
 UseBlackboard() を何度も呼び出しても
UBlackboardComponent は 1 つだけしか付加されない
 UBlackboardData (Blackboard の Asset) が異なる場合、
新しく指定された方で初期化される
 引数 BlackboardAsset が nullptr のときのみ false を返す
 return される変数 bSuccess が
初期値 true から変更される機会が実は存在しない
RunBehaviorTree() のソースを読む
 (/Runtime/AIModule/Private/AIController.cpp line:889)
RunBehaviorTree() のソースを読む
 ソースからわかること
 UBehaviorTreeComponent がなければ AAIController に追加する
 RunBehaviorTree() を何度も呼び出しても
UBehaviorTreeComponent は 1 つだけしか付加されない
RunBehaviorTree() のソースを読む
 ソースからわかること
 引数 BTAsset に Blackboard Asset が指定されていて、
かつ UBlacokboardComponent が存在しないか
存在していても互換性がない場合、
UseBlackboard() が実行される
 UseBlackboard() Node を実行しなくても
UBlackboardComponent が追加されていたのはこのため
 既存の Blackboard の Asset が
BTAsset が指定する Blackboard Asset の親である場合、
または両者の Key がすべて同一であれば、
互換性があると見なされる
 要するに、これから実行する BehaviorTree 内で使われる可能性のある Key が
既に存在する UBlackboardComponent 内に無いのは困るので、
その場合は UBlackboardComponent を作り直してしまえ、ということ
RunBehaviorTree() のソースを読む
 ソースからわかること
 引数 BTAsset が NULL のときのみ false を返す
 bSuccess = UseBlackboard(~); という行は
UseBlackboard() が常に true を返す状況でしか呼ばれないため、
return される変数 bSuccess は初期値 true から変更されない
実例 1
 UseBlackboard() は
呼び出さずに
RunBehaviorTree() だけ
呼び出した場合
 BehaviorTree に
BlackboardAsset が
設定されているので、
内部で UseBlackboard() が走って
UBlackboardComponent が
生成され、問題なく動作する
実例 2
 UseBlackboard() は
呼び出さずに
RunBehaviorTree() だけ
呼び出した場合
 BehaviorTree に
BlackboardAsset が
設定されていないので、
内部で UseBlackboard() が走らず
UBlackboardComponent が
生成されないので、
アクセス違反を起こす可能性がある
実例 3
 RunBehaviorTree() で実行される BehaviorTree と
UseBlackboard() で既に生成された Blackboard とで
Blackboard Asset の互換性がない場合
 UBlackboardComponent が初期化され、データが消し飛ぶ
互換性なし
実例 4
 RunBehaviorTree() で実行される BehaviorTree と
UseBlackboard() で既に生成された Blackboard とで
Blackboard Asset の互換性がある場合
 UBlackboardComponent のデータは維持される
 さらに、子の Blackboard にしか存在しない Key も使える
互換性あり
(子 - 親 の関係)
実例 5
 上と下の Node 群はほぼ同義
まとめ
 2 つの Node は具体的に何を行っているのか?
 UseBlackboard() は UBlackboardComponent を、
RunBehaviorTree() は UBehaviorTreeComponent を生成する
 RunBehaviorTree() は UBlackboardComponent が存在しない場合には
UseBlackboard() を呼び出すので、
UseBlackboard() を明示的に呼び出す必要はない
 ただし BehaviorTree に Blackboard Asset が設定されている必要がある
 BehaviorTree に設定されている Blackboard Asset の子を使いたい場合や
RunBehaviorTree() の呼び出し前に Key の Set を行いたい場合は、
UseBlackboard() を明示的に呼び出す必要がある
 両方共、何回呼び出しても対応する Component は 1 つしか生成されない
 複数回呼び出しても、無視されるか、既存のものが新しい設定で初期化される
まとめ
 2 つの Node の ReturnValue は何を返しているのか?
 引数が None であるときに false を返している
 IsValid() で事前に判定している場合、利用する必要はない
Blackboard の Key の初期値
Key の初期値に関する疑問
 Blueprint の変数には、初期値を設定することができる
 一方、Blackboard の Key には、初期値を設定するところがない
 Blackboard の Key の初期値はどうなっている?
Blackboard の Key の初期値
Blueprint で検証
初期値の検証 1
 Blackboard で扱うことができるすべての Type の Key を作成し、
Editor での Play 中に値を確認してみる
 Enum : UNKNOWN! とは?
 String : n/a とは?
 Rotator / Vector : (invalid) とは?
初期値の検証 2
 Key を GetBlackboardValueAs○○() で取得して PrintString() してみる
初期値の検証 2
 Key を GetBlackboardValueAs○○() で取得して PrintString() してみる
 Enum : (Byte の) 0
 String : 空の文字列
 Rotator / Vector : !?!?!?
初期値の検証
 ひとまず初期値はわかった
 が、Rotator / Vector は
何故こんな値なのか?
 意図的な設定なのか
 たまたまメモリに
残っていた値を
解釈したらこうなったのか
Blackboard の Key の初期値
エンジンソースを読む
Key の Type 毎の処理定義
 Blackboard の Key に対する処理は、
UBlackboardKeyType を継承した
UBlackboardKeyType_○○ で規定されている
 (/Runtime/AIModule/Classes/BehaviorTree/Blackboard/BlackboardKeyType.h)
 GetValue() : 値の取得
 SetValue() : 値の設定
 InitializeMemory() : 値の初期化
 例 :
Bool Key の値を取得するときの処理内容は
UBlackboardKeyType_Bool::GetValue()
Vector Key の初期化処理
 Vector Key の値を初期化するときの処理内容は
UBlackboardKeyType_Vector::InitializeMemory()
 FAISystem::InvalidLocation を Set している
 (/Runtime/AIModule/Private/BehaviorTree/Blackboard/BlackboardKeyType_Vector.cpp line:56)
Vector Key の初期化処理
 FAISystem::InvalidLocation は {x, y, z} が float の最大値を取る値
 (/Runtime/AIModule/Classes/AITypes.h line:24)
 float の最大値は 2 の 128 乗 ≒ 3.402823466e+38
→ 謎の巨大な値の正体
 つまり、謎の巨大な値は意図的に設定されたものである
Vector Key の初期値の取り扱い
ということは…
 初期値をそのまま計算に使わないように注意する必要がある
 例えば、FVector::Normalize() を実行すると
途中計算で x * x した時点で不定値 (–nan(ind)) が生じる
Vector Key の初期値の取り扱い
 Vector Key が初期値かどうか判定する方法は?
→ IsVectorValueSet() を使う
 (/Runtime/AIModule/Private/BehaviorTree/BlackboardComponent.cpp line:671)
 名前が Key Name である Vector Key が
謎の巨大な値であれば false を返す実装になっている
 初期値を計算に利用してしまうのを避けるために使える
 IsRotatorValueSet() など、他の Type 版は存在しない
Vector Key の初期値の取り扱い
 Vector Key を初期値に戻す方法は?
→ ClearValue() を使う
 (/Runtime/AIModule/Private/BehaviorTree/BlackboardComponent.cpp line:689)
 Vector 以外の Type の Key に対しても使える
 UBlackboardKeyType_○○::Clear() を間接的に呼び出して
Key に特定の値を Set する実装になっている
 Key にどのような値が Set されるかは実装次第だが、
標準で用意されている Type においては
初期値が Set される実装になっているため、
初期値に戻す関数として考えて差し支えない
Vector Key の初期値の取り扱い
 謎の巨大な値を『無効な値』として扱うと便利なことがある
 例: 目標地点が設定されていればそこへ移動する Behavior Tree Task
Vector Key の初期値の取り扱い
 謎の巨大な値を『無効な値』として扱うと便利なことがある
 例: 目標地点が設定されていればそこへ移動する Behavior Tree Task
まとめ
 Key の初期値は規定されている
 不定値ではない
 Blueprint の変数の初期値と一致するとは限らない
 Rotator / Vector Key は初期値として『無効な値』扱いの
巨大な値が設定されるので、
そのまま計算に使わないように注意
 IsVectorValueSet() で初期値でないことを確認してから使うなどする
 Vector Key においては
初期値を『無効な値』として活用することもできる
余談 1 : Blackboard の Key の
データはどこにある?
 UBlackboardComponent のメンバである
TArray<uint8> ValueMemory に格納される
 色々な Type の変数が一緒くたに格納される
 UBlackboardComponent::GetValue() の実装などを参照
 (/Runtime/AIModule/Classes/BehaviorTree/BlackboardComponent.h line:355)
 Blackboard Key を TArray 化できないのは
このような実装になっているためだと思われる
 BlackboardKeyType_○○ はあくまで
Key の取り扱いを規定するもので、
この Class の Instance がデータを持つわけではない
余談 2 : 存在しない名前の Key を
Get しようとすると何が返る?
 UBlackboardKeyType_○○::InvalidValue として
定義されたものが返る
 Key の初期値と InvalidValue は
同一のものが設定されている模様
 よって、存在しない Key の名前を与えて
GetValueAsVector() を呼び出すと謎の巨大な値が返る
 ただし、初期値と InvalidValue を別の値にすることは
実装上可能なので、常に等しいとは限らない点に注意
Behavior Tree Task の
通常版と AI 版の Event の違い
通常版と AI 版の Event?
 Behavior Tree Task の Overridable な Event のこと
 例 : Receive Tick 系の場合
 ReceiveTick() : 通常版
 ReceiveTickAI() : AI 版
通常版と AI 版の比較
 どっちを使えばいいの?
 AI を作るんだから AI 版?
 でも Behavior Tree って AI を作るためのものなのでは?
 通常版は何のために存在するのか?
 両方実装したらどちらが呼び出される?両方?
 通常版の Owner Actor は何を指すの?
Behavior Tree Task の
通常版と AI 版の Event の違い
Blueprint で検証
検証 1 : どちらが呼び出されるか
 AIController で RunBehaviorTree() を行い
以下のような Behavior Tree Task を実行する
 片方を実装しなかったり両方実装したりして、
どのように実行されるかを見る
検証 1 : どちらが呼び出されるか
 AI 版だけ実装 → AI 版が実行される
 通常版だけ実装 → 通常版が実行される
検証 1 : どちらが呼び出されるか
 両方実装 → AI 版だけが実行される
検証 1 : どちらが呼び出されるか
 AI 版は置くだけで何のノードも繋がない → ???
(虚無)
検証 1 : どちらが呼び出されるか
 AI 版は置くだけで何の Node も繋がない → AI 版が呼び出される
検証 1 : どちらが呼び出されるか
 わかったこと
 通常版と AI 版はどちらか片方だけが実行される
 両方実装されている場合、AI 版の実行が優先される
 Event に Node を繋いでいなくても実装されている扱いになる
検証 2 :
通常版の Owner Actor は何を指す?
 GetDisplayName() を使って名前を取得し PrintString()
→ AIController でした
Behavior Tree Task の
通常版と AI 版の Event の違い
エンジンソースを読む
通常版と AI 版の呼び分け部分を読む
 例として ReceiveTick 系の呼び分けを見ていく
 UBTTask_BlueprintBase::TickTask() で
通常版と AI 版の呼び分けを行っている
 (/Runtime/AIModule/Private/BehaviorTree/Tasks/BTTask_BlueprintBase.cpp line:98)
通常版と AI 版の呼び分け部分を読む
 以下の 2 つの式が両方 true だと AI 版が呼ばれる
 AIOwner != nullptr
 (ReceiveTickImplementations &
FBTNodeBPImplementationHelper::AISpecific)
通常版と AI 版の呼び分け部分を読む
 AIOwner != nullptr について
 AIController* AIOwner は
UBTTask_BlueprintBase::SetOwner() にて変更の機会がある
 (/Runtime/AIModule/Private/BehaviorTree/Tasks/BTTask_BlueprintBase.cpp line:39)
 AActor* ActorOwner には引数 InActorOwner がそのまま入る
 AIOwner は InActorOwner を Cast() した結果が入るので、
InActorOwner が AIController* でない場合は nullptr となる
通常版と AI 版の呼び分け部分を読む
 AIOwner != nullptr について
 UBTTask_BlueprintBase::SetOwner() は
UBTNode::InitializeInSubtree() にて呼び出しの機会がある
 (/Runtime/AIModule/Private/BehaviorTree/BTNode.cpp line:68)
 UBehaviorTreeComponent の Owner が引数として渡される
 つまり、UBehaviorTreeComponent を持つ Actor が
AAIController であるとき、式は true となる
ReceiveTick 系の挙動を追う
 (ReceiveTickImplementations &
FBTNodeBPImplementationHelper::AISpecific) について
 uint32 ReceiveTickImplementations には
FBTNodeBPImplementationHelper::CheckEventImplementationVersion()
の返却値が入る
 (/Runtime/AIModule/Private/BehaviorTree/Tasks/BTTask_BlueprintBase.cpp line:12)
 CheckEventImplementationVersion() は
2 つの指定した名前の Blueprint Function が実装されているかどうかを調べて
bit フラグ形式で結果を返す
ReceiveTick 系の挙動を追う
 (ReceiveTickImplementations &
FBTNodeBPImplementationHelper::AISpecific) について
 uint32 ReceiveTickImplementations には
FBTNodeBPImplementationHelper::CheckEventImplementationVersion()
の返却値が入る
 (/Runtime/AIModule/Private/BehaviorTree/Tasks/BTTask_BlueprintBase.cpp line:12)
 今回の場合、具体的には以下の値が返る
 0 (2 進数 00) : 通常版も AI 版も実装されていない
 1 (2 進数 01) : 通常版 (ReceiveTick) だけ実装されている
 2 (2 進数 10) : AI 版 (ReceiveTickAI) だけ実装されている
 3 (2 進数 11) : 両方実装されている
 つまり、(通常版の実装の有無に依らず)
AI 版が実装されていれば、式は true となる
ReceiveTick 系の挙動を追う
 条件式まとめ
 UBehaviorTreeComponent を持つ Actor が AAIController で、
かつ AI 版が実装されていれば、AI 版が呼び出される
 ReceiveTick 系を例に見てきたが、ReceiveExecute 系など他も同じ
新たな疑問
 UBehaviorTreeComponent を持つ Actor が
AAIController ではない状況なんてあるの?
 AAIController 以外の Actor に
UBehaviorTreeComponent を付けても動作するの?
AAIController 以外に
UBehaviorTreeComponent を付ける
 Blueprint では UBehaviorTreeComponent を生成・追加する
Node が見当たらなかったので、C++ でやってみた
AAIController 以外に
UBehaviorTreeComponent を付ける
 Pawn 継承 Class に UBehaviorTreeComponent を追加できた
AAIController 以外に
UBehaviorTreeComponent を付ける
 UBehaviorTreeComponent の Owner の名前と、
通常版と AI 版のどちらを呼んでいるかを表示する
Behavior Tree Task を作成
AAIController 以外に
UBehaviorTreeComponent を付ける
 UBehaviorTreeComponent を
無理矢理 Pawn に付けたものと
普通に RunBehaviorTree() で付けたものの
出力を比較する
 無理矢理 Pawn に付けた方は、
AI 版が実装されているにもかかわらず
通常版の方が呼び出されていることがわかる
まとめ
 通常版と AI 版、どっちを使えばいいの?
 ほとんどの場合、AI 版オンリーでよい
 AI 版の方が引数が具体的で便利
 通常版だとわざわざ Cast() する手間がかかる場合が多い
 一番避けるべきなのは、両方ごちゃ混ぜで使うこと
まとめ
 通常版の Owner Actor は何を指すの?
 Behavior Tree Task を動作させている
UBehaviorTreeComponent の Owner である Actor
 UBehaviorTreeComponent を
AAIController 以外の Actor に付けて動作させることは可能
 中断処理を組みやすい BehaviorTree の構造を
上手く活用できるケースがあるかも
 ただし AIMoveTo() のような経路探索系処理は
Pawn を所有する AIController の存在を前提としているため
使用は難しい
本当に言いたかったこと
検証をしよう!
 各項目の流れは以下のような感じでした
 疑問を持つ
 Blueprint で簡単に検証してみる
 新規プロジェクトを作成し、なるべく小さく作って検証する
 エンジンソースを読んで動作を理解する
 気合い
検証をしよう!
 わからないものをわからないまま使い続けていると…
検証をしよう!
 いつか困るかもしれません
 『わからないことをわからないままにできない』ということが、
UE4 を趣味から仕事にしたときに一番大きく変化した部分でした
 UE4 はわからなくても「なんとなく」でできてしまう面があります
 検証して、理解を深めて、情報を共有していきましょう!
検証をしよう!
 ※ 趣味で小規模にやる分にはそこまで考えなくていいです
 参考 : 何もかもわかっていない状態で作った 3 年前のぷちコン作品
ご清聴ありがとうございました!

Weitere ähnliche Inhalte

Was ist angesagt?

UE4におけるキャラクタークラス設計
UE4におけるキャラクタークラス設計UE4におけるキャラクタークラス設計
UE4におけるキャラクタークラス設計Masahiko Nakamura
 
そう、UE4ならね。あなたのモバイルゲームをより快適にする沢山の冴えたやり方について Part 2 <Texture Streaming, メモリプロ...
  そう、UE4ならね。あなたのモバイルゲームをより快適にする沢山の冴えたやり方について Part 2 <Texture Streaming, メモリプロ...  そう、UE4ならね。あなたのモバイルゲームをより快適にする沢山の冴えたやり方について Part 2 <Texture Streaming, メモリプロ...
そう、UE4ならね。あなたのモバイルゲームをより快適にする沢山の冴えたやり方について Part 2 <Texture Streaming, メモリプロ...エピック・ゲームズ・ジャパン Epic Games Japan
 
ゆるゆるUE4ネットワーク入門
ゆるゆるUE4ネットワーク入門ゆるゆるUE4ネットワーク入門
ゆるゆるUE4ネットワーク入門ssuser221848
 
UE4でAIとビヘイビアツリーと-基礎-
UE4でAIとビヘイビアツリーと-基礎-UE4でAIとビヘイビアツリーと-基礎-
UE4でAIとビヘイビアツリーと-基礎-com044
 
UE4のためのより良いゲーム設計を理解しよう!
UE4のためのより良いゲーム設計を理解しよう!UE4のためのより良いゲーム設計を理解しよう!
UE4のためのより良いゲーム設計を理解しよう!Masahiko Nakamura
 
UE4 コリジョン検証 -HitとOverlapイベントが発生する条件について-
UE4 コリジョン検証 -HitとOverlapイベントが発生する条件について-UE4 コリジョン検証 -HitとOverlapイベントが発生する条件について-
UE4 コリジョン検証 -HitとOverlapイベントが発生する条件について-Tatsuya Iwama
 
Editor Utility Widgetで色々便利にしてみた。
Editor Utility Widgetで色々便利にしてみた。Editor Utility Widgetで色々便利にしてみた。
Editor Utility Widgetで色々便利にしてみた。IndieusGames
 

Was ist angesagt? (20)

バイキング流UE4活用術 ~BPとお別れするまでの18ヶ月~
バイキング流UE4活用術 ~BPとお別れするまでの18ヶ月~バイキング流UE4活用術 ~BPとお別れするまでの18ヶ月~
バイキング流UE4活用術 ~BPとお別れするまでの18ヶ月~
 
UE4におけるキャラクタークラス設計
UE4におけるキャラクタークラス設計UE4におけるキャラクタークラス設計
UE4におけるキャラクタークラス設計
 
そう、UE4ならね。あなたのモバイルゲームをより快適にする沢山の冴えたやり方について Part 2 <Texture Streaming, メモリプロ...
  そう、UE4ならね。あなたのモバイルゲームをより快適にする沢山の冴えたやり方について Part 2 <Texture Streaming, メモリプロ...  そう、UE4ならね。あなたのモバイルゲームをより快適にする沢山の冴えたやり方について Part 2 <Texture Streaming, メモリプロ...
そう、UE4ならね。あなたのモバイルゲームをより快適にする沢山の冴えたやり方について Part 2 <Texture Streaming, メモリプロ...
 
メカアクションゲーム『DAEMON X MACHINA』 信念と血と鋼鉄の開発事例
メカアクションゲーム『DAEMON X MACHINA』 信念と血と鋼鉄の開発事例メカアクションゲーム『DAEMON X MACHINA』 信念と血と鋼鉄の開発事例
メカアクションゲーム『DAEMON X MACHINA』 信念と血と鋼鉄の開発事例
 
カリギュラオーバードーズにおけるUE4へのデータ移植の手引き
カリギュラオーバードーズにおけるUE4へのデータ移植の手引きカリギュラオーバードーズにおけるUE4へのデータ移植の手引き
カリギュラオーバードーズにおけるUE4へのデータ移植の手引き
 
ゆるゆるUE4ネットワーク入門
ゆるゆるUE4ネットワーク入門ゆるゆるUE4ネットワーク入門
ゆるゆるUE4ネットワーク入門
 
UE4.25 Update - Unreal Insights -
UE4.25 Update - Unreal Insights -UE4.25 Update - Unreal Insights -
UE4.25 Update - Unreal Insights -
 
Nintendo Switch『OCTOPATH TRAVELER』はこうして作られた
Nintendo Switch『OCTOPATH TRAVELER』はこうして作られたNintendo Switch『OCTOPATH TRAVELER』はこうして作られた
Nintendo Switch『OCTOPATH TRAVELER』はこうして作られた
 
UE4におけるレベル制作事例
UE4におけるレベル制作事例  UE4におけるレベル制作事例
UE4におけるレベル制作事例
 
UE4でAIとビヘイビアツリーと-基礎-
UE4でAIとビヘイビアツリーと-基礎-UE4でAIとビヘイビアツリーと-基礎-
UE4でAIとビヘイビアツリーと-基礎-
 
UE4 MultiPlayer Online Deep Dive 基礎編1 -Getting Started- (historia様ご講演) #UE4DD
UE4 MultiPlayer Online Deep Dive 基礎編1 -Getting Started-  (historia様ご講演) #UE4DDUE4 MultiPlayer Online Deep Dive 基礎編1 -Getting Started-  (historia様ご講演) #UE4DD
UE4 MultiPlayer Online Deep Dive 基礎編1 -Getting Started- (historia様ご講演) #UE4DD
 
UE4のためのより良いゲーム設計を理解しよう!
UE4のためのより良いゲーム設計を理解しよう!UE4のためのより良いゲーム設計を理解しよう!
UE4のためのより良いゲーム設計を理解しよう!
 
UE4 コリジョン検証 -HitとOverlapイベントが発生する条件について-
UE4 コリジョン検証 -HitとOverlapイベントが発生する条件について-UE4 コリジョン検証 -HitとOverlapイベントが発生する条件について-
UE4 コリジョン検証 -HitとOverlapイベントが発生する条件について-
 
[CEDEC2017] UE4プロファイリングツール総おさらい(グラフィクス編)
[CEDEC2017] UE4プロファイリングツール総おさらい(グラフィクス編)[CEDEC2017] UE4プロファイリングツール総おさらい(グラフィクス編)
[CEDEC2017] UE4プロファイリングツール総おさらい(グラフィクス編)
 
猫でも分かるUE4のポストプロセスを使った演出・絵作り
猫でも分かるUE4のポストプロセスを使った演出・絵作り猫でも分かるUE4のポストプロセスを使った演出・絵作り
猫でも分かるUE4のポストプロセスを使った演出・絵作り
 
Unreal Engine 4.27 ノンゲーム向け新機能まとめ
Unreal Engine 4.27 ノンゲーム向け新機能まとめUnreal Engine 4.27 ノンゲーム向け新機能まとめ
Unreal Engine 4.27 ノンゲーム向け新機能まとめ
 
UE4におけるエフェクトの為のエンジン改造事例
UE4におけるエフェクトの為のエンジン改造事例UE4におけるエフェクトの為のエンジン改造事例
UE4におけるエフェクトの為のエンジン改造事例
 
猫でも分かるUMG
猫でも分かるUMG猫でも分かるUMG
猫でも分かるUMG
 
60fpsアクションを実現する秘訣を伝授 基礎編
60fpsアクションを実現する秘訣を伝授 基礎編60fpsアクションを実現する秘訣を伝授 基礎編
60fpsアクションを実現する秘訣を伝授 基礎編
 
Editor Utility Widgetで色々便利にしてみた。
Editor Utility Widgetで色々便利にしてみた。Editor Utility Widgetで色々便利にしてみた。
Editor Utility Widgetで色々便利にしてみた。
 

Ähnlich wie わからないまま使っている?UE4 の AI の基本的なこと

Active Directoryデータの "大きい整数"
Active Directoryデータの "大きい整数"Active Directoryデータの "大きい整数"
Active Directoryデータの "大きい整数"Michio Koyama
 
What, Why, How Create OSS Libraries - 過去に制作した30のライブラリから見るC#コーディングテクニックと個人OSSの...
What, Why, How Create OSS Libraries - 過去に制作した30のライブラリから見るC#コーディングテクニックと個人OSSの...What, Why, How Create OSS Libraries - 過去に制作した30のライブラリから見るC#コーディングテクニックと個人OSSの...
What, Why, How Create OSS Libraries - 過去に制作した30のライブラリから見るC#コーディングテクニックと個人OSSの...Yoshifumi Kawai
 
Gura プログラミング言語の紹介
Gura プログラミング言語の紹介Gura プログラミング言語の紹介
Gura プログラミング言語の紹介Yutaka Saito
 
ゲームの自動テストを 作ってみた
ゲームの自動テストを 作ってみたゲームの自動テストを 作ってみた
ゲームの自動テストを 作ってみたYuusuke Takeuchi
 
Introduction to Continuous Test Runner MakeGood
Introduction to Continuous Test Runner MakeGoodIntroduction to Continuous Test Runner MakeGood
Introduction to Continuous Test Runner MakeGoodAtsuhiro Kubo
 
Cakephpstudy5 hacks jp
Cakephpstudy5 hacks jpCakephpstudy5 hacks jp
Cakephpstudy5 hacks jpHiroki Shimizu
 
Getting Started with Testing using PHPUnit
Getting Started with Testing using PHPUnitGetting Started with Testing using PHPUnit
Getting Started with Testing using PHPUnitAtsuhiro Kubo
 
Ec cube開発合宿 プラグインセミナー
Ec cube開発合宿 プラグインセミナーEc cube開発合宿 プラグインセミナー
Ec cube開発合宿 プラグインセミナーAyumu Kawaguchi
 
PHP, JavaScriptプログラマのためのC#入門
PHP, JavaScriptプログラマのためのC#入門PHP, JavaScriptプログラマのためのC#入門
PHP, JavaScriptプログラマのためのC#入門Tomo Mizoe
 
APASEC 2013 - ROP/JIT を使わずに DEP/ASLR を回避する手法を見てみた。
APASEC 2013 - ROP/JIT を使わずに DEP/ASLR を回避する手法を見てみた。APASEC 2013 - ROP/JIT を使わずに DEP/ASLR を回避する手法を見てみた。
APASEC 2013 - ROP/JIT を使わずに DEP/ASLR を回避する手法を見てみた。Satoshi Mimura
 
Kubeflowで何ができて何ができないのか #DEvFest18
Kubeflowで何ができて何ができないのか #DEvFest18Kubeflowで何ができて何ができないのか #DEvFest18
Kubeflowで何ができて何ができないのか #DEvFest18Shunya Ueta
 
An Internal of LINQ to Objects
An Internal of LINQ to ObjectsAn Internal of LINQ to Objects
An Internal of LINQ to ObjectsYoshifumi Kawai
 
Deep dive into oss written in swift
Deep dive into oss written in swiftDeep dive into oss written in swift
Deep dive into oss written in swiftYuki Asai
 
Deep dive into oss written in swift
Deep dive into oss written in swiftDeep dive into oss written in swift
Deep dive into oss written in swiftYuki Asai
 

Ähnlich wie わからないまま使っている?UE4 の AI の基本的なこと (20)

Aedlabo program 20150125
Aedlabo program 20150125Aedlabo program 20150125
Aedlabo program 20150125
 
Active Directoryデータの "大きい整数"
Active Directoryデータの "大きい整数"Active Directoryデータの "大きい整数"
Active Directoryデータの "大きい整数"
 
What, Why, How Create OSS Libraries - 過去に制作した30のライブラリから見るC#コーディングテクニックと個人OSSの...
What, Why, How Create OSS Libraries - 過去に制作した30のライブラリから見るC#コーディングテクニックと個人OSSの...What, Why, How Create OSS Libraries - 過去に制作した30のライブラリから見るC#コーディングテクニックと個人OSSの...
What, Why, How Create OSS Libraries - 過去に制作した30のライブラリから見るC#コーディングテクニックと個人OSSの...
 
Gura プログラミング言語の紹介
Gura プログラミング言語の紹介Gura プログラミング言語の紹介
Gura プログラミング言語の紹介
 
ゲームの自動テストを 作ってみた
ゲームの自動テストを 作ってみたゲームの自動テストを 作ってみた
ゲームの自動テストを 作ってみた
 
PHPコアから読み解くPHP5.5
PHPコアから読み解くPHP5.5PHPコアから読み解くPHP5.5
PHPコアから読み解くPHP5.5
 
Introduction to Continuous Test Runner MakeGood
Introduction to Continuous Test Runner MakeGoodIntroduction to Continuous Test Runner MakeGood
Introduction to Continuous Test Runner MakeGood
 
Cakephpstudy5 hacks jp
Cakephpstudy5 hacks jpCakephpstudy5 hacks jp
Cakephpstudy5 hacks jp
 
Getting Started with Testing using PHPUnit
Getting Started with Testing using PHPUnitGetting Started with Testing using PHPUnit
Getting Started with Testing using PHPUnit
 
Ec cube開発合宿 プラグインセミナー
Ec cube開発合宿 プラグインセミナーEc cube開発合宿 プラグインセミナー
Ec cube開発合宿 プラグインセミナー
 
PHP, JavaScriptプログラマのためのC#入門
PHP, JavaScriptプログラマのためのC#入門PHP, JavaScriptプログラマのためのC#入門
PHP, JavaScriptプログラマのためのC#入門
 
APIKit
APIKitAPIKit
APIKit
 
boost - std - C#
boost - std - C#boost - std - C#
boost - std - C#
 
APASEC 2013 - ROP/JIT を使わずに DEP/ASLR を回避する手法を見てみた。
APASEC 2013 - ROP/JIT を使わずに DEP/ASLR を回避する手法を見てみた。APASEC 2013 - ROP/JIT を使わずに DEP/ASLR を回避する手法を見てみた。
APASEC 2013 - ROP/JIT を使わずに DEP/ASLR を回避する手法を見てみた。
 
Kubeflowで何ができて何ができないのか #DEvFest18
Kubeflowで何ができて何ができないのか #DEvFest18Kubeflowで何ができて何ができないのか #DEvFest18
Kubeflowで何ができて何ができないのか #DEvFest18
 
Introduction of Python
Introduction of PythonIntroduction of Python
Introduction of Python
 
An Internal of LINQ to Objects
An Internal of LINQ to ObjectsAn Internal of LINQ to Objects
An Internal of LINQ to Objects
 
Deep dive into oss written in swift
Deep dive into oss written in swiftDeep dive into oss written in swift
Deep dive into oss written in swift
 
Deep dive into oss written in swift
Deep dive into oss written in swiftDeep dive into oss written in swift
Deep dive into oss written in swift
 
Unityで覚えるC#
Unityで覚えるC#Unityで覚えるC#
Unityで覚えるC#
 

わからないまま使っている?UE4 の AI の基本的なこと

  • 1. わからないまま使っている? UE4 の AI の基本的なこと らりほま 2018/02/25 Unreal Engine Meetup Nagoya #6 in 名古屋城
  • 2. 自己紹介  らりほま (Twitter : @rarihoma)  株式会社ヒストリアのエンジニア  UE4 歴 3 年  エンジニア歴 1 年  Gray ちゃん 3D モデルデータ配布中  http://rarihoma.xvs.jp/products/graychan
  • 3. 講演のテーマ『AI』  注意  やたらと地味で細かい内容です  間違っている部分があればご指摘お願いします  Unreal Engine 4.18.3 で検証しています  今日話さないこと  すごく賢い AI の作り方 (← むしろ自分が知りたい)  Navigation  Sensing  EQS
  • 4. UE4 の AI の資料  公式ドキュメント  ビヘイビアツリーのクイックスタート ガイド | Unreal Engine  https://docs.unrealengine.com/latest/JPN/Engine/AI/BehaviorTrees/QuickStart/ind ex.html  動画  はじめてのAI~ 愛のあるAIを作ろう - YouTube  https://www.youtube.com/watch?v=gT8uuc0DxWk&t=1225s  スクウェア・エニックスにおける UNREAL ENGINE 4 を用いた人工知能技術の開発 事例 - YouTube  https://www.youtube.com/watch?v=BV2GTGbSjq8  書籍  Unreal Engine 4 で極めるゲーム開発  作れる!学べる!Unreal Engine 4 ゲーム開発入門
  • 5. 目次  UE4 の AI の雑な解説  UserBlackboard() と RunBehaviorTree() の動作  Blackboard の Key の初期値  Behavior Tree Task の通常版と AI 版の Event の違い
  • 6. UE4 の AI の雑な解説
  • 7. UE4 の AI の雑な解説  BehaviorTree  挙動を定義  中断処理を 組みやすい
  • 8. UE4 の AI の雑な解説  Blackboard  変数を定義  変数のことを Key と呼ぶ
  • 9. UE4 の AI の雑な解説  AIController で UseBlackboard() と RunBehaviorTree() を実行 → AIController が所有 (Possess) している Pawn が BehaviorTree に従って動作する
  • 11. UseBlackboard() と RunBehaviorTree() に関する疑問  2 つの Node は具体的に何を行っているのか?  2 つの Node の ReturnValue は何を返しているのか?
  • 13. 検証 : AIController の Component を見る  UseBlackboard() は BlackboardComponent を返しているので、 RunBehaviorTree() も何らかの Component を付加しているのでは? → AIController に付加された Component を 各 Node の実行前・実行後で比較する
  • 14. 検証 : AIController の Component を見る  AIController に最初から付加されている Component
  • 15. 検証 : AIController の Component を見る  UseBlackboard() 実行後  BlackboardComponent が付加される
  • 16. 検証 : AIController の Component を見る  UseBlackboard() → RunBehaviorTree() 実行後  BTComponent が付加される
  • 17. 検証 : AIController の Component を見る  UseBlackboard() は実行せずに RunBehaviorTree() だけ実行後  !?!?!?  BTComponent だけでなく BlackboardComponent も付加される
  • 20.
  • 21. UseBlackboard() のソースを読む  ソースからわかること  UBlackboardComponent がなければ AAIController に追加する  UBlackboardComponent が既にある場合は、 追加せずに既存のものを初期化する  UseBlackboard() を何度も呼び出しても UBlackboardComponent は 1 つだけしか付加されない  UBlackboardData (Blackboard の Asset) が異なる場合、 新しく指定された方で初期化される  引数 BlackboardAsset が nullptr のときのみ false を返す  return される変数 bSuccess が 初期値 true から変更される機会が実は存在しない
  • 23.
  • 24. RunBehaviorTree() のソースを読む  ソースからわかること  UBehaviorTreeComponent がなければ AAIController に追加する  RunBehaviorTree() を何度も呼び出しても UBehaviorTreeComponent は 1 つだけしか付加されない
  • 25. RunBehaviorTree() のソースを読む  ソースからわかること  引数 BTAsset に Blackboard Asset が指定されていて、 かつ UBlacokboardComponent が存在しないか 存在していても互換性がない場合、 UseBlackboard() が実行される  UseBlackboard() Node を実行しなくても UBlackboardComponent が追加されていたのはこのため  既存の Blackboard の Asset が BTAsset が指定する Blackboard Asset の親である場合、 または両者の Key がすべて同一であれば、 互換性があると見なされる  要するに、これから実行する BehaviorTree 内で使われる可能性のある Key が 既に存在する UBlackboardComponent 内に無いのは困るので、 その場合は UBlackboardComponent を作り直してしまえ、ということ
  • 26. RunBehaviorTree() のソースを読む  ソースからわかること  引数 BTAsset が NULL のときのみ false を返す  bSuccess = UseBlackboard(~); という行は UseBlackboard() が常に true を返す状況でしか呼ばれないため、 return される変数 bSuccess は初期値 true から変更されない
  • 27. 実例 1  UseBlackboard() は 呼び出さずに RunBehaviorTree() だけ 呼び出した場合  BehaviorTree に BlackboardAsset が 設定されているので、 内部で UseBlackboard() が走って UBlackboardComponent が 生成され、問題なく動作する
  • 28. 実例 2  UseBlackboard() は 呼び出さずに RunBehaviorTree() だけ 呼び出した場合  BehaviorTree に BlackboardAsset が 設定されていないので、 内部で UseBlackboard() が走らず UBlackboardComponent が 生成されないので、 アクセス違反を起こす可能性がある
  • 29. 実例 3  RunBehaviorTree() で実行される BehaviorTree と UseBlackboard() で既に生成された Blackboard とで Blackboard Asset の互換性がない場合  UBlackboardComponent が初期化され、データが消し飛ぶ 互換性なし
  • 30. 実例 4  RunBehaviorTree() で実行される BehaviorTree と UseBlackboard() で既に生成された Blackboard とで Blackboard Asset の互換性がある場合  UBlackboardComponent のデータは維持される  さらに、子の Blackboard にしか存在しない Key も使える 互換性あり (子 - 親 の関係)
  • 31. 実例 5  上と下の Node 群はほぼ同義
  • 32. まとめ  2 つの Node は具体的に何を行っているのか?  UseBlackboard() は UBlackboardComponent を、 RunBehaviorTree() は UBehaviorTreeComponent を生成する  RunBehaviorTree() は UBlackboardComponent が存在しない場合には UseBlackboard() を呼び出すので、 UseBlackboard() を明示的に呼び出す必要はない  ただし BehaviorTree に Blackboard Asset が設定されている必要がある  BehaviorTree に設定されている Blackboard Asset の子を使いたい場合や RunBehaviorTree() の呼び出し前に Key の Set を行いたい場合は、 UseBlackboard() を明示的に呼び出す必要がある  両方共、何回呼び出しても対応する Component は 1 つしか生成されない  複数回呼び出しても、無視されるか、既存のものが新しい設定で初期化される
  • 33. まとめ  2 つの Node の ReturnValue は何を返しているのか?  引数が None であるときに false を返している  IsValid() で事前に判定している場合、利用する必要はない
  • 34. Blackboard の Key の初期値
  • 35. Key の初期値に関する疑問  Blueprint の変数には、初期値を設定することができる  一方、Blackboard の Key には、初期値を設定するところがない  Blackboard の Key の初期値はどうなっている?
  • 36. Blackboard の Key の初期値 Blueprint で検証
  • 37. 初期値の検証 1  Blackboard で扱うことができるすべての Type の Key を作成し、 Editor での Play 中に値を確認してみる  Enum : UNKNOWN! とは?  String : n/a とは?  Rotator / Vector : (invalid) とは?
  • 38. 初期値の検証 2  Key を GetBlackboardValueAs○○() で取得して PrintString() してみる
  • 39. 初期値の検証 2  Key を GetBlackboardValueAs○○() で取得して PrintString() してみる  Enum : (Byte の) 0  String : 空の文字列  Rotator / Vector : !?!?!?
  • 40. 初期値の検証  ひとまず初期値はわかった  が、Rotator / Vector は 何故こんな値なのか?  意図的な設定なのか  たまたまメモリに 残っていた値を 解釈したらこうなったのか
  • 41. Blackboard の Key の初期値 エンジンソースを読む
  • 42. Key の Type 毎の処理定義  Blackboard の Key に対する処理は、 UBlackboardKeyType を継承した UBlackboardKeyType_○○ で規定されている  (/Runtime/AIModule/Classes/BehaviorTree/Blackboard/BlackboardKeyType.h)  GetValue() : 値の取得  SetValue() : 値の設定  InitializeMemory() : 値の初期化  例 : Bool Key の値を取得するときの処理内容は UBlackboardKeyType_Bool::GetValue()
  • 43. Vector Key の初期化処理  Vector Key の値を初期化するときの処理内容は UBlackboardKeyType_Vector::InitializeMemory()  FAISystem::InvalidLocation を Set している  (/Runtime/AIModule/Private/BehaviorTree/Blackboard/BlackboardKeyType_Vector.cpp line:56)
  • 44. Vector Key の初期化処理  FAISystem::InvalidLocation は {x, y, z} が float の最大値を取る値  (/Runtime/AIModule/Classes/AITypes.h line:24)  float の最大値は 2 の 128 乗 ≒ 3.402823466e+38 → 謎の巨大な値の正体  つまり、謎の巨大な値は意図的に設定されたものである
  • 45. Vector Key の初期値の取り扱い ということは…  初期値をそのまま計算に使わないように注意する必要がある  例えば、FVector::Normalize() を実行すると 途中計算で x * x した時点で不定値 (–nan(ind)) が生じる
  • 46. Vector Key の初期値の取り扱い  Vector Key が初期値かどうか判定する方法は? → IsVectorValueSet() を使う  (/Runtime/AIModule/Private/BehaviorTree/BlackboardComponent.cpp line:671)  名前が Key Name である Vector Key が 謎の巨大な値であれば false を返す実装になっている  初期値を計算に利用してしまうのを避けるために使える  IsRotatorValueSet() など、他の Type 版は存在しない
  • 47. Vector Key の初期値の取り扱い  Vector Key を初期値に戻す方法は? → ClearValue() を使う  (/Runtime/AIModule/Private/BehaviorTree/BlackboardComponent.cpp line:689)  Vector 以外の Type の Key に対しても使える  UBlackboardKeyType_○○::Clear() を間接的に呼び出して Key に特定の値を Set する実装になっている  Key にどのような値が Set されるかは実装次第だが、 標準で用意されている Type においては 初期値が Set される実装になっているため、 初期値に戻す関数として考えて差し支えない
  • 48. Vector Key の初期値の取り扱い  謎の巨大な値を『無効な値』として扱うと便利なことがある  例: 目標地点が設定されていればそこへ移動する Behavior Tree Task
  • 49. Vector Key の初期値の取り扱い  謎の巨大な値を『無効な値』として扱うと便利なことがある  例: 目標地点が設定されていればそこへ移動する Behavior Tree Task
  • 50. まとめ  Key の初期値は規定されている  不定値ではない  Blueprint の変数の初期値と一致するとは限らない  Rotator / Vector Key は初期値として『無効な値』扱いの 巨大な値が設定されるので、 そのまま計算に使わないように注意  IsVectorValueSet() で初期値でないことを確認してから使うなどする  Vector Key においては 初期値を『無効な値』として活用することもできる
  • 51. 余談 1 : Blackboard の Key の データはどこにある?  UBlackboardComponent のメンバである TArray<uint8> ValueMemory に格納される  色々な Type の変数が一緒くたに格納される  UBlackboardComponent::GetValue() の実装などを参照  (/Runtime/AIModule/Classes/BehaviorTree/BlackboardComponent.h line:355)  Blackboard Key を TArray 化できないのは このような実装になっているためだと思われる  BlackboardKeyType_○○ はあくまで Key の取り扱いを規定するもので、 この Class の Instance がデータを持つわけではない
  • 52. 余談 2 : 存在しない名前の Key を Get しようとすると何が返る?  UBlackboardKeyType_○○::InvalidValue として 定義されたものが返る  Key の初期値と InvalidValue は 同一のものが設定されている模様  よって、存在しない Key の名前を与えて GetValueAsVector() を呼び出すと謎の巨大な値が返る  ただし、初期値と InvalidValue を別の値にすることは 実装上可能なので、常に等しいとは限らない点に注意
  • 53. Behavior Tree Task の 通常版と AI 版の Event の違い
  • 54. 通常版と AI 版の Event?  Behavior Tree Task の Overridable な Event のこと  例 : Receive Tick 系の場合  ReceiveTick() : 通常版  ReceiveTickAI() : AI 版
  • 55. 通常版と AI 版の比較  どっちを使えばいいの?  AI を作るんだから AI 版?  でも Behavior Tree って AI を作るためのものなのでは?  通常版は何のために存在するのか?  両方実装したらどちらが呼び出される?両方?  通常版の Owner Actor は何を指すの?
  • 56. Behavior Tree Task の 通常版と AI 版の Event の違い Blueprint で検証
  • 57. 検証 1 : どちらが呼び出されるか  AIController で RunBehaviorTree() を行い 以下のような Behavior Tree Task を実行する  片方を実装しなかったり両方実装したりして、 どのように実行されるかを見る
  • 58. 検証 1 : どちらが呼び出されるか  AI 版だけ実装 → AI 版が実行される  通常版だけ実装 → 通常版が実行される
  • 59. 検証 1 : どちらが呼び出されるか  両方実装 → AI 版だけが実行される
  • 60. 検証 1 : どちらが呼び出されるか  AI 版は置くだけで何のノードも繋がない → ??? (虚無)
  • 61. 検証 1 : どちらが呼び出されるか  AI 版は置くだけで何の Node も繋がない → AI 版が呼び出される
  • 62. 検証 1 : どちらが呼び出されるか  わかったこと  通常版と AI 版はどちらか片方だけが実行される  両方実装されている場合、AI 版の実行が優先される  Event に Node を繋いでいなくても実装されている扱いになる
  • 63. 検証 2 : 通常版の Owner Actor は何を指す?  GetDisplayName() を使って名前を取得し PrintString() → AIController でした
  • 64. Behavior Tree Task の 通常版と AI 版の Event の違い エンジンソースを読む
  • 65. 通常版と AI 版の呼び分け部分を読む  例として ReceiveTick 系の呼び分けを見ていく  UBTTask_BlueprintBase::TickTask() で 通常版と AI 版の呼び分けを行っている  (/Runtime/AIModule/Private/BehaviorTree/Tasks/BTTask_BlueprintBase.cpp line:98)
  • 66. 通常版と AI 版の呼び分け部分を読む  以下の 2 つの式が両方 true だと AI 版が呼ばれる  AIOwner != nullptr  (ReceiveTickImplementations & FBTNodeBPImplementationHelper::AISpecific)
  • 67. 通常版と AI 版の呼び分け部分を読む  AIOwner != nullptr について  AIController* AIOwner は UBTTask_BlueprintBase::SetOwner() にて変更の機会がある  (/Runtime/AIModule/Private/BehaviorTree/Tasks/BTTask_BlueprintBase.cpp line:39)  AActor* ActorOwner には引数 InActorOwner がそのまま入る  AIOwner は InActorOwner を Cast() した結果が入るので、 InActorOwner が AIController* でない場合は nullptr となる
  • 68. 通常版と AI 版の呼び分け部分を読む  AIOwner != nullptr について  UBTTask_BlueprintBase::SetOwner() は UBTNode::InitializeInSubtree() にて呼び出しの機会がある  (/Runtime/AIModule/Private/BehaviorTree/BTNode.cpp line:68)  UBehaviorTreeComponent の Owner が引数として渡される  つまり、UBehaviorTreeComponent を持つ Actor が AAIController であるとき、式は true となる
  • 69. ReceiveTick 系の挙動を追う  (ReceiveTickImplementations & FBTNodeBPImplementationHelper::AISpecific) について  uint32 ReceiveTickImplementations には FBTNodeBPImplementationHelper::CheckEventImplementationVersion() の返却値が入る  (/Runtime/AIModule/Private/BehaviorTree/Tasks/BTTask_BlueprintBase.cpp line:12)  CheckEventImplementationVersion() は 2 つの指定した名前の Blueprint Function が実装されているかどうかを調べて bit フラグ形式で結果を返す
  • 70. ReceiveTick 系の挙動を追う  (ReceiveTickImplementations & FBTNodeBPImplementationHelper::AISpecific) について  uint32 ReceiveTickImplementations には FBTNodeBPImplementationHelper::CheckEventImplementationVersion() の返却値が入る  (/Runtime/AIModule/Private/BehaviorTree/Tasks/BTTask_BlueprintBase.cpp line:12)  今回の場合、具体的には以下の値が返る  0 (2 進数 00) : 通常版も AI 版も実装されていない  1 (2 進数 01) : 通常版 (ReceiveTick) だけ実装されている  2 (2 進数 10) : AI 版 (ReceiveTickAI) だけ実装されている  3 (2 進数 11) : 両方実装されている  つまり、(通常版の実装の有無に依らず) AI 版が実装されていれば、式は true となる
  • 71. ReceiveTick 系の挙動を追う  条件式まとめ  UBehaviorTreeComponent を持つ Actor が AAIController で、 かつ AI 版が実装されていれば、AI 版が呼び出される  ReceiveTick 系を例に見てきたが、ReceiveExecute 系など他も同じ
  • 72. 新たな疑問  UBehaviorTreeComponent を持つ Actor が AAIController ではない状況なんてあるの?  AAIController 以外の Actor に UBehaviorTreeComponent を付けても動作するの?
  • 73. AAIController 以外に UBehaviorTreeComponent を付ける  Blueprint では UBehaviorTreeComponent を生成・追加する Node が見当たらなかったので、C++ でやってみた
  • 74. AAIController 以外に UBehaviorTreeComponent を付ける  Pawn 継承 Class に UBehaviorTreeComponent を追加できた
  • 75. AAIController 以外に UBehaviorTreeComponent を付ける  UBehaviorTreeComponent の Owner の名前と、 通常版と AI 版のどちらを呼んでいるかを表示する Behavior Tree Task を作成
  • 76.
  • 77. AAIController 以外に UBehaviorTreeComponent を付ける  UBehaviorTreeComponent を 無理矢理 Pawn に付けたものと 普通に RunBehaviorTree() で付けたものの 出力を比較する  無理矢理 Pawn に付けた方は、 AI 版が実装されているにもかかわらず 通常版の方が呼び出されていることがわかる
  • 78. まとめ  通常版と AI 版、どっちを使えばいいの?  ほとんどの場合、AI 版オンリーでよい  AI 版の方が引数が具体的で便利  通常版だとわざわざ Cast() する手間がかかる場合が多い  一番避けるべきなのは、両方ごちゃ混ぜで使うこと
  • 79. まとめ  通常版の Owner Actor は何を指すの?  Behavior Tree Task を動作させている UBehaviorTreeComponent の Owner である Actor  UBehaviorTreeComponent を AAIController 以外の Actor に付けて動作させることは可能  中断処理を組みやすい BehaviorTree の構造を 上手く活用できるケースがあるかも  ただし AIMoveTo() のような経路探索系処理は Pawn を所有する AIController の存在を前提としているため 使用は難しい
  • 81. 検証をしよう!  各項目の流れは以下のような感じでした  疑問を持つ  Blueprint で簡単に検証してみる  新規プロジェクトを作成し、なるべく小さく作って検証する  エンジンソースを読んで動作を理解する  気合い
  • 83. 検証をしよう!  いつか困るかもしれません  『わからないことをわからないままにできない』ということが、 UE4 を趣味から仕事にしたときに一番大きく変化した部分でした  UE4 はわからなくても「なんとなく」でできてしまう面があります  検証して、理解を深めて、情報を共有していきましょう!
  • 84. 検証をしよう!  ※ 趣味で小規模にやる分にはそこまで考えなくていいです  参考 : 何もかもわかっていない状態で作った 3 年前のぷちコン作品