🏛️

【UneralEngine AI編】 サービス(Service)について

に公開

Behavior Treeにおける「サービス(Service)」とは?

  • サービスは、特定のノード(主にSelectorSequence)にアタッチされて使われます。
  • アタッチされたノードが「アクティブ」である間、定期的に実行され続けます。
  • 実行間隔(更新頻度)は設定可能で、例えば毎秒チェックする、毎フレーム確認する、などのカスタマイズができます。

サービスの主な用途

用途 説明
敵の位置の定期確認 視界内に敵がいるかをチェックして、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