環境
UE 5.0.3
GameplayAbilityTargetingLocationInfo
FGameplayAbilityTargetingLocationInfo | Unreal Engine Documentation
以前書いた WaitTargetData と Radius の記事 で上記のように GASDocumentation の GA_Meteor_BP
から持ってきたノードをそのまま使っている部分がありました。
なんとなく分かった気になっていたのですが、いざ自分で組んでみると期待と違う動きをしたので理解できていなかったことがわかりました。
定義の確認
GameplayAbilityTargetType.h
に定義されています。
/** Structure that stores a location in one of several different formats */ USTRUCT(BlueprintType) struct GAMEPLAYABILITIES_API FGameplayAbilityTargetingLocationInfo { GENERATED_USTRUCT_BODY() FGameplayAbilityTargetingLocationInfo() : LocationType(EGameplayAbilityTargetingLocationType::LiteralTransform) , SourceActor(nullptr) , SourceComponent(nullptr) , SourceAbility(nullptr) { } virtual ~FGameplayAbilityTargetingLocationInfo() {} void operator=(const FGameplayAbilityTargetingLocationInfo& Other) { LocationType = Other.LocationType; LiteralTransform = Other.LiteralTransform; SourceActor = Other.SourceActor; SourceComponent = Other.SourceComponent; SourceAbility = Other.SourceAbility; SourceSocketName = Other.SourceSocketName; } /** Converts internal format into a literal world space transform */ FTransform GetTargetingTransform() const { //Return or calculate based on LocationType. switch (LocationType) { case EGameplayAbilityTargetingLocationType::ActorTransform: if (SourceActor) { return SourceActor->GetTransform(); } break; case EGameplayAbilityTargetingLocationType::SocketTransform: if (SourceComponent) { // Bad socket name will just return component transform anyway, so we're safe return SourceComponent->GetSocketTransform(SourceSocketName); } break; case EGameplayAbilityTargetingLocationType::LiteralTransform: return LiteralTransform; default: check(false); break; } // It cannot get here return FTransform::Identity; } /** Initializes new target data and fills in with hit results */ FGameplayAbilityTargetDataHandle MakeTargetDataHandleFromHitResult(TWeakObjectPtr<UGameplayAbility> Ability, const FHitResult& HitResult) const; FGameplayAbilityTargetDataHandle MakeTargetDataHandleFromHitResults(TWeakObjectPtr<UGameplayAbility> Ability, const TArray<FHitResult>& HitResults) const; /** Initializes new actor list target data, and sets this as the origin */ FGameplayAbilityTargetDataHandle MakeTargetDataHandleFromActors(const TArray<TWeakObjectPtr<AActor>>& TargetActors, bool OneActorPerHandle = false) const; /** Type of location used - will determine what data is transmitted over the network and what fields are used when calculating position. */ UPROPERTY(BlueprintReadWrite, meta = (ExposeOnSpawn = true), Category = Targeting) TEnumAsByte<EGameplayAbilityTargetingLocationType::Type> LocationType; /** A literal world transform can be used, if one has been calculated outside of the actor using the ability. */ UPROPERTY(BlueprintReadWrite, meta = (ExposeOnSpawn = true), Category = Targeting) FTransform LiteralTransform; /** A source actor is needed for Actor-based targeting, but not for Socket-based targeting. */ UPROPERTY(BlueprintReadWrite, meta = (ExposeOnSpawn = true), Category = Targeting) AActor* SourceActor; /** Socket-based targeting requires a skeletal mesh component to check for the named socket. */ UPROPERTY(BlueprintReadWrite, meta = (ExposeOnSpawn = true), Category = Targeting) UMeshComponent* SourceComponent; /** Ability that will be using the targeting data */ UPROPERTY(BlueprintReadWrite, meta = (ExposeOnSpawn = true), Category = Targeting) UGameplayAbility* SourceAbility; /** If SourceComponent is valid, this is the name of the socket transform that will be used. If no Socket is provided, SourceComponent's transform will be used. */ UPROPERTY(BlueprintReadWrite, meta = (ExposeOnSpawn = true), Category = Targeting) FName SourceSocketName; /** Optimized serialize function */ bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess); };
Blueprint 公開部分
上記のコードからです。
/** Type of location used - will determine what data is transmitted over the network and what fields are used when calculating position. */ UPROPERTY(BlueprintReadWrite, meta = (ExposeOnSpawn = true), Category = Targeting) TEnumAsByte<EGameplayAbilityTargetingLocationType::Type> LocationType; /** A literal world transform can be used, if one has been calculated outside of the actor using the ability. */ UPROPERTY(BlueprintReadWrite, meta = (ExposeOnSpawn = true), Category = Targeting) FTransform LiteralTransform; /** A source actor is needed for Actor-based targeting, but not for Socket-based targeting. */ UPROPERTY(BlueprintReadWrite, meta = (ExposeOnSpawn = true), Category = Targeting) AActor* SourceActor; /** Socket-based targeting requires a skeletal mesh component to check for the named socket. */ UPROPERTY(BlueprintReadWrite, meta = (ExposeOnSpawn = true), Category = Targeting) UMeshComponent* SourceComponent; /** Ability that will be using the targeting data */ UPROPERTY(BlueprintReadWrite, meta = (ExposeOnSpawn = true), Category = Targeting) UGameplayAbility* SourceAbility; /** If SourceComponent is valid, this is the name of the socket transform that will be used. If no Socket is provided, SourceComponent's transform will be used. */ UPROPERTY(BlueprintReadWrite, meta = (ExposeOnSpawn = true), Category = Targeting) FName SourceSocketName;
ノードの情報と一致しますね。SourceSocketName
は SourceComponent
と連動して利用されるようです。
続いて LocationType
の型である EGameplayAbilityTargetingLocationType
を確認します。
EGameplayAbilityTargetingLocationType
UENUM(BlueprintType) namespace EGameplayAbilityTargetingLocationType { /** What type of location calculation to use when an ability asks for our transform */ enum Type { /** We report an actual raw transform. This is also the final fallback if other methods fail */ LiteralTransform UMETA(DisplayName = "Literal Transform"), /** We pull the transform from an associated actor directly */ ActorTransform UMETA(DisplayName = "Actor Transform"), /** We aim from a named socket on the player's skeletal mesh component */ SocketTransform UMETA(DisplayName = "Socket Transform"), }; }
LiteralTransform
は 他を指定した時に、失敗しても Fallback として利用されるとあります。ということは、FGameplayAbilityTargetingLocationInfo
の LiteralTransform
プロパティにもなんらかは指定しておいた方が無難ということか FTransform
なのでまあ何も指定しなくてもデフォルトの値が使われるということでしょうか。
LiteralTransform
は actual raw transform とありますが、よくわかりません。ただ、 FTransform
を直接プロパティ値として受け取るので、そこで評価された値をそのまま利用するということなんでしょうか。(Literal って単語は解釈が毎度分からず苦手です 😅)
ちなみに、デフォルトコンストラクタでは LocationType
は LiteralTransform
が指定されています。
GetTargetingTransform
おそらくここが値を利用する箇所になるかと思います。
/** Converts internal format into a literal world space transform */ FTransform GetTargetingTransform() const { //Return or calculate based on LocationType. switch (LocationType) { case EGameplayAbilityTargetingLocationType::ActorTransform: if (SourceActor) { return SourceActor->GetTransform(); } break; case EGameplayAbilityTargetingLocationType::SocketTransform: if (SourceComponent) { // Bad socket name will just return component transform anyway, so we're safe return SourceComponent->GetSocketTransform(SourceSocketName); } break; case EGameplayAbilityTargetingLocationType::LiteralTransform: return LiteralTransform; default: check(false); break; } // It cannot get here return FTransform::Identity; }
この関数から以下のような関係性というのが確認できました。
LocationType |
利用するプロパティ |
---|---|
LiteralTransform |
LiteralTransform |
ActorTransform |
SourceActor (fallback で LiteralTransform ) |
SocketTransform |
SourceComponent , SourceSocketName (fallback で LiteralTransform )SourceSocketName 未指定の場合は SourceComponent の Transform を利用 |
ただ、ここだけ見ると fallback で LiteralTransform
ではなく、単に FTransform::Identity
が返されるだけですね。
ここまでで主要な部分は確認できました。(NetSerialize
はスキップします)
続いて、この構造体を作るために用意された関数群を見ていきます。
MakeTargetLocationInfoFrom~
UCLASS(Blueprintable) class GAMEPLAYABILITIES_API UGameplayAbility : public UObject, public IGameplayTaskOwnerInterface { ~略 // ---------------------------------------------------------------------------------------------------------------- // Target Data // ---------------------------------------------------------------------------------------------------------------- /** Creates a target location from where the owner avatar is */ UFUNCTION(BlueprintPure, Category = Ability) FGameplayAbilityTargetingLocationInfo MakeTargetLocationInfoFromOwnerActor(); /** Creates a target location from a socket on the owner avatar's skeletal mesh */ UFUNCTION(BlueprintPure, Category = Ability) FGameplayAbilityTargetingLocationInfo MakeTargetLocationInfoFromOwnerSkeletalMeshComponent(FName SocketName);
FGameplayAbilityTargetingLocationInfo UGameplayAbility::MakeTargetLocationInfoFromOwnerActor() { FGameplayAbilityTargetingLocationInfo ReturnLocation; ReturnLocation.LocationType = EGameplayAbilityTargetingLocationType::ActorTransform; ReturnLocation.SourceActor = GetActorInfo().AvatarActor.Get(); ReturnLocation.SourceAbility = this; return ReturnLocation; } FGameplayAbilityTargetingLocationInfo UGameplayAbility::MakeTargetLocationInfoFromOwnerSkeletalMeshComponent(FName SocketName) { FGameplayAbilityTargetingLocationInfo ReturnLocation; ReturnLocation.LocationType = EGameplayAbilityTargetingLocationType::SocketTransform; ReturnLocation.SourceComponent = GetActorInfo().SkeletalMeshComponent.Get(); ReturnLocation.SourceAbility = this; ReturnLocation.SourceSocketName = SocketName; return ReturnLocation; }
ヘッダとソースを貼っています。
見てわかる通りで、EGameplayAbilityTargetingLocationType
のうち、LiteralTransform
を除いた 2 つについて関数が用意されています。
GameplayAbility の関数であるため、関連する Actor にも簡単にアクセスできるようになっており、コードとしても特にややこしい部分はありません。
SourceAbility
でしっかり this
が指定されていることからも、SourceAbility
は EGameplayAbilityTargetingLocationType
に関係なく必要なことが分かります。
あれ、GA_Meteor_BP のノードって?
さて、ここで改めて GA_Meteor_BP
からコピーしてきたノードを見てみます。
まず、MakeTargetLocationInfoFromOwnerActor
で作成されていることが分かります。つまり、作成時点では EGameplayAbilityTargetingLocationType
は ActorTransform
になっているということです。
にもかかわらず、LiteralTransform
の Location
が変更されています。ここまで読んできた内容どおりで、最終的に GetTargetingTransform
を経由して取得するならば、ここで LiteralTransform
を変更したところでその値が使われることはありません。
GA_Meteor_BP
の奥の cpp のコードまで読んでいないので、実際この LiteralTransform の変更が全くの無駄なのか、または GetTargetingTransform
は使わずに直接 LiteralTransform
から値を読んで使っているのかは分かりません。
ただ、何も考えずにコピーしてきた私が良くなかったのは確かです 😥 反省します。。