{"id":427981,"date":"2024-07-31T21:02:11","date_gmt":"2024-07-31T21:02:11","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=427981"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=427981","title":{"rendered":"<span>Combat Abilities System \u2014 \u0420\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 Gameplay Ability System \u0432 Unreal Engine, \u0427\u0430\u0441\u0442\u044c 1<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/c3d\/6cf\/a6a\/c3d6cfa6a99551e0250eb489b2146d93.png\" width=\"700\" height=\"350\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/c3d\/6cf\/a6a\/c3d6cfa6a99551e0250eb489b2146d93.png\"\/><\/figure>\n<p>\u0414\u043b\u044f \u0441\u0432\u043e\u0435\u0439 \u043d\u043e\u0432\u043e\u0439 \u0438\u0433\u0440\u044b Code Of Person (\u041a\u0430\u043a-\u043d\u0438\u0431\u0443\u0434\u044c \u0432 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0445 \u0441\u0442\u0430\u0442\u044c\u044f\u0445 \u043f\u0440\u043e \u0435\u0451 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443) \u044f \u0440\u0435\u0448\u0438\u043b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0438 \u0440\u0430\u0441\u0448\u0438\u0440\u0438\u0442\u044c \u043f\u043b\u0430\u0433\u0438\u043d Gameplay Ability System.<\/p>\n<p>\u0412 \u0441\u0435\u0440\u0438\u0438 \u0441\u0442\u0430\u0442\u044c\u0435\u0439 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u043e \u043c\u043e\u0451\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u043b\u0430\u0433\u0438\u043d\u0430 Combat Abilities System, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0435\u0442 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0433\u043e \u043f\u043b\u0430\u0433\u0438\u043d\u0430 Gameplay Ability System \u0432 Unreal Engine. \u041c\u044b \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0448\u0430\u0433\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438, \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u043d\u044b\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u0438 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0434\u0435\u043b\u0430\u044e\u0442 \u043d\u0430\u0448 \u043f\u043b\u0430\u0433\u0438\u043d \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u043c \u0438 \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u043c \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0431\u043e\u0435\u0432\u044b\u0445 \u043c\u0435\u0445\u0430\u043d\u0438\u043a \u0432 \u0438\u0433\u0440\u0430\u0445.<\/p>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u043f\u043e\u043a\u0430\u0436\u0443 \u0441\u0432\u043e\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 Enhanced Input \u0438 Game Features.<\/p>\n<h2>\u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 Gameplay Ability System \u0438 \u0434\u043b\u044f \u0447\u0435\u0433\u043e \u043e\u043d \u043d\u0443\u0436\u0435\u043d?<\/h2>\n<p>Gameplay Ability System \u2014 \u044d\u0442\u043e \u043a\u043e\u043c\u043f\u043b\u0435\u043a\u0441\u043d\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430, \u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u043d\u0430\u044f \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u044f\u043c\u0438. \u041e\u043d\u0430 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0440\u0430\u0437\u043d\u043e\u043e\u0431\u0440\u0430\u0437\u043d\u044b\u0435 \u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u0438, \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0438\u0445 \u0430\u043a\u0442\u0438\u0432\u0430\u0446\u0438\u0435\u0439, \u044d\u0444\u0444\u0435\u043a\u0442\u0430\u043c\u0438 \u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435\u043c \u0441 \u0434\u0440\u0443\u0433\u0438\u043c\u0438 \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u043c\u0438 \u0438\u0433\u0440\u044b. \u041e\u0434\u043d\u0430\u043a\u043e, \u0431\u0430\u0437\u043e\u0432\u043e\u0433\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u0430 \u043c\u043d\u0435 \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e, \u0438 \u044f \u0445\u043e\u0442\u0435\u043b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0442\u0430\u043a \u0447\u0442\u043e\u0431\u044b \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0432\u043e\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043c\u0435\u0436\u0434\u0443 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u043c\u0438 + \u0437\u0430\u044f\u0432\u0438\u0442\u044c \u0441\u0435\u0431\u044f <\/p>\n<p>\u0414\u043b\u044f \u0441\u0435\u0431\u044f \u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043b \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0436\u0435\u043b\u0430\u043d\u0438\u044f \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b:<\/p>\n<ul>\n<li>\n<p>\u0431\u043e\u0435\u0432\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430<\/p>\n<\/li>\n<li>\n<p>\u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0432\u0432\u043e\u0434\u0430 \u0438\u0433\u0440\u043e\u043a\u0430<\/p>\n<\/li>\n<li>\n<p>\u043c\u043e\u0434\u0443\u043b\u044c\u043d\u043e\u0441\u0442\u044c <\/p>\n<\/li>\n<\/ul>\n<h2>\u041f\u0440\u0438\u0432\u044f\u0437\u043a\u0430 \u043a Enhanced Input<\/h2>\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u044f \u0441\u043e\u0437\u0434\u0430\u043b \u043a\u043b\u0430\u0441\u0441 <strong>UPlayerControlsComponent<\/strong>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u0432\u0435\u0447\u0430\u0442\u044c \u0437\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u0432\u0432\u043e\u0434\u0430 \u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0447\u0435\u0440\u0435\u0437 Enhanced Input <\/p>\n<pre><code class=\"cpp\">PlayerControlsComponent.h  #pragma once  #include \"EnhancedInputComponent.h\" #include \"Components\/PawnComponent.h\" #include \"PlayerControlsComponent.generated.h\"  class UEnhancedInputLocalPlayerSubsystem; class UInputAction; class UInputMappingContext; \/**  *   *\/ UCLASS(BlueprintType, Blueprintable, meta=(BlueprintSpawnableComponent)) class COMBATABILITIESSYSTEMRUNTIME_API UPlayerControlsComponent : public UPawnComponent { GENERATED_BODY()  public: virtual void OnRegister() override; virtual void OnUnregister() override;  protected: UFUNCTION(BlueprintNativeEvent, Category=\"Player Controls\") void SetupPlayerControls(UEnhancedInputComponent* InPlayerInputComponent);  UFUNCTION(BlueprintNativeEvent, Category=\"Player Controls\") void TeardownPlayerControls(UEnhancedInputComponent* InPlayerInputComponent);  template&lt;class UserClass, typename FuncType&gt; bool BindInputAction(const UInputAction* InAction, const ETriggerEvent InEventType, UserClass* InObject, FuncType InFunction) { if(ensure(InputComponent != nullptr) &amp;&amp; ensure(InAction != nullptr)) { InputComponent-&gt;BindAction(InAction, InEventType, InObject, InFunction); return true; } return false; }  UFUNCTION() virtual void OnPawnRestarted(APawn* InPawn); UFUNCTION() virtual void OnControllerChanged(APawn* InPawn, AController* InOldController, AController* NewController);  virtual void SetupInputComponent(APawn* InPawn); virtual void ReleaseInputComponent(AController* OldController = nullptr); UEnhancedInputLocalPlayerSubsystem* GetEnhancedInputSubsystem(AController* OldController = nullptr) const;  UEnhancedInputComponent* GetInputComponent() const { return InputComponent;}  private: UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=\"Player Controls\", meta=(AllowPrivateAccess=\"true\")) TObjectPtr&lt;UInputMappingContext&gt; InputMappingContext{nullptr}; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=\"Player Controls\", meta=(AllowPrivateAccess=\"true\")) int32 InputPriority{0};  UPROPERTY(Transient) UEnhancedInputComponent* InputComponent;  };<\/code><\/pre>\n<pre><code class=\"cpp\">UPlayerControlsComponent.cpp   #include \"Components\/PlayerControlsComponent.h\"  #include \"EnhancedInputSubsystems.h\"  #include UE_INLINE_GENERATED_CPP_BY_NAME(PlayerControlsComponent)  void UPlayerControlsComponent::SetupPlayerControls_Implementation(UEnhancedInputComponent* InPlayerInputComponent) { }  void UPlayerControlsComponent::TeardownPlayerControls_Implementation(UEnhancedInputComponent* InPlayerInputComponent) { }  void UPlayerControlsComponent::OnRegister() { Super::OnRegister();  const UWorld* World {GetWorld()}; APawn* PawnOwner{GetPawn&lt;APawn&gt;()}; if(!ensure(PawnOwner) &amp;&amp; !World-&gt;IsGameWorld()) return;  PawnOwner-&gt;ReceiveRestartedDelegate.AddDynamic(this, &amp;UPlayerControlsComponent::OnPawnRestarted); PawnOwner-&gt;ReceiveControllerChangedDelegate.AddDynamic(this, &amp;UPlayerControlsComponent::OnControllerChanged);  if(PawnOwner-&gt;InputComponent) { OnPawnRestarted(PawnOwner); }  }  void UPlayerControlsComponent::OnUnregister() { if(const UWorld* World {GetWorld()}; World &amp;&amp; World-&gt;IsGameWorld()) { ReleaseInputComponent(); if(auto* PawnOwner{GetPawn&lt;APawn&gt;()}; PawnOwner) { PawnOwner-&gt;ReceiveRestartedDelegate.RemoveAll(this); PawnOwner-&gt;ReceiveControllerChangedDelegate.RemoveAll(this); } }  Super::OnUnregister(); }  void UPlayerControlsComponent::OnPawnRestarted(APawn* InPawn) { if(ensure(InPawn &amp;&amp; InPawn == GetOwner()) &amp;&amp; InPawn-&gt;InputComponent) { ReleaseInputComponent(); if(InPawn-&gt;InputComponent) { SetupInputComponent(InPawn); } } }  void UPlayerControlsComponent::OnControllerChanged(APawn* InPawn, AController* InOldController, AController* NewController) { if(ensure(InPawn &amp;&amp; InPawn == GetOwner()) &amp;&amp; InOldController) { ReleaseInputComponent(InOldController); } }  void UPlayerControlsComponent::SetupInputComponent(APawn* InPawn) { InputComponent = CastChecked&lt;UEnhancedInputComponent&gt;(InPawn-&gt;InputComponent);  UEnhancedInputLocalPlayerSubsystem* Subsystem = {GetEnhancedInputSubsystem()}; check(Subsystem); if(InputMappingContext) { Subsystem-&gt;AddMappingContext(InputMappingContext, InputPriority); } SetupPlayerControls(InputComponent); }  void UPlayerControlsComponent::ReleaseInputComponent(AController* OldController) { if(UEnhancedInputLocalPlayerSubsystem* Subsystem {GetEnhancedInputSubsystem(OldController)}; Subsystem &amp;&amp; InputComponent) { TeardownPlayerControls(InputComponent); if(InputMappingContext) { Subsystem-&gt;RemoveMappingContext(InputMappingContext); } } InputComponent = nullptr; }  UEnhancedInputLocalPlayerSubsystem* UPlayerControlsComponent::GetEnhancedInputSubsystem( AController* OldController) const { const APlayerController* PlayerController {GetController&lt;APlayerController&gt;()}; if(!PlayerController) { PlayerController = Cast&lt;APlayerController&gt;(OldController); if(!PlayerController) { return nullptr; } }  const ULocalPlayer* LocalPlayer {PlayerController-&gt;GetLocalPlayer()}; if(!LocalPlayer) return nullptr;  return LocalPlayer-&gt;GetSubsystem&lt;UEnhancedInputLocalPlayerSubsystem&gt;(); } <\/code><\/pre>\n<p>\u0414\u0430\u043b\u0435\u0435 \u043d\u0443\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043e\u0442 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430, \u043e\u043f\u0438\u0441\u0430\u043d\u043d\u043e\u0433\u043e \u0432\u044b\u0448\u0435, \u0434\u043e\u0447\u0435\u0440\u043d\u0438\u0439 \u043a\u043b\u0430\u0441\u0441  <strong>UAbilityInputBindingComponent<\/strong>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u0432\u0435\u0447\u0430\u0442\u044c \u0437\u0430 \u043f\u0440\u0438\u0432\u044f\u0437\u043a\u0443 \u0432\u0432\u043e\u0434\u0430 \u0438 \u0438\u0433\u0440\u043e\u0432\u044b\u043c\u0438 \u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u044f\u043c\u0438:<\/p>\n<pre><code class=\"cpp\">AbilityInputBindingComponent.h  #pragma once  #include \"GameplayAbilitySpec.h\" #include \"GameplayAbilitySpecHandle.h\" #include \"Components\/PlayerControlsComponent.h\" #include \"AbilityInputBindingComponent.generated.h\"  class UAbilitySystemComponent;  USTRUCT() struct FAbilityInputBinding { GENERATED_BODY()  int32 InputID{0}; uint32 OnPressedHandle{0}; uint32 OnReleasedHandle{0}; TArray&lt;FGameplayAbilitySpecHandle&gt; BoundAbilitiesStack;  };  UCLASS(meta=(BlueprintSpawnableComponent)) class COMBATABILITIESSYSTEMRUNTIME_API UAbilityInputBindingComponent : public UPlayerControlsComponent { GENERATED_BODY()  public: UFUNCTION(BlueprintCallable, Category=\"Abilities\") void SetInputBinding(UInputAction* InInputAction, FGameplayAbilitySpecHandle AbilitySpecHandle);  UFUNCTION(BlueprintCallable, Category=\"Abilities\") void ClearInputBinding(FGameplayAbilitySpecHandle InAbilityHandle);  UFUNCTION(BlueprintCallable, Category=\"Abilities\") void ClearAbilityBindings(UInputAction* InInputAction);  virtual void SetupPlayerControls_Implementation(UEnhancedInputComponent* InPlayerInputComponent) override; virtual void ReleaseInputComponent(AController* OldController) override;  private: void ResetBindings(); void RunAbilitySystemSetup(); void OnAbilityInputPressed(UInputAction* InInputAction); void OnAbilityInputReleased(UInputAction* InInputAction);  void RemoveEntry(UInputAction* InInputAction);  FGameplayAbilitySpec* FindAbilitySpec(FGameplayAbilitySpecHandle InHandle); void TryBindAbilityInput(UInputAction* InInputAction, FAbilityInputBinding&amp; InAbilityInputBinding);  private: UPROPERTY(Transient) UAbilitySystemComponent* AbilityComponent;  UPROPERTY(Transient) TMap&lt;UInputAction*, FAbilityInputBinding&gt; MappedAbilities;  };<\/code><\/pre>\n<pre><code class=\"cpp\">AbilityInputBindingComponent.cpp  #include \"Input\/AbilityInputBindingComponent.h\"  #include \"AbilitySystemComponent.h\" #include \"AbilitySystemGlobals.h\"  #include UE_INLINE_GENERATED_CPP_BY_NAME(AbilityInputBindingComponent)  namespace AbilityInputBindingComponent_Impl { constexpr int32 InvalidInputID{0}; int32 IncrementingInputID{InvalidInputID}; static int32 GetNextInputID() { return ++IncrementingInputID; } }  void UAbilityInputBindingComponent::SetInputBinding(UInputAction* InInputAction, FGameplayAbilitySpecHandle AbilitySpecHandle) { FGameplayAbilitySpec* BindingAbility {FindAbilitySpec(AbilitySpecHandle)};  FAbilityInputBinding* AbilityInputBinding {MappedAbilities.Find(InInputAction)}; if(AbilityInputBinding) { if(FGameplayAbilitySpec* OldBoundAbility {FindAbilitySpec(AbilityInputBinding-&gt;BoundAbilitiesStack.Top())}; OldBoundAbility &amp;&amp; OldBoundAbility-&gt;InputID == AbilityInputBinding-&gt;InputID) { OldBoundAbility-&gt;InputID = AbilityInputBindingComponent_Impl::InvalidInputID; } } else { AbilityInputBinding = &amp;MappedAbilities.Add(InInputAction); AbilityInputBinding-&gt;InputID = AbilityInputBindingComponent_Impl::GetNextInputID(); }  if(BindingAbility) { BindingAbility-&gt;InputID = AbilityInputBinding-&gt;InputID; }  AbilityInputBinding-&gt;BoundAbilitiesStack.Push(AbilitySpecHandle); TryBindAbilityInput(InInputAction, *AbilityInputBinding);  }  void UAbilityInputBindingComponent::ClearInputBinding(FGameplayAbilitySpecHandle InAbilityHandle) { FGameplayAbilitySpec* FoundAbility {FindAbilitySpec(InAbilityHandle)}; if(!FoundAbility) return;  auto MappedIterator = MappedAbilities.CreateIterator(); while (MappedIterator) { if(MappedIterator.Value().InputID == FoundAbility-&gt;InputID) { break; } ++MappedIterator; }  if(!MappedIterator) return;  FAbilityInputBinding&amp; AbilityInputBinding = MappedIterator.Value(); if(AbilityInputBinding.BoundAbilitiesStack.Remove(InAbilityHandle) &gt; 0) { if(AbilityInputBinding.BoundAbilitiesStack.Num() &gt; 0) { FGameplayAbilitySpec* StackedAbility {FindAbilitySpec(AbilityInputBinding.BoundAbilitiesStack.Top())}; if(StackedAbility &amp;&amp; StackedAbility-&gt;InputID == 0) { StackedAbility-&gt;InputID = AbilityInputBinding.InputID; } } else { RemoveEntry(MappedIterator.Key()); }  FoundAbility-&gt;InputID = AbilityInputBindingComponent_Impl::InvalidInputID; }  }  void UAbilityInputBindingComponent::ClearAbilityBindings(UInputAction* InInputAction) { RemoveEntry(InInputAction);  }  void UAbilityInputBindingComponent::SetupPlayerControls_Implementation(UEnhancedInputComponent* InPlayerInputComponent) { for(auto&amp; InputBinding : MappedAbilities) { if(auto* CurrentInputComponent {GetInputComponent()}; CurrentInputComponent) { CurrentInputComponent-&gt;RemoveBindingByHandle(InputBinding.Value.OnPressedHandle); CurrentInputComponent-&gt;RemoveBindingByHandle(InputBinding.Value.OnReleasedHandle); } if(AbilityComponent) { const int32 ExpectedInputID = InputBinding.Value.InputID; for(FGameplayAbilitySpecHandle AbilityHandle : InputBinding.Value.BoundAbilitiesStack) { if(FGameplayAbilitySpec* FoundAbility = AbilityComponent-&gt;FindAbilitySpecFromHandle(AbilityHandle); FoundAbility &amp;&amp; FoundAbility-&gt;InputID == ExpectedInputID) { FoundAbility-&gt;InputID = AbilityInputBindingComponent_Impl::InvalidInputID; } } } }  AbilityComponent = nullptr;  }  void UAbilityInputBindingComponent::ReleaseInputComponent(AController* OldController) { ResetBindings();  Super::ReleaseInputComponent(OldController);  }  void UAbilityInputBindingComponent::ResetBindings() { for(auto&amp; InputBinding : MappedAbilities) { if(auto* CurrentInputComponent {GetInputComponent()}; CurrentInputComponent) { CurrentInputComponent-&gt;RemoveBindingByHandle(InputBinding.Value.OnPressedHandle); CurrentInputComponent-&gt;RemoveBindingByHandle(InputBinding.Value.OnReleasedHandle); } if(AbilityComponent) { const int32 ExpectedInputID = InputBinding.Value.InputID; for(FGameplayAbilitySpecHandle AbilitySpecHandle : InputBinding.Value.BoundAbilitiesStack) { FGameplayAbilitySpec* FoundAbility = AbilityComponent-&gt;FindAbilitySpecFromHandle(AbilitySpecHandle); if(FoundAbility &amp;&amp; FoundAbility-&gt;InputID == ExpectedInputID) { FoundAbility-&gt;InputID = AbilityInputBindingComponent_Impl::InvalidInputID; } } }  }  AbilityComponent = nullptr;  }  void UAbilityInputBindingComponent::RunAbilitySystemSetup() { const AActor* Owner {GetOwner()}; check(Owner);  AbilityComponent = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(Owner); if(!AbilityComponent) return;  for (auto&amp; InputBinding : MappedAbilities) { const int32 NewInputID {AbilityInputBindingComponent_Impl::GetNextInputID()}; InputBinding.Value.InputID = NewInputID; for(const FGameplayAbilitySpecHandle AbilityHandle : InputBinding.Value.BoundAbilitiesStack) { if(FGameplayAbilitySpec* FoundAbility = AbilityComponent-&gt;FindAbilitySpecFromHandle(AbilityHandle); FoundAbility) { FoundAbility-&gt;InputID = NewInputID; } } }  }  void UAbilityInputBindingComponent::OnAbilityInputPressed(UInputAction* InInputAction) { if(!AbilityComponent) { RunAbilitySystemSetup(); } if(AbilityComponent) { if(const FAbilityInputBinding* FoundBinding {MappedAbilities.Find(InInputAction)}; FoundBinding &amp;&amp; ensure(FoundBinding-&gt;InputID) != AbilityInputBindingComponent_Impl::InvalidInputID) { AbilityComponent-&gt;AbilityLocalInputPressed(FoundBinding-&gt;InputID); } }  }  void UAbilityInputBindingComponent::OnAbilityInputReleased(UInputAction* InInputAction) { if(!AbilityComponent) return;  if(const FAbilityInputBinding* FoundBinding {MappedAbilities.Find(InInputAction)}; FoundBinding &amp;&amp; ensure(FoundBinding-&gt;InputID != AbilityInputBindingComponent_Impl::InvalidInputID)) { AbilityComponent-&gt;AbilityLocalInputReleased(FoundBinding-&gt;InputID); }  }  void UAbilityInputBindingComponent::RemoveEntry(UInputAction* InInputAction) { if(FAbilityInputBinding* Binding {MappedAbilities.Find(InInputAction)}) { if(auto* CurrentInputComponent {GetInputComponent()}; CurrentInputComponent) { CurrentInputComponent-&gt;RemoveBindingByHandle(Binding-&gt;OnPressedHandle); CurrentInputComponent-&gt;RemoveBindingByHandle(Binding-&gt;OnReleasedHandle); }  for(FGameplayAbilitySpecHandle AbilityHandle : Binding-&gt;BoundAbilitiesStack) { FGameplayAbilitySpec* AbilitySpec = FindAbilitySpec(AbilityHandle); if(AbilitySpec &amp;&amp; AbilitySpec-&gt;InputID == Binding-&gt;InputID) { AbilitySpec-&gt;InputID = AbilityInputBindingComponent_Impl::InvalidInputID; } }  MappedAbilities.Remove(InInputAction); }  }  FGameplayAbilitySpec* UAbilityInputBindingComponent::FindAbilitySpec(FGameplayAbilitySpecHandle InHandle) { FGameplayAbilitySpec* FoundAbility{nullptr}; if(AbilityComponent) { FoundAbility = AbilityComponent-&gt;FindAbilitySpecFromHandle(InHandle); } return FoundAbility;  }  void UAbilityInputBindingComponent::TryBindAbilityInput(UInputAction* InInputAction, FAbilityInputBinding&amp; InAbilityInputBinding) { if(auto* CurrentInputComponent {GetInputComponent()}; CurrentInputComponent) { if(InAbilityInputBinding.OnPressedHandle == 0) { InAbilityInputBinding.OnPressedHandle = CurrentInputComponent-&gt;BindAction(InInputAction, ETriggerEvent::Started, this, &amp;UAbilityInputBindingComponent::OnAbilityInputPressed, InInputAction).GetHandle(); }  if(InAbilityInputBinding.OnReleasedHandle == 0) { InAbilityInputBinding.OnReleasedHandle = CurrentInputComponent-&gt;BindAction(InInputAction, ETriggerEvent::Completed, this, &amp;UAbilityInputBindingComponent::OnAbilityInputReleased, InInputAction).GetHandle(); } }  }<\/code><\/pre>\n<h2>Game Features<\/h2>\n<p>\u041f\u043b\u0430\u0433\u0438\u043d Game Features \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0436\u043d\u043e \u0432\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u0438 \u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043f\u043e\u0442\u0440\u0435\u0431\u043d\u043e\u0441\u0442\u0435\u0439 \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u0438\u0437\u0431\u0435\u0433\u0430\u0442\u044c \u043b\u0438\u0448\u043d\u0438\u0445 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439.<\/p>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432 \u0434\u043b\u044f \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044f \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432, \u043d\u0443\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0432 Game Features, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c Mapping Context \u0432 \u043e\u0431\u0449\u0443\u044e \u0441\u0443\u0431\u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u0432\u0432\u043e\u0434\u0430.<\/p>\n<pre><code class=\"cpp\">AddInputContextMapping_GameFeatureAction.h  #pragma once  #include \"CoreMinimal.h\" #include \"GameFeature\/GameFeature_WorldActionBase.h\" #include \"AddInputContextMapping_GameFeatureAction.generated.h\"  class UInputMappingContext; struct FComponentRequestHandle;   UCLASS(DisplayName=\"Add Input Context Mapping\") class COMBATABILITIESSYSTEMRUNTIME_API UAddInputContextMapping_GameFeatureAction : public UGameFeature_WorldActionBase { GENERATED_BODY()  public:  virtual void OnGameFeatureActivating() override; virtual void OnGameFeatureDeactivating(FGameFeatureDeactivatingContext&amp; Context) override;  #if WITH_EDITORONLY_DATA virtual void AddAdditionalAssetBundleData(FAssetBundleData&amp; AssetBundleData) override; #endif  #if WITH_EDITOR virtual EDataValidationResult IsDataValid(TArray&lt;FText&gt;&amp; ValidationErrors) override; #endif   private: virtual void AddToWorld(const FWorldContext&amp; InWorldContext) override;  void Reset(); void HandleControllerExtension(AActor* InActor, FName EventName); void AddInputMappingForPlayer(UPlayer* InPlayer); void RemoveInputMapping(APlayerController* InPlayerController);  private: UPROPERTY(EditAnywhere, Category=\"Input\", meta=(AllowPrivateAccess=\"true\")) TSoftObjectPtr&lt;UInputMappingContext&gt; InputMapping; UPROPERTY(EditAnywhere, Category=\"Input\", meta=(AllowPrivateAccess=\"true\")) int32 Priority{0};  TArray&lt;TSharedPtr&lt;FComponentRequestHandle&gt;&gt; ExtensionRequestHandles; TArray&lt;TWeakObjectPtr&lt;APlayerController&gt;&gt; ControllersAddedTo;  };<\/code><\/pre>\n<pre><code class=\"cpp\">AddInputContextMapping_GameFeatureAction.cpp  #include \"GameFeature\/AddInputContextMapping_GameFeatureAction.h\" #include \"CombatAbilitiesSystemRuntime\/Public\/CombatAbilitiesSystemRuntimeModule.h\" #include \"Engine\/AssetManager.h\" #include \"GameFeaturesSubsystemSettings.h\" #include \"Components\/GameFrameworkComponentManager.h\" #include \"GameFramework\/PlayerController.h\" #include \"EnhancedInputSubsystems.h\" #include \"InputMappingContext.h\"  #define LOCTEXT_NAMESPACE \"CombatAbilitiesSystemRuntimeFeatures\"  #include UE_INLINE_GENERATED_CPP_BY_NAME(AddInputContextMapping_GameFeatureAction)  void UAddInputContextMapping_GameFeatureAction::OnGameFeatureActivating() { if (!ensure(ExtensionRequestHandles.IsEmpty()) || !ensure(ControllersAddedTo.IsEmpty())) { Reset(); } Super::OnGameFeatureActivating(); }  void UAddInputContextMapping_GameFeatureAction::OnGameFeatureDeactivating(FGameFeatureDeactivatingContext&amp; Context) { Super::OnGameFeatureDeactivating(Context); Reset(); }  #if WITH_EDITORONLY_DATA void UAddInputContextMapping_GameFeatureAction::AddAdditionalAssetBundleData(FAssetBundleData&amp; AssetBundleData) { if (UAssetManager::IsValid()) { AssetBundleData.AddBundleAsset(UGameFeaturesSubsystemSettings::LoadStateClient, InputMapping.ToSoftObjectPath()); AssetBundleData.AddBundleAsset(UGameFeaturesSubsystemSettings::LoadStateServer, InputMapping.ToSoftObjectPath()); } } #endif  #if WITH_EDITOR EDataValidationResult UAddInputContextMapping_GameFeatureAction::IsDataValid(TArray&lt;FText&gt;&amp; ValidationErrors) { EDataValidationResult Result {CombineDataValidationResults(Super::IsDataValid(ValidationErrors), EDataValidationResult::Valid)};  if (InputMapping.IsNull()) { Result = EDataValidationResult::Invalid; ValidationErrors.Add(LOCTEXT(\"NullInputMapping\", \"Null InputMapping.\")); }  return Result; } #endif  void UAddInputContextMapping_GameFeatureAction::AddToWorld(const FWorldContext&amp; WorldContext) { const UWorld* World {WorldContext.World()}; const UGameInstance* GameInstance {WorldContext.OwningGameInstance.Get()};  if ((GameInstance != nullptr) &amp;&amp; (World != nullptr) &amp;&amp; World-&gt;IsGameWorld()) { if (UGameFrameworkComponentManager* ComponentMan {UGameInstance::GetSubsystem&lt;UGameFrameworkComponentManager&gt;(GameInstance)}) { if (!InputMapping.IsNull()) { const UGameFrameworkComponentManager::FExtensionHandlerDelegate AddAbilitiesDelegate {UGameFrameworkComponentManager::FExtensionHandlerDelegate::CreateUObject( this, &amp;UAddInputContextMapping_GameFeatureAction::HandleControllerExtension)}; const TSharedPtr&lt;FComponentRequestHandle&gt; ExtensionRequestHandle {ComponentMan-&gt;AddExtensionHandler(APlayerController::StaticClass(), AddAbilitiesDelegate)};  ExtensionRequestHandles.Add(ExtensionRequestHandle); } } } }  void UAddInputContextMapping_GameFeatureAction::Reset() { ExtensionRequestHandles.Empty();  while (!ControllersAddedTo.IsEmpty()) { TWeakObjectPtr&lt;APlayerController&gt; ControllerPtr {ControllersAddedTo.Top()}; if (ControllerPtr.IsValid()) { RemoveInputMapping(ControllerPtr.Get()); } else { ControllersAddedTo.Pop(); } } }  void UAddInputContextMapping_GameFeatureAction::HandleControllerExtension(AActor* InActor, FName InEventName) { APlayerController* AsController {CastChecked&lt;APlayerController&gt;(InActor)};  if (InEventName == UGameFrameworkComponentManager::NAME_ExtensionRemoved || InEventName == UGameFrameworkComponentManager::NAME_ReceiverRemoved) { RemoveInputMapping(AsController); } else if (InEventName == UGameFrameworkComponentManager::NAME_ExtensionAdded || InEventName == UGameFrameworkComponentManager::NAME_GameActorReady) { AddInputMappingForPlayer(AsController-&gt;GetLocalPlayer()); } }  void UAddInputContextMapping_GameFeatureAction::AddInputMappingForPlayer(UPlayer* InPlayer) { if (const ULocalPlayer* LocalPlayer {Cast&lt;ULocalPlayer&gt;(InPlayer)}) { if (UEnhancedInputLocalPlayerSubsystem* InputSystem {LocalPlayer-&gt;GetSubsystem&lt;UEnhancedInputLocalPlayerSubsystem&gt;()}) { if (!InputMapping.IsNull()) { InputSystem-&gt;AddMappingContext(InputMapping.LoadSynchronous(), Priority); } } else { UE_LOG(LogCombatAbilitySystem, Error, TEXT(\"Failed to find `UEnhancedInputLocalPlayerSubsystem` for local player. Input mappings will not be added. Make sure you're set to use the EnhancedInput system via config file.\")); } } }  void UAddInputContextMapping_GameFeatureAction::RemoveInputMapping(APlayerController* InPlayerController) { if (const ULocalPlayer* LocalPlayer {InPlayerController-&gt;GetLocalPlayer()}) { if (UEnhancedInputLocalPlayerSubsystem* InputSystem = LocalPlayer-&gt;GetSubsystem&lt;UEnhancedInputLocalPlayerSubsystem&gt;()) { InputSystem-&gt;RemoveMappingContext(InputMapping.Get()); } }  ControllersAddedTo.Remove(InPlayerController); }  #undef LOCTEXT_NAMESPACE<\/code><\/pre>\n<p>\u0414\u043b\u044f \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u0431\u044b\u043b\u0430 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u043a \u0430\u043a\u0442\u043e\u0440\u0443, \u043d\u0430\u0434\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0447\u0442\u043e\u0431\u044b \u043e\u043d \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043b \u0441\u0435\u0431\u044f \u043d\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432 \u0443 <strong>Game Framework Component Manager. <\/strong>\u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0434\u043b\u044f PlayerController, \u0441 \u0434\u0440\u0443\u0433\u0438\u043c\u0438 \u0430\u043a\u0442\u043e\u0440\u0430\u043c\u0438 \u0442\u043e\u0436\u0435 \u0441\u0430\u043c\u043e\u0435, \u043d\u043e \u0431\u0435\u0437 ReceivedPlayer() \u0438 PlayerTick():<\/p>\n<pre><code class=\"cpp\">AModularPlayerController.h  #pragma once  #include \"GameFramework\/PlayerController.h\"  #include \"ModularPlayerController.generated.h\"   UCLASS(Blueprintable) class MODULARGAMEPLAYACTORS_API AModularPlayerController : public APlayerController { GENERATED_BODY()  public: virtual void PreInitializeComponents() override;  virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;  virtual void ReceivedPlayer() override;  virtual void PlayerTick(float DeltaTime) override;  };<\/code><\/pre>\n<pre><code class=\"cpp\">ModularPlayerController.cpp  #include \"ModularPlayerController.h\"  #include \"Components\/ControllerComponent.h\" #include \"Components\/GameFrameworkComponentManager.h\"  #include UE_INLINE_GENERATED_CPP_BY_NAME(ModularPlayerController)  void AModularPlayerController::PreInitializeComponents() { Super::PreInitializeComponents();  UGameFrameworkComponentManager::AddGameFrameworkComponentReceiver(this);  }  void AModularPlayerController::EndPlay(const EEndPlayReason::Type EndPlayReason) { UGameFrameworkComponentManager::RemoveGameFrameworkComponentReceiver(this);  Super::EndPlay(EndPlayReason);  }  void AModularPlayerController::ReceivedPlayer() { UGameFrameworkComponentManager::SendGameFrameworkComponentExtensionEvent(this, UGameFrameworkComponentManager::NAME_GameActorReady);  Super::ReceivedPlayer();  TArray&lt;UControllerComponent*&gt; ModularComponents; GetComponents(ModularComponents); for (UControllerComponent* Component : ModularComponents) { Component-&gt;ReceivedPlayer(); }  }  void AModularPlayerController::PlayerTick(float DeltaTime) { Super::PlayerTick(DeltaTime);  TArray&lt;UControllerComponent*&gt; ModularComponents; GetComponents(ModularComponents); for (UControllerComponent* Component : ModularComponents) { Component-&gt;PlayerTick(DeltaTime); }  }<\/code><\/pre>\n<h2>\u041f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435<\/h2>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0432\u0441\u0435\u0439 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0445 \u043a\u043b\u0430\u0441\u0441\u043e\u0432, \u043f\u0440\u0438\u0448\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u044d\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c.<\/p>\n<p>\u0412 GameMode \u0432\u044b\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c \u0432 Player Controller Class AModularPlayerController.<br \/>\u0414\u0430\u043b\u043b\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c <strong>AddInputContextMapping_GameFeatureAction<\/strong> (Add Input Context Mapping) \u0438 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c <strong>InputMappingContext <\/strong>\u0432 \u0444\u0430\u0439\u043b GameFeatureData<strong>:<\/strong><\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/50a\/40a\/082\/50a40a082bf240d225304b24b6e4498a.PNG\" width=\"430\" height=\"280\"\/><\/figure>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/7b0\/39b\/5a6\/7b039b5a637eee5331660b71102ed335.PNG\" width=\"512\" height=\"225\"\/><\/figure>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0447\u0442\u043e\u0431\u044b \u044d\u0442\u043e \u0447\u0443\u0434\u043e \u0437\u0430\u0440\u0430\u0431\u043e\u0442\u0430\u043b\u043e \u043f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u0438\u0433\u0440\u044b, \u043d\u0430\u0434\u043e \u0432 \u0447\u0435\u0440\u0442\u0435\u0436\u0435 \u0443\u0440\u043e\u0432\u043d\u044f \u043f\u0440\u043e\u043f\u0438\u0441\u0430\u0442\u044c \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043a\u043e\u043d\u0441\u043e\u043b\u044c\u043d\u043e\u0439 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u2014 &#171;LoadGameFeaturePlugin CombatAbilitiesSystem&#187;:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/c1f\/eca\/568\/c1feca5685098c0a20a7cd03b9a4b56b.png\" width=\"672\" height=\"196\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/c1f\/eca\/568\/c1feca5685098c0a20a7cd03b9a4b56b.png\"\/><\/figure>\n<h2>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h2>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0432 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u0447\u0430\u0441\u0442\u0438 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u043e \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 \u0441\u0430\u043c\u043e\u0433\u043e <strong>AbilitySystemComponent<\/strong> \u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 Abilities.<\/p>\n<p>\u0421\u043f\u0430\u0441\u0438\u0431\u043e \u0437\u0430 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435! \u0421\u0442\u0430\u0432\u044c\u0442\u0435 \u043b\u0430\u0439\u043a\u0438, \u043f\u043e\u0434\u043f\u0438\u0441\u044b\u0432\u0430\u0439\u0442\u0435\u0441\u044c \u0438 \u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0439\u0442\u0435 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/832914\/\"> https:\/\/habr.com\/ru\/articles\/832914\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><\/figure>\n<p>\u0414\u043b\u044f \u0441\u0432\u043e\u0435\u0439 \u043d\u043e\u0432\u043e\u0439 \u0438\u0433\u0440\u044b Code Of Person (\u041a\u0430\u043a-\u043d\u0438\u0431\u0443\u0434\u044c \u0432 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0445 \u0441\u0442\u0430\u0442\u044c\u044f\u0445 \u043f\u0440\u043e \u0435\u0451 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443) \u044f \u0440\u0435\u0448\u0438\u043b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0438 \u0440\u0430\u0441\u0448\u0438\u0440\u0438\u0442\u044c \u043f\u043b\u0430\u0433\u0438\u043d Gameplay Ability System.<\/p>\n<p>\u0412 \u0441\u0435\u0440\u0438\u0438 \u0441\u0442\u0430\u0442\u044c\u0435\u0439 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u043e \u043c\u043e\u0451\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u043b\u0430\u0433\u0438\u043d\u0430 Combat Abilities System, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0435\u0442 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0433\u043e \u043f\u043b\u0430\u0433\u0438\u043d\u0430 Gameplay Ability System \u0432 Unreal Engine. \u041c\u044b \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0448\u0430\u0433\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438, \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u043d\u044b\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u0438 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0434\u0435\u043b\u0430\u044e\u0442 \u043d\u0430\u0448 \u043f\u043b\u0430\u0433\u0438\u043d \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u043c \u0438 \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u043c \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0431\u043e\u0435\u0432\u044b\u0445 \u043c\u0435\u0445\u0430\u043d\u0438\u043a \u0432 \u0438\u0433\u0440\u0430\u0445.<\/p>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u043f\u043e\u043a\u0430\u0436\u0443 \u0441\u0432\u043e\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 Enhanced Input \u0438 Game Features.<\/p>\n<h2>\u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 Gameplay Ability System \u0438 \u0434\u043b\u044f \u0447\u0435\u0433\u043e \u043e\u043d \u043d\u0443\u0436\u0435\u043d?<\/h2>\n<p>Gameplay Ability System \u2014 \u044d\u0442\u043e \u043a\u043e\u043c\u043f\u043b\u0435\u043a\u0441\u043d\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430, \u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u043d\u0430\u044f \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u044f\u043c\u0438. \u041e\u043d\u0430 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0440\u0430\u0437\u043d\u043e\u043e\u0431\u0440\u0430\u0437\u043d\u044b\u0435 \u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u0438, \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0438\u0445 \u0430\u043a\u0442\u0438\u0432\u0430\u0446\u0438\u0435\u0439, \u044d\u0444\u0444\u0435\u043a\u0442\u0430\u043c\u0438 \u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435\u043c \u0441 \u0434\u0440\u0443\u0433\u0438\u043c\u0438 \u0441\u0438\u0441\u0442\u0435\u043c\u0430\u043c\u0438 \u0438\u0433\u0440\u044b. \u041e\u0434\u043d\u0430\u043a\u043e, \u0431\u0430\u0437\u043e\u0432\u043e\u0433\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u0430 \u043c\u043d\u0435 \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e, \u0438 \u044f \u0445\u043e\u0442\u0435\u043b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0442\u0430\u043a \u0447\u0442\u043e\u0431\u044b \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0432\u043e\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043c\u0435\u0436\u0434\u0443 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u043c\u0438 + \u0437\u0430\u044f\u0432\u0438\u0442\u044c \u0441\u0435\u0431\u044f <\/p>\n<p>\u0414\u043b\u044f \u0441\u0435\u0431\u044f \u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043b \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0436\u0435\u043b\u0430\u043d\u0438\u044f \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b:<\/p>\n<ul>\n<li>\n<p>\u0431\u043e\u0435\u0432\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430<\/p>\n<\/li>\n<li>\n<p>\u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0432\u0432\u043e\u0434\u0430 \u0438\u0433\u0440\u043e\u043a\u0430<\/p>\n<\/li>\n<li>\n<p>\u043c\u043e\u0434\u0443\u043b\u044c\u043d\u043e\u0441\u0442\u044c <\/p>\n<\/li>\n<\/ul>\n<h2>\u041f\u0440\u0438\u0432\u044f\u0437\u043a\u0430 \u043a Enhanced Input<\/h2>\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u044f \u0441\u043e\u0437\u0434\u0430\u043b \u043a\u043b\u0430\u0441\u0441 <strong>UPlayerControlsComponent<\/strong>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u0432\u0435\u0447\u0430\u0442\u044c \u0437\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u0432\u0432\u043e\u0434\u0430 \u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0447\u0435\u0440\u0435\u0437 Enhanced Input <\/p>\n<pre><code class=\"cpp\">PlayerControlsComponent.h  #pragma once  #include \"EnhancedInputComponent.h\" #include \"Components\/PawnComponent.h\" #include \"PlayerControlsComponent.generated.h\"  class UEnhancedInputLocalPlayerSubsystem; class UInputAction; class UInputMappingContext; \/**  *   *\/ UCLASS(BlueprintType, Blueprintable, meta=(BlueprintSpawnableComponent)) class COMBATABILITIESSYSTEMRUNTIME_API UPlayerControlsComponent : public UPawnComponent { GENERATED_BODY()  public: virtual void OnRegister() override; virtual void OnUnregister() override;  protected: UFUNCTION(BlueprintNativeEvent, Category=\"Player Controls\") void SetupPlayerControls(UEnhancedInputComponent* InPlayerInputComponent);  UFUNCTION(BlueprintNativeEvent, Category=\"Player Controls\") void TeardownPlayerControls(UEnhancedInputComponent* InPlayerInputComponent);  template&lt;class UserClass, typename FuncType&gt; bool BindInputAction(const UInputAction* InAction, const ETriggerEvent InEventType, UserClass* InObject, FuncType InFunction) { if(ensure(InputComponent != nullptr) &amp;&amp; ensure(InAction != nullptr)) { InputComponent-&gt;BindAction(InAction, InEventType, InObject, InFunction); return true; } return false; }  UFUNCTION() virtual void OnPawnRestarted(APawn* InPawn); UFUNCTION() virtual void OnControllerChanged(APawn* InPawn, AController* InOldController, AController* NewController);  virtual void SetupInputComponent(APawn* InPawn); virtual void ReleaseInputComponent(AController* OldController = nullptr); UEnhancedInputLocalPlayerSubsystem* GetEnhancedInputSubsystem(AController* OldController = nullptr) const;  UEnhancedInputComponent* GetInputComponent() const { return InputComponent;}  private: UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=\"Player Controls\", meta=(AllowPrivateAccess=\"true\")) TObjectPtr&lt;UInputMappingContext&gt; InputMappingContext{nullptr}; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=\"Player Controls\", meta=(AllowPrivateAccess=\"true\")) int32 InputPriority{0};  UPROPERTY(Transient) UEnhancedInputComponent* InputComponent;  };<\/code><\/pre>\n<pre><code class=\"cpp\">UPlayerControlsComponent.cpp   #include \"Components\/PlayerControlsComponent.h\"  #include \"EnhancedInputSubsystems.h\"  #include UE_INLINE_GENERATED_CPP_BY_NAME(PlayerControlsComponent)  void UPlayerControlsComponent::SetupPlayerControls_Implementation(UEnhancedInputComponent* InPlayerInputComponent) { }  void UPlayerControlsComponent::TeardownPlayerControls_Implementation(UEnhancedInputComponent* InPlayerInputComponent) { }  void UPlayerControlsComponent::OnRegister() { Super::OnRegister();  const UWorld* World {GetWorld()}; APawn* PawnOwner{GetPawn&lt;APawn&gt;()}; if(!ensure(PawnOwner) &amp;&amp; !World-&gt;IsGameWorld()) return;  PawnOwner-&gt;ReceiveRestartedDelegate.AddDynamic(this, &amp;UPlayerControlsComponent::OnPawnRestarted); PawnOwner-&gt;ReceiveControllerChangedDelegate.AddDynamic(this, &amp;UPlayerControlsComponent::OnControllerChanged);  if(PawnOwner-&gt;InputComponent) { OnPawnRestarted(PawnOwner); }  }  void UPlayerControlsComponent::OnUnregister() { if(const UWorld* World {GetWorld()}; World &amp;&amp; World-&gt;IsGameWorld()) { ReleaseInputComponent(); if(auto* PawnOwner{GetPawn&lt;APawn&gt;()}; PawnOwner) { PawnOwner-&gt;ReceiveRestartedDelegate.RemoveAll(this); PawnOwner-&gt;ReceiveControllerChangedDelegate.RemoveAll(this); } }  Super::OnUnregister(); }  void UPlayerControlsComponent::OnPawnRestarted(APawn* InPawn) { if(ensure(InPawn &amp;&amp; InPawn == GetOwner()) &amp;&amp; InPawn-&gt;InputComponent) { ReleaseInputComponent(); if(InPawn-&gt;InputComponent) { SetupInputComponent(InPawn); } } }  void UPlayerControlsComponent::OnControllerChanged(APawn* InPawn, AController* InOldController, AController* NewController) { if(ensure(InPawn &amp;&amp; InPawn == GetOwner()) &amp;&amp; InOldController) { ReleaseInputComponent(InOldController); } }  void UPlayerControlsComponent::SetupInputComponent(APawn* InPawn) { InputComponent = CastChecked&lt;UEnhancedInputComponent&gt;(InPawn-&gt;InputComponent);  UEnhancedInputLocalPlayerSubsystem* Subsystem = {GetEnhancedInputSubsystem()}; check(Subsystem); if(InputMappingContext) { Subsystem-&gt;AddMappingContext(InputMappingContext, InputPriority); } SetupPlayerControls(InputComponent); }  void UPlayerControlsComponent::ReleaseInputComponent(AController* OldController) { if(UEnhancedInputLocalPlayerSubsystem* Subsystem {GetEnhancedInputSubsystem(OldController)}; Subsystem &amp;&amp; InputComponent) { TeardownPlayerControls(InputComponent); if(InputMappingContext) { Subsystem-&gt;RemoveMappingContext(InputMappingContext); } } InputComponent = nullptr; }  UEnhancedInputLocalPlayerSubsystem* UPlayerControlsComponent::GetEnhancedInputSubsystem( AController* OldController) const { const APlayerController* PlayerController {GetController&lt;APlayerController&gt;()}; if(!PlayerController) { PlayerController = Cast&lt;APlayerController&gt;(OldController); if(!PlayerController) { return nullptr; } }  const ULocalPlayer* LocalPlayer {PlayerController-&gt;GetLocalPlayer()}; if(!LocalPlayer) return nullptr;  return LocalPlayer-&gt;GetSubsystem&lt;UEnhancedInputLocalPlayerSubsystem&gt;(); } <\/code><\/pre>\n<p>\u0414\u0430\u043b\u0435\u0435 \u043d\u0443\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043e\u0442 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430, \u043e\u043f\u0438\u0441\u0430\u043d\u043d\u043e\u0433\u043e \u0432\u044b\u0448\u0435, \u0434\u043e\u0447\u0435\u0440\u043d\u0438\u0439 \u043a\u043b\u0430\u0441\u0441  <strong>UAbilityInputBindingComponent<\/strong>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u0432\u0435\u0447\u0430\u0442\u044c \u0437\u0430 \u043f\u0440\u0438\u0432\u044f\u0437\u043a\u0443 \u0432\u0432\u043e\u0434\u0430 \u0438 \u0438\u0433\u0440\u043e\u0432\u044b\u043c\u0438 \u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u044f\u043c\u0438:<\/p>\n<pre><code class=\"cpp\">AbilityInputBindingComponent.h  #pragma once  #include \"GameplayAbilitySpec.h\" #include \"GameplayAbilitySpecHandle.h\" #include \"Components\/PlayerControlsComponent.h\" #include \"AbilityInputBindingComponent.generated.h\"  class UAbilitySystemComponent;  USTRUCT() struct FAbilityInputBinding { GENERATED_BODY()  int32 InputID{0}; uint32 OnPressedHandle{0}; uint32 OnReleasedHandle{0}; TArray&lt;FGameplayAbilitySpecHandle&gt; BoundAbilitiesStack;  };  UCLASS(meta=(BlueprintSpawnableComponent)) class COMBATABILITIESSYSTEMRUNTIME_API UAbilityInputBindingComponent : public UPlayerControlsComponent { GENERATED_BODY()  public: UFUNCTION(BlueprintCallable, Category=\"Abilities\") void SetInputBinding(UInputAction* InInputAction, FGameplayAbilitySpecHandle AbilitySpecHandle);  UFUNCTION(BlueprintCallable, Category=\"Abilities\") void ClearInputBinding(FGameplayAbilitySpecHandle InAbilityHandle);  UFUNCTION(BlueprintCallable, Category=\"Abilities\") void ClearAbilityBindings(UInputAction* InInputAction);  virtual void SetupPlayerControls_Implementation(UEnhancedInputComponent* InPlayerInputComponent) override; virtual void ReleaseInputComponent(AController* OldController) override;  private: void ResetBindings(); void RunAbilitySystemSetup(); void OnAbilityInputPressed(UInputAction* InInputAction); void OnAbilityInputReleased(UInputAction* InInputAction);  void RemoveEntry(UInputAction* InInputAction);  FGameplayAbilitySpec* FindAbilitySpec(FGameplayAbilitySpecHandle InHandle); void TryBindAbilityInput(UInputAction* InInputAction, FAbilityInputBinding&amp; InAbilityInputBinding);  private: UPROPERTY(Transient) UAbilitySystemComponent* AbilityComponent;  UPROPERTY(Transient) TMap&lt;UInputAction*, FAbilityInputBinding&gt; MappedAbilities;  };<\/code><\/pre>\n<pre><code class=\"cpp\">AbilityInputBindingComponent.cpp  #include \"Input\/AbilityInputBindingComponent.h\"  #include \"AbilitySystemComponent.h\" #include \"AbilitySystemGlobals.h\"  #include UE_INLINE_GENERATED_CPP_BY_NAME(AbilityInputBindingComponent)  namespace AbilityInputBindingComponent_Impl { constexpr int32 InvalidInputID{0}; int32 IncrementingInputID{InvalidInputID}; static int32 GetNextInputID() { return ++IncrementingInputID; } }  void UAbilityInputBindingComponent::SetInputBinding(UInputAction* InInputAction, FGameplayAbilitySpecHandle AbilitySpecHandle) { FGameplayAbilitySpec* BindingAbility {FindAbilitySpec(AbilitySpecHandle)};  FAbilityInputBinding* AbilityInputBinding {MappedAbilities.Find(InInputAction)}; if(AbilityInputBinding) { if(FGameplayAbilitySpec* OldBoundAbility {FindAbilitySpec(AbilityInputBinding-&gt;BoundAbilitiesStack.Top())}; OldBoundAbility &amp;&amp; OldBoundAbility-&gt;InputID == AbilityInputBinding-&gt;InputID) { OldBoundAbility-&gt;InputID = AbilityInputBindingComponent_Impl::InvalidInputID; } } else { AbilityInputBinding = &amp;MappedAbilities.Add(InInputAction); AbilityInputBinding-&gt;InputID = AbilityInputBindingComponent_Impl::GetNextInputID(); }  if(BindingAbility) { BindingAbility-&gt;InputID = AbilityInputBinding-&gt;InputID; }  AbilityInputBinding-&gt;BoundAbilitiesStack.Push(AbilitySpecHandle); TryBindAbilityInput(InInputAction, *AbilityInputBinding);  }  void UAbilityInputBindingComponent::ClearInputBinding(FGameplayAbilitySpecHandle InAbilityHandle) { FGameplayAbilitySpec* FoundAbility {FindAbilitySpec(InAbilityHandle)}; if(!FoundAbility) return;  auto MappedIterator = MappedAbilities.CreateIterator(); while (MappedIterator) { if(MappedIterator.Value().InputID == FoundAbility-&gt;InputID) { break; } ++MappedIterator; }  if(!MappedIterator) return;  FAbilityInputBinding&amp; <\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-427981","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/427981","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=427981"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/427981\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=427981"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=427981"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=427981"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}