Lyra インタラクション システム

Lyra Game サンプルの Lyra インタラクション システムの概要について説明します。

Lyra Interaction System

Lyra は、独自の ゲームプレイ アビリティ/UGameplayAbility (ULyraGameplayAbility_Interact) を介して、インタラクション

[インターフェース](programming-and-scripting/programming-language-implementation/cpp-in-unreal-engine/Interfaces)
/Iインターフェース を使用使用して、プレイヤーが Lyra 内のオブジェクトと対話する方法と、それらのオブジェクトがプレイヤーと対話する方法との間に因果関係を確立します。

LyraGameplayAbility_Interact クラスによって、インタラクションの呼び出し方法のロジックを管理できます。

ULyraGameplayAbility_Interact.h

    #pragma once
    #include "CoreMinimal.h"
    #include "AbilitySystem/Abilities/LyraGameplayAbility.h"
    #include "Interaction/InteractionQuery.h"
    #include "Interaction/IInteractableTarget.h"
    #include "LyraGameplayAbility_Interact.generated.h"

    class FIndicatorDescriptor;
    /**

     * ULyraGameplayAbility_Interact
     *
     * Gameplay ability used for character interacting
     */
    UCLASS(Abstract)
    class ULyraGameplayAbility_Interact : public ULyraGameplayAbility
    {
        GENERATED_BODY()
    public:

        ULyraGameplayAbility_Interact(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
        virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override;

        UFUNCTION(BlueprintCallable)
        void UpdateInteractions(const TArray<FInteractionOption>& InteractiveOptions);

        UFUNCTION(BlueprintCallable)
        void TriggerInteraction();

    protected:

        UPROPERTY(BlueprintReadWrite)
        TArray<FInteractionOption> CurrentOptions;

        TArray<TSharedRef<FIndicatorDescriptor>> Indicators;

    protected:

        UPROPERTY(EditDefaultsOnly)
        float InteractionScanRate = 0.1f;

        UPROPERTY(EditDefaultsOnly)
        float InteractionScanRange = 500;

        UPROPERTY(EditDefaultsOnly)
        TSoftClassPtr<UUserWidget> DefaultInteractionWidgetClass;

    };

AbilityTask_WaitForInteractableTargets_SingleLineTrace はゲームプレイ アビリティ タスク であり、ライン トレースを実行し、インターフェースを実装するアクタに遭遇するまでループ タイマーで待機します。

次に例を示します。

LyraPawnActor を制御しているプレイヤーのヘルスが低下しているので、プレイヤーはポーンに収集可能なヘルス アイテムをピックアップするように指示します。プレイヤーの照準線を収集品に合わせて、「Use/Interact」キーを押すと、ポーンからライン トレースが発射されます。トレースが収集品に命中すると、収集品に実装されているインタラクション インターフェースがプレイヤーのヘルスをフルに回復させるロジックを処理します。

インタラクション アビリティ タスク

UAbilityTask_WaitForInteractableTargets は、インタラクション可能なターゲットを追跡する新しいトレース方法を作るために使用されます。

次に例を示します。

LyraPawnActor を制御するプレイヤーが、開きたいドアに近づいているとします。プレイヤーの照準線をドアに合わせて、「使用」キーを押すと、ドアの「ロック解除/ロック」、またはドアを開けることを試みるオプションを含む、放射状メニューが表示されます。

Unreal のライン トレースの詳細については、「トレース」を参照してください。

UAbilityTask_WaitForInteractableTargets.h

    #pragma once
    #include "CoreMinimal.h"
    #include "UObject/ObjectMacros.h"
    #include "Abilities/Tasks/AbilityTask.h"
    #include "Engine/EngineTypes.h"
    #include "CollisionQueryParams.h"
    #include "WorldCollision.h"
    #include "Engine/CollisionProfile.h"
    #include "Abilities/GameplayAbilityTargetDataFilter.h"
    #include "Interaction/InteractionOption.h"
    #include "Interaction/InteractionQuery.h"
    #include "Interaction/IInteractableTarget.h"
    #include "AbilityTask_WaitForInteractableTargets.generated.h"

    class AActor;
    class UPrimitiveComponent;
    class UGameplayAbility;

    DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FInteractableObjectsChangedEvent, const TArray<FInteractionOption>&, InteractableOptions);

    UCLASS(Abstract)
    class UAbilityTask_WaitForInteractableTargets : public UAbilityTask
    {
        GENERATED_UCLASS_BODY()

    public:

        UPROPERTY(BlueprintAssignable)
        FInteractableObjectsChangedEvent InteractableObjectsChanged;

    protected:

        static void LineTrace(FHitResult& OutHitResult, const UWorld* World, const FVector& Start, const FVector& End, FName ProfileName, const FCollisionQueryParams Params);
        void AimWithPlayerController(const AActor* InSourceActor, FCollisionQueryParams Params, const FVector& TraceStart, float MaxRange, FVector& OutTraceEnd, bool bIgnorePitch = false) const;
        static bool ClipCameraRayToAbilityRange(FVector CameraLocation, FVector CameraDirection, FVector AbilityCenter, float AbilityRange, FVector& ClippedPosition);
        void UpdateInteractableOptions(const FInteractionQuery& InteractQuery, const TArray<TScriptInterface<IInteractableTarget>>& InteractableTargets);
        FCollisionProfileName TraceProfile;

        // Does the trace affect the aiming pitch
        bool bTraceAffectsAimPitch = true;

        TArray<FInteractionOption> CurrentOptions;

    };

トレース用に選択した AbilityTask は、FInteractionQuery 構造体から一連のインタラクション可能なターゲットを提供します。

struct FInteractionQuery

    #pragma once
    #include "CoreMinimal.h"
    #include "Abilities/GameplayAbility.h"
    #include "InteractionQuery.generated.h"

    /**  */
    USTRUCT(BlueprintType)
    struct FInteractionQuery
    {

        GENERATED_BODY()

    public:
        /** The requesting pawn. */
        UPROPERTY(BlueprintReadWrite)
        TWeakObjectPtr<AActor> RequestingAvatar;

        /** Provides us the capability to specify a controller - this does not need to match the owner of the requesting avatar. */
        UPROPERTY(BlueprintReadWrite)
        TWeakObjectPtr<AController> RequestingController;

        /** A generic UObject to provide extra data required for the interaction */
        UPROPERTY(BlueprintReadWrite)
        TWeakObjectPtr<UObject> OptionalObjectData;
    };

to the method UAbilityTask_WaitForInteractableTargets::UpdateInteractableOptions:

    void UAbilityTask_WaitForInteractableTargets::UpdateInteractableOptions(const FInteractionQuery& InteractQuery, const TArray<TScriptInterface<IInteractableTarget>>& InteractableTargets)
    {

        TArray<FInteractionOption> NewOptions;

        for (const TScriptInterface<IInteractableTarget>& InteractiveTarget :InteractableTargets)

        {

            TArray<FInteractionOption> TempOptions;

            FInteractionOptionBuilder InteractionBuilder(InteractiveTarget, TempOptions);

            InteractiveTarget->GatherInteractionOptions(InteractQuery, InteractionBuilder);

            for (FInteractionOption& Option :TempOptions)

            {

                FGameplayAbilitySpec* InteractionAbilitySpec = nullptr;

                // if there is a handle and a target ability system, we're triggering the ability on the target.

                if (Option.TargetAbilitySystem && Option.TargetInteractionAbilityHandle.IsValid())

                {

                    // Find the spec

                    InteractionAbilitySpec = Option.TargetAbilitySystem->FindAbilitySpecFromHandle(Option.TargetInteractionAbilityHandle);

                }

                // If there's an interaction ability then we're activating it on ourselves.

                else if (Option.InteractionAbilityToGrant)

                {

                    // Find the spec

                    InteractionAbilitySpec = AbilitySystemComponent->FindAbilitySpecFromClass(Option.InteractionAbilityToGrant);

                    if (InteractionAbilitySpec)

                    {

                        // update the option

                        Option.TargetAbilitySystem = AbilitySystemComponent;

                        Option.TargetInteractionAbilityHandle = InteractionAbilitySpec->Handle;

                    }

                }

                if (InteractionAbilitySpec)

                {

                    // Filter any options that we can't activate right now for whatever reason.

                    if (InteractionAbilitySpec->Ability->CanActivateAbility(InteractionAbilitySpec->Handle, AbilitySystemComponent->AbilityActorInfo.Get()))

                    {

                        NewOptions.Add(Option);

                    }

                }

            }

        }

        bool bOptionsChanged = false;

        if (NewOptions.Num() == CurrentOptions.Num())

        {

            NewOptions.Sort();

            for (int OptionIndex = 0; OptionIndex < NewOptions.Num(); OptionIndex++)

            {

                const FInteractionOption& NewOption = NewOptions[OptionIndex];

                const FInteractionOption& CurrentOption = CurrentOptions[OptionIndex];

                if (NewOption != CurrentOption)

                {

                    bOptionsChanged = true;

                    break;

                }

            }

        }

        else

        {

            bOptionsChanged = true;

        }

        if (bOptionsChanged)

        {

            CurrentOptions = NewOptions;

            InteractableObjectsChanged.Broadcast(CurrentOptions);

        }

    }

これは、インタラクション可能な各ターゲットで IInteractableTarget::GatherInteractionOptions を呼び出します。

    virtual void GatherInteractionOptions(const FInteractionQuery& InteractQuery, FInteractionOptionBuilder& OptionBuilder) = 0;

一連のインタラクション可能なオブジェクトを更新すると、インタラクション アビリティ (GA_Interact) は、プレイヤーがインタラクション対象オブジェクトにフォーカスして、その特定のオブジェクトとインタラクションしたいプレイヤーからの入力を受ける取ると、この TriggerInteraction 関数を呼び出します。

現在のオプションを呼び出したら、インタラクションを実行させる方法は 2 つあります。1 つ目の方法は、FInteractionOption::InteractionAbilityToGrant 関数を使用して、プレイヤーの Ability System コンポーネントにアビリティを付与する方法です。Weapon Pickup アクタなどのシンプルなロジックでは、この関数の使用が推奨されます。

また、複雑なロジックを処理するために、ユーザーが独自の Ability System コンポーネントを含むオブジェクトをインタラクションする場合は、FInteractionOption::TargetAbilitySystem 関数と`FInteractionOption::TargetInteractionHandle` 関数を呼び出すことができます。これにより、Lyra キャラクター (アバター) のアビリティ システム コンポーネントでアビリティを呼び出すのではなく、インタラクション可能なオブジェクトでアビリティを呼び出します。

インタラクション関数 FInteractionOption::InteractionAbilityToGrant は、ULyraGameplayAbility_Interact インタラクション アビリティの基本クラスから継承されます。これは、範囲指定されたループとタイマーとしてタスク関数 AbilityTask_GrantNearbyInteraction を実行し、近くのアビリティを収集し、ユーザーがそれらのアビリティのインタラクションを試行する前にキャラクターにそれらのアビリティを付与します。InteractionScanRate float を大きくすることで、InteractionRange より半径を広げることができます。これを行わないと、レプリケーションでアビリティが十分迅速にクライアントに提供されません。

このアビリティはイベント

[ゲームプレイ タグ](programming-and-scripting/interactive-framework/Tags)
FInteractionOption::InteractionEventTag を使用して呼び出されます。このタグは、アビリティのトリガーと一致している必要があります。たとえば、GA_Collect_InteractionAbility.Type.Interact.Collect イベントが送信されたときにトリガーされます。これはインタラクション オプションで設定されます。

InteractInterface.png

GA_Collect_Interaction は、1 種類のみのインタラクションを表します。すなわち、地面にあるオブジェクトをピックアップして、インベントリに追加する能力を提供するアビリティです。地面にあるリンゴを食べてプレイヤーのヘルスを回復させたり、ドアを開けたり、車両に乗ったり、チェストを開けたりするアビリティを作成するなど、イマジネーションは無限に広がります。

この分離動作により、中央のパッシブ インタラクション スキャナーから、あらゆる種類のインタラクションが提供されます。

Lyra インタラクションの重要な用語

InteractableTarget - IInteractableTarget インターフェースを実装しているアクタまたはコンポーネント。ワールドのどのオブジェクトとインタラクションできるかを決定します。

InteractionOption - 「Affordance」または「Option」。たとえば、リンゴは「Collect」および「Consume」の両方を指定できます。

InteractionInstigator - インタラクションを開始するポーン (LyraPawnActor)。これは IInteractionInstigator インターフェースを実装する場合も実装しない場合もあります。これにより、オプションとその表示方法をさらにカスタマイズすることができます。

Unreal Engine のドキュメントを改善するために協力をお願いします!どのような改善を望んでいるかご意見をお聞かせください。
調査に参加する
キャンセル