Is there a good way to do this right now? One of the best things about event dispatchers on Actors is that you can hook up to them very easily in the level blueprint (see the top part of the attached image).
I’d like to use a component based system instead though because of the flexibilities that that affords, but one of the main things that I’d want to do is hook into their event dispatchers from the level blueprint. Right now the only way I seem to be able to do this is very long-winded and will get extremely messy when we’re talking about dozens if not hundreds of Actors in the level. You also need a custom event with unique name for each. See the bottom part of the attached image for example of just one single binding.
Is there any more elegant way to hook into a component dispatcher than this?
My ‘best practice’ is to put nothing into the level blueprint. You should be able to create a new, empty level and be able to drop actors into it without having to mess around with the level blueprint. The only thing which should go into a level blueprint is stuff which is level specific.
If I have an actor which needs to do something related to another actor, I use its “Begin Play” method and create a subscription to listen for broadcast events from the other actor. I use the “Bind” node to create a delegate and wire that up to a function which responds to it.
Everything in the game we’re making is level specific though It’s an adventure game where pretty much everything you come across to interact with is unique and exists solely for its purpose within the level. There’s pretty much zero functionality in objects themselves, it’s all special case hooks to things that uniquely play out in the scene.
I’m guessing you have found another way to do things by now but I was just confronted to the same problem and here is a pretty good bypass suggested to me by Rudy from Epic’s staff :
Instead of using a delegate you spawn a custom event in the blueprint and use PlayerController::ConsoleCommand(“ce evetname”) to call it. I don’t know if you have a way to easily handle parameters and/or return values as i did not need any for now but I guess you can find out easily by investigating this “ce” command.
Here is what my code looks like :
in your component’s header you add :
void Interact(class APlayerController* Player) const;
UFUNCTION(CallInEditor, Category = Interaction)
void GenerateEvent();
FString UVampireInteraction::GetEventName() const // handle your name generation as you see fit, here's mine
{
return FString::Printf(TEXT("%s_%s_Interact"), *GetOwner()->GetName(), *GetName());
}
and in the cpp:
#include "Kismet2/KismetEditorUtilities.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "EdGraphSchema_K2_Actions.h"
#include "K2Node_CustomEvent.h"
#include "Gameframework/PlayerController.h"
#include "Engine/LevelScriptBlueprint.h"
void UMyInteractionComponent::Interact(APlayerController* Player) const
{
Player->ConsoleCommand("ce " + GetEventName());
}
void UMyInteractionComponent::GenerateEvent()
{
UBlueprint* Blueprint = GetOwner()->GetLevel()->GetLevelScriptBlueprint();
FString EventName = GetEventName();
UEdGraph* TargetGraph = Blueprint->GetLastEditedUberGraph();
if (TargetGraph != nullptr)
{
TArray<UK2Node_CustomEvent*> EventNodes;
FBlueprintEditorUtils::GetAllNodesOfClass(Blueprint, EventNodes);
for (auto NodeIter = EventNodes.CreateIterator(); NodeIter; ++NodeIter)
{
UK2Node_CustomEvent* BoundEvent = *NodeIter;
if (BoundEvent->CustomFunctionName.Compare(*EventName) == 0)
return;
}
// Figure out a decent place to stick the node
const FVector2D NewNodePos = TargetGraph->GetGoodPlaceForNewNode();
// Create a new event node
UK2Node_CustomEvent* EventNode = FEdGraphSchemaAction_K2NewNode::SpawnNode<UK2Node_CustomEvent>(
TargetGraph,
NewNodePos,
EK2NewNodeFlags::SelectNewNode
);
if (EventNode->Rename(*EventName, EventNode->GetOuter(), REN_Test))
{
EventNode->CustomFunctionName = *EventName;
EventNode->Rename(*EventName, EventNode->GetOuter(), REN_ForceNoResetLoaders | REN_DontCreateRedirectors);
}
else
{
// You might want to handle the error here
}
// Finally, bring up kismet and jump to the new node
if (EventNode != nullptr)
{
FKismetEditorUtilities::BringKismetToFocusAttentionOnObject(EventNode);
}
}
}