But now I have a playing card that can be clicked, but I want my gameLogic class to be informed about this as well,
how do I relay that information upwards my hierarchy? My plan is, to also handle the event in my gameLogic, since here
I have pointers to all my cards, and can control all my logic from here.
And that’s my problem: I don’t understand how to handle the onClicked event on my card’s mesh, from my gamelogic class.
Edit for clarification:
I want to call AddDynamic from my gamelogic, and tell it to register clicks on the card’s mesh and call a function within my gamelogic accordingly.
My hierarchy is this:
gamemode->gamelogic->deck->card
for (Acard* a : mygamelogic->mydeck->cards) a->OnClicked.AddDynamic(this, &AMyGameLogic::cardPlayed);
...
// activates if any block is clicked
void AMyGameLogic::cardPlayed(){
//do stuff to card
}
However, if I try to pass any parameters to my “cardPlayed”, for example a “UPrimitiveComponent* ClickedComp” like inside my class.cpp,
the compiler will complain with this (instead of gamelogic its now mystatemachine, but same problem):
error C2664: 'void TBaseDynamicMulticastDelegate<FWeakObjectPtr,void,>::__Internal_AddDynamic<AMyStateMachine>(UserClass *,void (__cdecl AMyStateMachine::* )(void),const FString &)' : cannot convert argument 2 from 'void (__cdecl AMyStateMachine::* )(UPrimitiveComponent *)' to 'void (__cdecl AMyStateMachine::* )(void)'
1> with
1>
1> UserClass=AMyStateMachine
1> ]
1> Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
These are very interesting, and I guess a public reference is exactly what I need. But how do I reference my gamelogic? If I set a public pointer of Type Agamelogic, I need to include the header of it, and this leads to a circular dependency. Is there a way to define a gobal getter for my class, like in the blueprint example’s you linked, but in C++?
Sorry man my bad … I missed that you were in the C++ forum. I am not in front of my dev machine right now … so hopefully somebody else can jump in here.
Sorry for giving you Blueprint stuff in the C++ forum. 8-{
The problem remains: If I typecast I must include the gamelogic.h in my headerfiles, which won’t work, as its a circular dependency. Is there a global way to “find actor” ?
When binding delegates, you have to respect the delegate’s signature. Basically, binding a delegate is like inverting the responsibility of calling a function. Instead of AActor saying, “I was just clicked, I will now call my hardcoded Clicked function”, what happens is you have an outside observer telling AActor, “I would like you to call this when you are clicked”.
In the case of the hardcoded function, if it had any parameters like a UPrimitiveComponent*, then AActor code obviously would have to provide such this parameter in order for the function to compile and be called correctly. The same is necessary when binding a delegate – the function you are assigning to the delegate has to match the same arguments in order to be called correctly. This simple contract is what makes it possible to store and pass delegates around safely.
Now, if you look up the signature of OnClicked, here’s what you’ll find:
The signature declares no parameters and therefore the function you assign to it cannot have a parameter. Now, if you had a fixed amount of Card actors you wanted to monitor for Clicked events, this wouldn’t be a problem. You could declare a separate function for each card and bind each function to each card, like so:
Now you’re going to say, “But that’s terrible! I don’t want to declare a function for each card!”. And yes, it is a terrible way of doing it. The Clicked delegate was really meant for very simple situations with fixed actors. But don’t worry, there’s some good news as well as some bad.
The good news first: What you are attempting to do is in fact supported by the delegate system. You can add arguments at bind time which will be automatically passed to your function when the delegate is invoked. This is called a delegate payload and you can read more about it in Delegate.h if you feel so inclined.
Suppose you want this extra payload argument to be the card that was clicked. You would then change your function to accept one ACard argument, despite the fact that the signature does not contain an ACard:
void AMyGameLogic::CardPlayed(ACard* PlayedCard);
Then, you pass the card as an extra argument when binding the delegate:
The delegate will be saved with this argument and it will automatically be appended to the end of the function you’re calling.
Now, before you rush out and try to do this, the bad news. Delegate payloads are an extremely powerful tool. They are also not supported on dynamic delegates, which AActor::OnClicked is. But because they are very useful, especially in UI code (which is what you are doing, in a way), I wanted to give you a brief intro of how they work.
The bottom line, simply put, is that the AActor::OnClicked delegate simply won’t meet your needs. So let’s declare our own instead. Since you’re making your own signature, then you might as well just add whatever arguments you need right off the bat so you don’t need to deal with payloads. Feel free to amend this to your liking, but I imagine the signature you need would be along those lines:
// If you want to use the delegate in Blueprint, make it dynamic:
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam( FOnCardClickedSignature, Acard*, ClickedCard );
// If you don't need Blueprint and never will, a native delegate will work:
DECLARE_MULTICAST_DELEGATE_OneParam( FOnCardClickedSignature, Acard* );
Now, you will need to call this when your card actor is clicked. Looking through AActor code, there unfortunately isn’t an easy to override function that you could use for this in Acard. But we do have an existing OnClicked delegate, so let’s just bind to that and forward the event:
This will forward the OnClicked delegate to your more palatable OnCardClicked. The last thing to do is obviously to use it in your game logic:
for (Acard* a : mygamelogic->mydeck->cards) a->OnCardClicked.AddDynamic(this, &AMyGameLogic::cardPlayed);
(...)
void AMyGameLogic::cardPlayed(Acard* ClickedCard){
//do stuff to card
}
That should clear things up delegate-wise.
p.s.: Alternatively, note that in this lengthy explanation, I’ve only been talking about AActor::OnClicked. The delegate signature you were dealing with initially was actually that of UPrimitiveComponent::OnClicked, which you were trying to bind to AActor::OnClicked. You can probably just do this instead:
for (Acard* a : mygamelogic->mydeck->cards)
{
a->**BlockMesh**->OnClicked.AddDynamic(this, &AMyGameLogic::cardPlayed);
(...)
void AMyGameLogic::cardPlayed(UPrimitiveComponent* ClickedComp){
Acard* ClickedCard = Cast<Acard>( ClickedComp->GetOwner() );
//do stuff to card
}
This absolutely did the job! I noticed the missing BlockMesh as well, and your “PS” solution works like a charm. But I really want to thank your for going out of your way explaining the payload concept, this was absolutely phenomenal teaching. You made my day!
You’re welcome. I felt like writing out a decent reference for delegates even though I could’ve just pointed out that you were trying to register the component delegate on the actor.
(Actually, I only noticed that after writing out half of the explanation so I just finished it. ;d)