🏛️
【UneralEngine AI編】 サービス(Service)について
Behavior Treeにおける「サービス(Service)」とは?
- サービスは、特定のノード(主に
Selector
やSequence
)にアタッチされて使われます。 - アタッチされたノードが「アクティブ」である間、定期的に実行され続けます。
- 実行間隔(更新頻度)は設定可能で、例えば毎秒チェックする、毎フレーム確認する、などのカスタマイズができます。
サービスの主な用途
用途 | 説明 |
---|---|
敵の位置の定期確認 | 視界内に敵がいるかをチェックして、Blackboardに保存 |
プレイヤーとの距離チェック | 距離に応じて戦闘・回避・探索などのステートを切り替える |
ナビゲーション更新 | 定期的に目的地を更新したり、目的地が到達済みか確認 |
状態変化のトリガー | 体力が低下したら「逃走」モードに切り替えるなど |
実際に作ってみる
今回は例として、敵キャラ(相手)がプレイヤー(自分)の方向をずっと見る処理を作成します。
これに、サービスを使う理由は、敵キャラはずっと(定期的に)プレイヤーの方向を見てほしいため、
サービスが適しています。
クラスは以下のBTService
を使います。
UBTService_OrientToTargetActor.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "BehaviorTree/BTService.h"
#include "BTService_OrientToTargetActor.generated.h"
/**
*
*/
UCLASS()
class MYACTION_API UBTService_OrientToTargetActor : public UBTService
{
GENERATED_BODY()
//コンストラクタ
UBTService_OrientToTargetActor();
//~ Begin UBTNode Interface.
virtual void InitializeFromAsset(UBehaviorTree& Asset) override;
virtual FString GetStaticDescription() const override;
//~ End UBTNode Interface
//ここでメインの処理を書いていく
virtual void TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds) override;
//ターゲット(プレイヤー)のキー
UPROPERTY(EditAnywhere, Category = "Target")
FBlackboardKeySelector InTargetActorKey;
//方向を向くスピード
UPROPERTY(EditAnywhere, Category = "Target")
float RotationInterpSpeed;
};
UBTService_OrientToTargetActor.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "AI/BTService_OrientToTargetActor.h"
#include "BehaviorTree/BlackboardComponent.h" //追加したヘッダー
#include "AIController.h" //追加したヘッダー
#include "Kismet/KismetMathLibrary.h" //追加したヘッダー
UBTService_OrientToTargetActor::UBTService_OrientToTargetActor()
{
//サービスをアタッチしたときに表示されるタイトル
NodeName = TEXT("Native Orient Rotation To Target Actor");
//サービスという機能を使えるようにするもの
INIT_SERVICE_NODE_NOTIFY_FLAGS();
//回転速度の初期化
RotationInterpSpeed = 5.f;
//間隔の初期化
Interval = 0.f;
RandomDeviation = 0.f;
//ターゲットキーにアクタのみ設定できるようにする
InTargetActorKey.AddObjectFilter(this, GET_MEMBER_NAME_CHECKED(ThisClass, InTargetActorKey), AActor::StaticClass());
}
//** 初期化処理 **//
void UBTService_OrientToTargetActor::InitializeFromAsset(UBehaviorTree& Asset)
{
Super::InitializeFromAsset(Asset);
//ブラックボードを取得
if (UBlackboardData* BBAsset = GetBlackboardAsset())
{
//キー情報をブラックボードに保存(これがないとキー情報を取得できない)
InTargetActorKey.ResolveSelectedKey(*BBAsset);
}
}
//** サービスの説明 **//
FString UBTService_OrientToTargetActor::GetStaticDescription() const
{
//** 以下にサービスの説明を記入 **//
const FString KeyDescription = InTargetActorKey.SelectedKeyName.ToString();
return FString::Printf(TEXT("Orient rotation to %s Key %s"), *KeyDescription, *GetStaticServiceDescription());
}
//** メイン処理 **//
void UBTService_OrientToTargetActor::TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds)
{
Super::TickNode(OwnerComp, NodeMemory, DeltaSeconds);
//設定したキー情報をオブジェクト型で取得
UObject* ActorObject = OwnerComp.GetBlackboardComponent()->GetValueAsObject(InTargetActorKey.SelectedKeyName);
//取得したオブジェクトをアクタに変換
AActor* TargetActor = Cast<AActor>(ActorObject);
//オーナーポーン(敵)の取得
APawn* OwningPawn = OwnerComp.GetAIOwner()->GetPawn();
if (OwningPawn && TargetActor)
{
//敵とプレイヤーの位置から方向角度を取得
const FRotator LookAtRot = UKismetMathLibrary::FindLookAtRotation(OwningPawn->GetActorLocation(), TargetActor->GetActorLocation());
//方向角度を徐々に向く
const FRotator TargetRot = FMath::RInterpTo(OwningPawn->GetActorRotation(), LookAtRot, DeltaSeconds, RotationInterpSpeed);
//オーナー(敵)に角度をセット
OwningPawn->SetActorRotation(TargetRot);
}
}
最後に
このように、状態を毎フレームや定期的に監視したりするときにサービスは適していることがわかります。
Discussion