PageSourceSearch

https://elmarathstudio.netlify.app/assets/js/359d8cd3.e6078995.js

js elmarathstudio.netlify.app collected 2026-10-03 10:42:27 UTC 10,694 bytes, 1 lines download raw bytes

1"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[6684],{28453:(e,t,n)=>{n.d(t,{R:()=>l,x:()=>c});var i=n(96540);const s={},a=i.createContext(s);function l(e){const t=i.useContext(a);return i.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function c(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:l(e.components),i.createElement(a.Provider,{value:t},e.children)}},38161:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>o,contentTitle:()=>c,default:()=>u,frontMatter:()=>l,metadata:()=>i,toc:()=>r});const i=JSON.parse('{"id":"Ability-Queue-Task-System/UseCaseExample","title":"Use Case Example","description":"This page shows one practical way I integrated the plugin into my project so you can see the typical workflow. This is only an example \u2014 adapt it to your needs.","source":"@site/docs/Ability-Queue-Task-System/UseCaseExample.md","sourceDirName":"Ability-Queue-Task-System","slug":"/Ability-Queue-Task-System/UseCaseExample","permalink":"/docs/Ability-Queue-Task-System/UseCaseExample","draft":false,"unlisted":false,"tags":[],"version":"current","sidebarPosition":4,"frontMatter":{"sidebar_position":4},"sidebar":"tutorialSidebar","previous":{"title":"Add Abilities to the Queue","permalink":"/docs/Ability-Queue-Task-System/AddAbilitiesToTheQueue"},"next":{"title":"Final Notes","permalink":"/docs/Ability-Queue-Task-System/UseCaseExample copy"}}');var s=n(74848),a=n(28453);const l={sidebar_position:4},c="Use Case Example",o={},r=[{value:"Melee Attack Gameplay Ability",id:"melee-attack-gameplay-ability",level:2},{value:"Adding Ability To The Queue",id:"adding-ability-to-the-queue",level:3},{value:"Changing Queue States",id:"changing-queue-states",level:2}];function d(e){const t={a:"a",admonition:"admonition",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",header:"header",hr:"hr",img:"img",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(t.header,{children:(0,s.jsx)(t.h1,{id:"use-case-example",children:"Use Case Example"})}),"\n",(0,s.jsx)(t.p,{children:"This page shows one practical way I integrated the plugin into my project so you can see the typical workflow. This is only an example \u2014 adapt it to your needs."}),"\n",(0,s.jsx)(t.h2,{id:"melee-attack-gameplay-ability",children:"Melee Attack Gameplay Ability"}),"\n",(0,s.jsxs)(t.p,{children:["I use a custom Gameplay Ability class for melee attacks. The class is mostly a standard ",(0,s.jsx)(t.em,{children:"Gameplay Ability"})," but I set the ",(0,s.jsx)(t.code,{children:"Instancing Policy"})," to ",(0,s.jsx)(t.code,{children:"Instanced Per Execution"})," so each activation creates a separate instance. You can use different instancing if your project requires it."]}),"\n",(0,s.jsx)(t.h3,{id:"adding-ability-to-the-queue",children:"Adding Ability To The Queue"}),"\n",(0,s.jsx)(t.p,{children:"My input \u2192 queue flow is:"}),"\n",(0,s.jsxs)(t.ol,{children:["\n",(0,s.jsxs)(t.li,{children:["Player presses an ability input \u2192 call ",(0,s.jsx)(t.code,{children:"Press Input ID"})," on the Ability System Component."]}),"\n",(0,s.jsxs)(t.li,{children:["When the ability's ",(0,s.jsx)(t.code,{children:"Event Activate Ability"})," runs, create/start the ",(0,s.jsx)(t.code,{children:"Add Ability To Queue"})," task."]}),"\n"]}),"\n",(0,s.jsx)(t.p,{children:"This lets input presses request activation even if the ability is already active."}),"\n",(0,s.jsx)(t.p,{children:(0,s.jsx)(t.img,{alt:"Add Ability To Queue Node",src:n(99482).A+"",width:"1116",height:"454"})}),"\n",(0,s.jsxs)(t.admonition,{title:"How to create an ability instance for every input press",type:"note",children:[(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsxs)(t.li,{children:["Add a boolean (e.g. ",(0,s.jsx)(t.code,{children:"Activate on Every Input Press"}),") to abilities that should spawn a new instance per press."]}),"\n",(0,s.jsxs)(t.li,{children:["Override ",(0,s.jsx)(t.code,{children:"AbilityLocalInputPressed(int32 InputID)"})," in your ",(0,s.jsx)(t.code,{children:"Ability System Component"})," to check the flag and call ",(0,s.jsx)(t.code,{children:"TryActivateAbility(Spec.Handle)"})," when the flag is true."]}),"\n"]}),(0,s.jsx)(t.p,{children:"The code you provided performs exactly this: it checks your custom ability's boolean and either forces a new instance or forwards the input to the active instance."}),(0,s.jsxs)(t.p,{children:["Because a new ability instance (and therefore new task) can be created on every press, creating the ",(0,s.jsx)(t.code,{children:"Add Ability To Queue"})," task in ",(0,s.jsx)(t.code,{children:"Event Activate Ability"})," works naturally with repeated inputs."]}),(0,s.jsxs)(t.p,{children:["Important: clean up queued tasks when they are cancelled (handle ",(0,s.jsx)(t.code,{children:"On Queue Canceled"}),") so requests do not linger."]}),(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-cpp",children:'void UThrallGuardAbilitySystem::AbilityLocalInputPressed(int32 InputID)\n{\n\t// Consume the input if this InputID is overloaded with GenericConfirm/Cancel and the GenericConfim/Cancel callback is bound\n\tif (IsGenericConfirmInputBound(InputID))\n\t{\n\t\tLocalInputConfirm();\n\t\treturn;\n\t}\n\n\tif (IsGenericCancelInputBound(InputID))\n\t{\n\t\tLocalInputCancel();\n\t\treturn;\n\t}\n\n\t// ---------------------------------------------------------\n\n\tABILITYLIST_SCOPE_LOCK();\n\tfor (FGameplayAbilitySpec& Spec : ActivatableAbilities.Items)\n\t{\n\t\tif (Spec.InputID == InputID)\n\t\t{\n\t\t\tif (Spec.Ability)\n\t\t\t{\n\t\t\t\tbool bShouldForceActivate = false;\n\t\t\t\tif (UThrallGuardGameplayAbilityBase* ThrallGuardAbility = Cast<UThrallGuardGameplayAbilityBase>(Spec.Ability))\n\t\t\t\t{\n\t\t\t\t\tbShouldForceActivate = ThrallGuardAbility->bActivateOnEveryInputPress;\n\t\t\t\t}\n                \n\t\t\t\tSpec.InputPressed = true;\n                \n\t\t\t\tif (bShouldForceActivate)\n\t\t\t\t{\n\t\t\t\t\t// Force activate a new instance regardless of current state\n\t\t\t\t\tTryActivateAbility(Spec.Handle);\n\t\t\t\t}\n\t\t\t\telse if (Spec.IsActive())\n\t\t\t\t{\n\t\t\t\t\t// Default behavior: send input pressed to active ability\n\t\t\t\t\tif (Spec.Ability->bReplicateInputDirectly && IsOwnerActorAuthoritative() == false)\n\t\t\t\t\t{\n\t\t\t\t\t\tServerSetInputPressed(Spec.Handle);\n\t\t\t\t\t}\n\n\t\t\t\t\tAbilitySpecInputPressed(Spec);\n\n\t\t\t\t\tPRAGMA_DISABLE_DEPRECATION_WARNINGS\n\t\t\t\t\t// Fixing this up to use the instance activation, but this function should be deprecated as it cannot work with InstancedPerExecution\n\t\t\t\t\tUE_CLOG(Spec.Ability->
1GetInstancingPolicy() == EGameplayAbilityInstancingPolicy::InstancedPerExecution, LogTemp, Warning, TEXT("%hs: %s is InstancedPerExecution. This is unreliable for Input as you may only interact with the latest spawned Instance"), __func__, *GetNameSafe(Spec.Ability));\n\t\t\t\t\tTArray<UGameplayAbility*> Instances = Spec.GetAbilityInstances();\n\t\t\t\t\tconst FGameplayAbilityActivationInfo& ActivationInfo = Instances.IsEmpty() ? Spec.ActivationInfo : Instances.Last()->GetCurrentActivationInfoRef();\n\t\t\t\t\tPRAGMA_ENABLE_DEPRECATION_WARNINGS\n\t\t\t\t\t// Invoke the InputPressed event. This is not replicated here. If someone is listening, they may replicate the InputPressed event to the server.\n\t\t\t\t\tInvokeReplicatedEvent(EAbilityGenericReplicatedEvent::InputPressed, Spec.Handle, ActivationInfo.GetActivationPredictionKey());                    \n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t// Ability is not active, so try to activate it\n\t\t\t\t\tTryActivateAbility(Spec.Handle);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n'})}),(0,s.jsx)(t.hr,{}),(0,s.jsxs)(t.p,{children:["This is the same logic as the parent implementation with the added ",(0,s.jsx)(t.code,{children:"bShouldForceActivate"})," check. So your ability system component will work as expected. If that flag is true, the code forces activation of a new ability instance on each input press."]}),(0,s.jsxs)(t.p,{children:["After enabling this behavior, pressing an ability input creates a new ability instance (if the flag is set), so ",(0,s.jsx)(t.code,{children:"Add Ability To Queue"})," can be called in ",(0,s.jsx)(t.code,{children:"Event Activate Ability"})," safely. Remember to finish or cancel queued tasks/abilities via your ",(0,s.jsx)(t.code,{children:"On Queue Canceled"})," handling by using ",(0,s.jsx)(t.code,{children:"End Ability"}),"."]})]}),"\n",(0,s.jsx)(t.h2,{id:"changing-queue-states",children:"Changing Queue States"}),"\n",(0,s.jsxs)(t.p,{children:["I use the ",(0,s.jsx)(t.code,{children:"bAutoCommitAbility"})," on the Ability Queue Settings. So whenever the queue gets confirmed, ",(0,s.jsx)(t.code,{children:"Event Commit Execute"})," will fire too."]}),"\n",(0,s.jsxs)(t.p,{children:["In ",(0,s.jsx)(t.code,{children:"Event Commit Execute"})," I wait for gameplay events using ",(0,s.jsx)(t.code,{children:"Wait Gameplay Event"})," ability task. Such as ",(0,s.jsx)(t.code,{children:"GameplayEventTagsCategory.AnimNotify.Queue.Main"}),". This tells us our montage has entered a prior state that is no longer cancelleable by lower priority abilities such as ",(0,s.jsx)(t.em,{children:"dodge"}),". So I call the ",(0,s.jsx)(t.code,{children:"Start Ability Queue State"})," from my Queue Task and set the new state as ",(0,s.jsx)(t.em,{children:"main"})," thus updating the priority of the playing ability."]}),"\n",(0,s.jsxs)(t.p,{children:["I use ",(0,s.jsx)(t.a,{href:"https://dev.epicgames.com/documentation/en-us/unreal-engine/animation-notifies-in-unreal-engine",children:"Anim Notifies"})," to send gameplay events to my pawn in my animation montages. This way I can precisely set the state changing points."]}),"\n",(0,s.jsxs)(t.admonition,{type:"note",children:[(0,s.jsxs)(t.p,{children:["In ",(0,s.jsx)(t.strong,{children:"network environments"})," you should change your states at the same time as the server on clients. Animation Notifies work, because the montage is being played plays both on the server and the client."]}),(0,s.jsx)(t.p,{children:"So in order to not cause desync choose events that both fires on the server and the client or use replication."})]}),"\n",(0,s.jsx)(t.hr,{}),"\n",(0,s.jsx)(t.p,{children:"That is it! After that this is just a regular gameplay ability."})]})}function u(e={}){const{wrapper:t}={...(0,a.R)(),...e.components};return t?(0,s.jsx)(t,{...e,children:(0,s.jsx)(d,{...e})}):d(e)}},99482:(e,t,n)=>{n.d(t,{A:()=>i});const i=n.p+"assets/images/T_AddAbilityToQueueNode-5df46c8c27687bc43e7fd14f995d576d.png"}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.