#ApplyRootForce Callback Question

1 messages ยท Page 1 of 1 (latest)

summer mango
#

i took it to a thread so we dont clog up the chat if thats ok

#

these are the only files from it so i guess he maybe overrode something from unreals stuff

glacial sandal
#

so it will be somewhere in RootMovementLibrary

summer mango
#

this is a search for the function

glacial sandal
#

what are all the functions available in RootMovementLibrary.h

summer mango
#

just the one without callbacks

glacial sandal
#

AsyncRootMovement.h?

summer mango
#

ahh

#

it seems to have the callback one!

#

in the display name at least

glacial sandal
#

yah thats the one

summer mango
#

oh so here is the on complete and fail nodes i presume

glacial sandal
#

so the way you use AsyncActions like this one here, is instead of just calling the function,

the function returns you an Object (AsyncAction*).
you can bind to the delegates with the object that is returned, and you can keep the Object around to cancel it or do whatever.

#

and when you are ready for it to run you call Activate on it

#

sounds complicated but it's like 3-4 steps max

#

in blueprint the Activate is called automatically for you

summer mango
#

it does cause im kinda lost again. are you saying i dont call the function in my player controller class like i did?

#

lemme review your original comment

glacial sandal
#

instead of URootMovementLibrary::ApplyRootMotionConstantForce( ... );
you do those steps in that image

summer mango
#

i put the async stuff?

glacial sandal
#

it's still calling the Synchronous function in the image

#

first step, you call it like this:
auto* AsyncAction = UAsyncRootMovement::AsyncRootMovement( ... );

summer mango
#

like so?

#

i gotta fix the parameters

glacial sandal
#

yup, once you fix the parameters first step is good

summer mango
#

alternatively can i declare an asyncaction in the header or is it better to just make a new one each time?

#

kinda a tangent

glacial sandal
#

you need to create a single async action for each time you call it, the resulting object "owns" the async call's lifetime. it acts as a lifetime scope for the async call that can exist for multiple frames

summer mango
#

ah ok. is world context gotten from the player?

glacial sandal
#

the player controller should have access to the World yeah

#

sorry, WorldContextObject should be this

#

WorldContextObject should be the UObject that is calling the AsyncAction, so it would be:
UAsyncRootMovement::AsyncRootMovement(this, ... );

summer mango
#

i think the only thing trippin me in the parameters is this noe

#

havent used it before

glacial sandal
#

that's library specific, your going to have to make a call

summer mango
#

im not sure its needed i guess i could remove it from the parameter

#

ah yeah i did that in the original function lol

#

ok cool parameters are fixed

#

so now is it making delegates in the header of the player controller?

glacial sandal
#

step 2 creating the UFUNCTIONS that will be bound to the delegates

#

yeah

#

you need to match their parameters to the Delegate type

#

so you need to look at the signature of OnComplete's and OnFail's variable types

summer mango
#

the Fmovmentevent?

glacial sandal
#

yeah ctrl+click FMovementEvent to see what parameters it needs

summer mango
#

ah here at the top

glacial sandal
#

0 parameters

#

so 0 parameter UFUNCTION to match it

summer mango
#

a unfunction in my player controller class correct?

glacial sandal
#

yup

summer mango
#

im following

glacial sandal
#

the player controller:

UFUNCTION()
void OnComplete();

UFUNCTION()
void OnFail();
#

(not UPROPERTY)

summer mango
#

ohhhh

glacial sandal
#

I like to call them ReceiveOnComplete / Receive<Blah> to distinguish it

summer mango
#

so i dont need the uprop fmovement ones?

#

damn i feel dumb lol

glacial sandal
#

no worries, we all start somewhere

summer mango
#

ive done dynamic delegates in the past with some courses but i took huge hiatus' and forgetten alot of it

glacial sandal
#

so you need to implement those functions... can just leave them empty for now,
and move on to step 3, which is simply to bind them

#

back where you created the async action

summer mango
#

and that was in my jump function correct

glacial sandal
#

โ˜‘๏ธ

summer mango
#

you bind them directly in the jump function?

glacial sandal
#

yeah

#

because it's an AsyncAction, remember the AsyncAction object get's created every time you call it

#

so it get's bound right there

#

not like other delegates that get bound in BeginPlay

summer mango
#

ahh ok yeah yet another reason i was confused since anytime i bound delegate its was in begin play

#

ah yeah lol

#

and it would be bound as an FMovmentEvent right?

glacial sandal
#

it would look like this:
AsyncAction->OnComplete.AddDynamic(this, &ThisClass::ReceiveOnComplete);

summer mango
#

oh whoops

#

hm the OnComplete is inaccessible in a private field. i guess in the async class i need to move it?

#

doesnt say its in a private field tho i as look

#

yeezus

#

that seemed to fix it

glacial sandal
#

if it's private/protected you would need to move it to public yeah

summer mango
#

i guess if its not specified its automatically private?

#

anyways i put it in a public field

glacial sandal
#

class fields are private by default if not specified yeah.

summer mango
#

ah ok makes sense then

glacial sandal
#

struct fields are public by default

#

fun c++ conventions party_manny

summer mango
#

galloping gophers batman

#

ok so now i can just put whatever code i want in the ReceivedOnComplete correct and everything is kosher?

glacial sandal
#

step 4 call Activate on the async action right below binding the delegates

glacial sandal
#

that **should **be the last step. but one thing I need to check. does the library author call SetReadyToDestroy();

summer mango
#

i dont believe so

glacial sandal
#

so in AsyncRootMovement.h ctrl+f SetReadyToDestroy has no hits?

#

AsyncRootMovement.cpp I mean not .h

summer mango
#

no hits

glacial sandal
#

that's likely a bug.
you need to call SetReadyToDestroy(); after OnComplete and OnFail is called.

summer mango
#

or the author didnt put it maybe im not sure

glacial sandal
#

otherwise async action object stay's alive as long as the GameInstance is, I.E. for the duration of the game. even after it's done being used

summer mango
#

thats in the same place in my jump?

glacial sandal
#

no you would need to modify AsyncRootMovement.cpp

summer mango
#

i assume mayhap somewhere here

glacial sandal
#

actually.

#

Cancel probably calls SetReadyToDestroy

#

can you check to see what it does

summer mango
glacial sandal
#

what does Super::Cancel do

summer mango
#

ah yep

glacial sandal
#

alright so you are good to go then

glacial sandal
summer mango
#

bruh how do i throw you like 10 bucks holy hell

glacial sandal
#

im good

summer mango
#

seriously thank you

#

i never would have figured this out

#

i always hate asking question in unreal discords cause people have a superiority complex against noobs. again thank you

summer mango
#

im actually not sure if the broadcast events complete tho. i added debug messages to the functions but nothing popped up

glacial sandal
#

show the log messages?

summer mango
#

no log actually pops up. in debug its saying there is no async actoin

#

so i breakpointed this

#

and when i step to the next it goes back and forth between the if

glacial sandal
#

sorry, I could be more specific. I mean the functions you are using to log,
in C++ you need to use
GEngine->AddOnScreenDebugMessage
or UKismetSystemLibrary::PrintString with bPrintToScreen as true

summer mango
#

oh a ue log doesnt work?

glacial sandal
#

no

summer mango
#

ah

#

its worked in the past which is why i did it

glacial sandal
#

UE_LOG works per se, but it wont add it to your screen

summer mango
#

so maybe it is calling

glacial sandal
#

it will only show up in the output log

summer mango
#

oh it didnt even show in my output log lemme try the gengine

#

hm doesnt appear to call

glacial sandal
#

also WorldContext should be this instead of GetWorld

summer mango
#

yeah i changed it cause it didnt seem to work so was just shooting in the dark

glacial sandal
#

ah np

summer mango
#

it seems like in debug it just gets to eht first line, then steps back in forth between the if call and never goes to activate

#

but if you thought i was noob my debug skills is dirt

glacial sandal
#

you are likely building Development, which confuses the debugger a bit because the optimized c++ doesn't behave as you would expect.

need to build DebugGame instead

summer mango
#

ah whoops

#

ok swapped it

#

gonna reload

#

just waiting on it to rebuild

glacial sandal
#

feel free to ping me between messages

#

wait I can set this thread to have specific notification settings actually ๐Ÿ‘€cool.

summer mango
#

ight its loaded

#

try breakpointing it again?

glacial sandal
#

I would set a breakpoint in ReceiveOnComplete, ReceiveOnFailed and UAsyncRootMovement::Cancel.
might be hitting Cancel before broadcasting for some reason

summer mango
#

seems none of those were called

glacial sandal
#

what is the callstack for Cancel

summer mango
#

smh i think its frozen

#

lol

#

shouldnt it show up if its being called?

#

i hit escape then stuff poped up

glacial sandal
#

ah there it is

#

I was confused as hell

summer mango
#

and now the editor is frozen again

#

oh maybe casue the point hit

#

normally it would pause then pop the stack

#

idk i dont know much about debugging yet

glacial sandal
#

๐Ÿค” BeginDestroy is being called from EndPlayMap...

#

when is this getting hit

#

right when you jump, or are you quitting the editor and then it gets hit?

summer mango
#

idk it didnt react til i hit escape

#

so i guess when cancelling the game?

glacial sandal
#

right

#

but when you jump nothing is getting hit

summer mango
#

right

glacial sandal
#

so I would set a breakpoint in UAsyncRootMovement::Activate

#

the AsyncAction is getting created, the delegates are bound, but the actual action itself doesn't seem to be running (otherwise it would call OnComplete or OnFail). so something may be going wrong in the initial setup.

summer mango
#

this does in fact activate as soon as i push the jump while being crouched

glacial sandal
#

but if you step through the activate code ( with F10 (Step Over) and F11 (Step Into) ) where does it go?

#

need to see the code for UAsyncRootMovement::Activate

summer mango
#

i guess on rider its f8 lol

glacial sandal
#

right... different keybind setups forgot.

#

well you need to make sure you are getting to this specific line:

summer mango
#

it goes from

#

straight to return

#

this straight to return

glacial sandal
#

so it's getting constructed correctly ๐Ÿค” what is OngoingDelay's value

summer mango
#

wait now it hit the on complete

#

maybe cause the delay timer was 0

#

i changed it to .3

glacial sandal
#

ah, I think 0 has special meaning with TimerManager

summer mango
#

ok so it hit

glacial sandal
#

nice ๐Ÿ‘

summer mango
#

so i guess not having an amount other than 0 messed with it i have no idea lol

glacial sandal
#
    /**
     * Sets a timer to call the given native function at a set interval.  If a timer is already set
     * for this handle, it will replace the current timer.
     *
     * @param InOutHandle            If the passed-in handle refers to an existing timer, it will be cleared before the new timer is added. A new handle to the new timer is returned in either case.
     * @param InObj                    Object to call the timer function on.
     * @param InTimerMethod            Method to call when timer fires.
     * @param InRate                The amount of time (in seconds) between set and firing.  If <= 0.f, clears existing timers.
     * @param InbLoop                true to keep firing at Rate intervals, false to fire only once.
     * @param InFirstDelay            The time (in seconds) for the first iteration of a looping timer. If < 0.f InRate will be used.
     */

    template< class UserClass >
    FORCEINLINE void SetTimer(FTimerHandle& InOutHandle, UserClass* InObj, typename FTimerDelegate::TMethodPtr< UserClass > InTimerMethod, float InRate, bool InbLoop = false, float InFirstDelay = -1.f)
    ```
#

"If <= 0.f, clears existing timers."

summer mango
#

ahh

#

yeah the message calls everytime now

#

me and this function

glacial sandal
#

yeah Epic has alot of foot guns and idiosyncrasies to learn about

summer mango
#

idek know how you go about knowing those kinds of things

#

i mean i guess over time but to figure them out from scratch

#

yeesh

glacial sandal
#

tribal knowledge, always ask

#

asking is not a sign of weakness, it's a sign of not being willing to give up

summer mango
#

most of the time its you suck cant you figure it out and look it up lmao

#

hopefully its the last time i bug you about this haha

glacial sandal
#

no worries

#

I'm just as new to different parts of the engine and bug other people on the discord

#

๐Ÿ˜›

summer mango
#

like im still lost on what exactly and how the async movement is doing the magic but ill leave figuring that out for later lol

glacial sandal
#

literally me just today a while ago:
"
Mk2 โ€” Today at 11:22 AM
is there a way to tell what ini category a cvar needs to be created under?
(specifically tick.AllowBatchedTicks )
"

summer mango
#

im unrealed out for the day

#

on the contrary my stubborness allowed me to finally get this straightend out. been mulling on it asking everywhere for 2 days lol

#

well and the help of a genius too ofc

glacial sandal
#

no you're breath taking

summer mango
#

lol if its one thing my adhd has granted me its the willingness to keep being stubborn about things lmao

#

and at the same time needing to ask a million questions before finally connecting the dots