Combo actions finally have Blueprint event nodes for each class.

This commit is contained in:
Jamie Greunbaum 2023-09-17 01:44:27 -04:00
parent 5326c81528
commit b24d7af1a3
7 changed files with 141 additions and 122 deletions

View File

@ -52,10 +52,11 @@ void UComboManagerComponent::ActivateComboInput(const UComboInputAsset *Input)
/********** END DEBUG **********/ /********** END DEBUG **********/
// If we received an offset input, perform the offset here and then leave. // If we received an offset input, perform the offset here and then leave.
if (Input == this->OffsetInput) if (Input == this->OffsetMap.ComboInput)
{ {
this->ActiveNode = this->PreviousNode; this->ActiveNode = this->PreviousNode;
this->GetWorld()->GetTimerManager().ClearTimer(this->FinishTransitionTimer); this->GetWorld()->GetTimerManager().ClearTimer(this->FinishTransitionTimer);
this->BroadcastDelegates(this->OffsetMap.ComboAction, EComboActionTriggerEvent::Activated);
UE_LOG(LogComboManagerComponent, Verbose, TEXT("Combo has been offset by %s"), *Input->ComboInputName.ToString()); UE_LOG(LogComboManagerComponent, Verbose, TEXT("Combo has been offset by %s"), *Input->ComboInputName.ToString());
return; return;
} }
@ -69,12 +70,7 @@ void UComboManagerComponent::ActivateComboInput(const UComboInputAsset *Input)
if (ActionData.ComboAction) if (ActionData.ComboAction)
{ {
this->BeginNodeTransition(ActionData.NextNode); this->BeginNodeTransition(ActionData.NextNode);
this->BroadcastDelegates(ActionData.ComboAction, EComboActionTriggerEvent::Activated);
for (const FComboActionHandlerDynamicSignature &Binding : this->ComboActionEventBindings)
{
Binding.Execute(/*EComboActionTriggerEvent::Activated*/);
}
UE_LOG(LogComboManagerComponent, Verbose, TEXT("%s activated"), *ActionData.ComboAction->ActionName.ToString()); UE_LOG(LogComboManagerComponent, Verbose, TEXT("%s activated"), *ActionData.ComboAction->ActionName.ToString());
} }
} }
@ -108,3 +104,14 @@ void UComboManagerComponent::FinishTransition()
{ {
this->PreviousNode = this->ActiveNode; this->PreviousNode = this->ActiveNode;
} }
void UComboManagerComponent::BroadcastDelegates(const UComboAction *ComboAction, const EComboActionTriggerEvent &TriggerEvent)
{
const FComboDelegateKey Key(ComboAction, TriggerEvent);
if (const FComboActionHandlerDynamicSignature *Binding = this->ComboActionEventBindings.Find(Key.Key))
{
Binding->Execute(ComboAction, TriggerEvent);
}
this->OnComboAction.Broadcast(ComboAction, (TriggerEvent == EComboActionTriggerEvent::Activated) ? true : false);
}

View File

@ -26,11 +26,7 @@ void UComboActionDelegateBinding::BindDynamicDelegates(UObject *InInstance) cons
{ {
for (const FBlueprintComboActionBinding &Binding : this->ComboActionDelegateBindings) for (const FBlueprintComboActionBinding &Binding : this->ComboActionDelegateBindings)
{ {
// Get the function we want to bind ComboManager->BindAction(PlayerController, Binding.FunctionNameToBind, Binding.ComboAction, Binding.TriggerEvent);
if (UFunction *FunctionToBind = InInstance->GetClass()->FindFunctionByName(Binding.FunctionNameToBind))
{
ComboManager->BindAction(InInstance, Binding.FunctionNameToBind);
}
} }
} }
} }

View File

@ -26,48 +26,17 @@
#define LOCTEXT_NAMESPACE "K2Node_ComboAction" #define LOCTEXT_NAMESPACE "K2Node_ComboAction"
#define COMBO_ACTION_ACTIVATED_PIN_NAME TEXT("Activated")
#define COMBO_ACTION_RELEASED_PIN_NAME TEXT("Released")
#define COMBO_ACTION_OBJECT_PIN_NAME TEXT("ComboAction")
namespace UE::Input
{
static bool bComboActionShouldWarnOnUnsupportedInputPin = false;
static FAutoConsoleVariableRef CVarShouldWarnOnUnsupportedInputPin(
TEXT("ComboAction.bp.bComboActionShouldWarnOnUnsupportedInputPin"),
bComboActionShouldWarnOnUnsupportedInputPin,
TEXT("Should the Combo Action event node throw a warning if an \"Unsupported\" pin has a connection?"),
ECVF_Default);
}
UK2Node_ComboAction::UK2Node_ComboAction(const FObjectInitializer &ObjectInitializer)
: Super(ObjectInitializer)
{
}
void ForEachEventPinName(TFunctionRef<void(EComboActionTriggerEvent Event, FName PinName)> PinLambda)
{
UEnum *EventEnum = StaticEnum<EComboActionTriggerEvent>();
for (int32 i = 0; i < EventEnum->NumEnums() - 1; ++i)
{
if (!EventEnum->HasMetaData(TEXT("Hidden"), i))
{
PinLambda(EComboActionTriggerEvent(EventEnum->GetValueByIndex(i)), *EventEnum->GetNameStringByIndex(i));
}
}
}
void UK2Node_ComboAction::AllocateDefaultPins() void UK2Node_ComboAction::AllocateDefaultPins()
{ {
this->PreloadObject((UObject*)this->ComboAction); CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, COMBO_ACTION_ACTIVATED_PIN_NAME);
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, COMBO_ACTION_RELEASED_PIN_NAME);
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Object, UComboAction::StaticClass(), COMBO_ACTION_OBJECT_PIN_NAME);
ForEachEventPinName([this](EComboActionTriggerEvent Event, FName PinName)
{
static const UEnum *EventEnum = StaticEnum<EComboActionTriggerEvent>();
UEdGraphPin *NewPin = CreatePin(EEdGraphPinDirection::EGPD_Output, UEdGraphSchema_K2::PC_Exec, PinName);
NewPin->PinToolTip = EventEnum->GetToolTipTextByIndex(EventEnum->GetIndexByValue(static_cast<uint8>(Event))).ToString();
});
this->AdvancedPinDisplay = ENodeAdvancedPins::NoPins;
Super::AllocateDefaultPins(); Super::AllocateDefaultPins();
} }
@ -83,7 +52,6 @@ FName UK2Node_ComboAction::GetActionName() const
FText UK2Node_ComboAction::GetNodeTitle(ENodeTitleType::Type TitleType) const FText UK2Node_ComboAction::GetNodeTitle(ENodeTitleType::Type TitleType) const
{ {
// TODO: Is Using InputAction->GetFName okay here? Full Asset path would be better for disambiguation.
if (TitleType == ENodeTitleType::MenuTitle) if (TitleType == ENodeTitleType::MenuTitle)
{ {
return FText::FromName(this->GetActionName()); return FText::FromName(this->GetActionName());
@ -106,23 +74,18 @@ FText UK2Node_ComboAction::GetTooltipText() const
if (CachedTooltip.IsOutOfDate(this)) if (CachedTooltip.IsOutOfDate(this))
{ {
// FText::Format() is slow, so we cache this to save on performance // FText::Format() is slow, so we cache this to save on performance
FString ActionPath = this->ComboAction ? this->ComboAction->GetFullName() : TEXT(""); CachedTooltip.SetCachedText(FText::Format(NSLOCTEXT("K2Node", "ComboAction_Tooltip", "Event for when the combo action {0} is pressed or released."), FText::FromName(this->ComboAction->GetFName())), this);
CachedTooltip.SetCachedText(
FText::Format(
LOCTEXT("ComboAction_Tooltip", "Event for when '{0}' triggers.\n\nNOTE: This is not guaranteed to fire every frame, only when the Action is triggered and the current Input Mode includes 'Game'."),
FText::FromString(ActionPath)),
this);
} }
return CachedTooltip; return CachedTooltip;
} }
FSlateIcon UK2Node_ComboAction::GetIconAndTint(FLinearColor& OutColor) const FSlateIcon UK2Node_ComboAction::GetIconAndTint(FLinearColor &OutColor) const
{ {
static FSlateIcon Icon(FAppStyle::GetAppStyleSetName(), "GraphEditor.Event_16x"); static FSlateIcon Icon(FAppStyle::GetAppStyleSetName(), "GraphEditor.Event_16x");
return Icon; return Icon;
} }
bool UK2Node_ComboAction::IsCompatibleWithGraph(UEdGraph const* Graph) const bool UK2Node_ComboAction::IsCompatibleWithGraph(UEdGraph const *Graph) const
{ {
// This node expands into event nodes and must be placed in a Ubergraph // This node expands into event nodes and must be placed in a Ubergraph
EGraphType const GraphType = Graph->GetSchema()->GetGraphType(Graph); EGraphType const GraphType = Graph->GetSchema()->GetGraphType(Graph);
@ -130,9 +93,9 @@ bool UK2Node_ComboAction::IsCompatibleWithGraph(UEdGraph const* Graph) const
if (bIsCompatible) if (bIsCompatible)
{ {
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraph(Graph); UBlueprint *Blueprint = FBlueprintEditorUtils::FindBlueprintForGraph(Graph);
UEdGraphSchema_K2 const* K2Schema = Cast<UEdGraphSchema_K2>(Graph->GetSchema()); UEdGraphSchema_K2 const *K2Schema = Cast<UEdGraphSchema_K2>(Graph->GetSchema());
bool const bIsConstructionScript = (K2Schema != nullptr) ? UEdGraphSchema_K2::IsConstructionScript(Graph) : false; bool const bIsConstructionScript = (K2Schema != nullptr) ? UEdGraphSchema_K2::IsConstructionScript(Graph) : false;
bIsCompatible = (Blueprint != nullptr) && Blueprint->SupportsInputEvents() && !bIsConstructionScript && Super::IsCompatibleWithGraph(Graph); bIsCompatible = (Blueprint != nullptr) && Blueprint->SupportsInputEvents() && !bIsConstructionScript && Super::IsCompatibleWithGraph(Graph);
@ -140,9 +103,9 @@ bool UK2Node_ComboAction::IsCompatibleWithGraph(UEdGraph const* Graph) const
return bIsCompatible; return bIsCompatible;
} }
UObject*UK2Node_ComboAction::GetJumpTargetForDoubleClick() const UObject *UK2Node_ComboAction::GetJumpTargetForDoubleClick() const
{ {
return const_cast<UObject*>(Cast<UObject>(this->ComboAction)); return const_cast<UObject *>(Cast<UObject>(this->ComboAction));
} }
void UK2Node_ComboAction::JumpToDefinition() const void UK2Node_ComboAction::JumpToDefinition() const
@ -165,79 +128,101 @@ void UK2Node_ComboAction::ExpandNode(FKismetCompilerContext &CompilerContext, UE
{ {
Super::ExpandNode(CompilerContext, SourceGraph); Super::ExpandNode(CompilerContext, SourceGraph);
if(!this->ComboAction) UEdGraphPin *ComboActionActivatedPin = this->FindPin(COMBO_ACTION_ACTIVATED_PIN_NAME);
UEdGraphPin *ComboActionReleasedPin = this->FindPin(COMBO_ACTION_RELEASED_PIN_NAME);
struct EventPinData
{ {
static const FText InvalidActionWarning = LOCTEXT("InvalidComboActionDuringExpansion", "@@ does not have a valid Combo Action asset!!"); EventPinData(UEdGraphPin *InPin, EComboActionTriggerEvent InEvent) { this->Pin = InPin; this->TriggerEvent = InEvent; }
CompilerContext.MessageLog.Warning(*InvalidActionWarning.ToString(), this); UEdGraphPin *GetPin() const { return this->Pin; }
return; const EComboActionTriggerEvent &GetTriggerEvent() { return this->TriggerEvent; }
} private:
UEdGraphPin *Pin;
// Establish active pins
struct ActivePinData
{
ActivePinData(UEdGraphPin* InPin, EComboActionTriggerEvent InTriggerEvent) : Pin(InPin), TriggerEvent(InTriggerEvent) {}
UEdGraphPin* Pin;
EComboActionTriggerEvent TriggerEvent; EComboActionTriggerEvent TriggerEvent;
}; };
TArray<ActivePinData> ActivePins; TArray<EventPinData> ActivePins;
ForEachEventPinName([this, &ActivePins, &CompilerContext](EComboActionTriggerEvent Event, FName PinName) if ((ComboActionActivatedPin != nullptr) && (ComboActionActivatedPin->LinkedTo.Num() > 0))
{ {
UEdGraphPin *ComboActionPin = FindPin(PinName, EEdGraphPinDirection::EGPD_Output); ActivePins.Add(EventPinData(ComboActionActivatedPin, EComboActionTriggerEvent::Activated));
if (ComboActionPin && ComboActionPin->LinkedTo.Num() > 0) }
{ if ((ComboActionReleasedPin != nullptr) && (ComboActionReleasedPin->LinkedTo.Num() > 0))
ActivePins.Add(ActivePinData(ComboActionPin, Event));
}
});
if (ActivePins.Num() == 0)
{ {
return; ActivePins.Add(EventPinData(ComboActionReleasedPin, EComboActionTriggerEvent::Released));
} }
// Bind all active pins to their action delegate
const UEdGraphSchema_K2 *Schema = CompilerContext.GetSchema(); const UEdGraphSchema_K2 *Schema = CompilerContext.GetSchema();
auto CreateComboActionEvent = [this, &CompilerContext, &SourceGraph](UEdGraphPin *Pin, EComboActionTriggerEvent TriggerEvent) -> UK2Node_ComboActionEvent * // If more than one is linked we have to do more complicated behaviors
{ //if (ActivePins.Num() > 1)
if (!this->ComboAction) //{
// Create a temporary variable to copy Key in to
UK2Node_TemporaryVariable *ComboActionObjectVar = CompilerContext.SpawnIntermediateNode<UK2Node_TemporaryVariable>(this, SourceGraph);
ComboActionObjectVar->VariableType.PinCategory = UEdGraphSchema_K2::PC_Object;
ComboActionObjectVar->VariableType.PinSubCategoryObject = UComboAction::StaticClass();
ComboActionObjectVar->AllocateDefaultPins();
for (auto PinIt = ActivePins.CreateIterator(); PinIt; ++PinIt)
{ {
return nullptr; UEdGraphPin *EachPin = (*PinIt).GetPin();
// Create the combo action event
UK2Node_ComboActionEvent *ComboActionEvent = CompilerContext.SpawnIntermediateEventNode<UK2Node_ComboActionEvent>(this, EachPin, SourceGraph);
ComboActionEvent->CustomFunctionName = FName(*FString::Printf(TEXT("ComboActionEvent_%s_%s"), *this->GetActionName().ToString(), *ComboActionEvent->GetName()));
ComboActionEvent->ComboAction = this->ComboAction;
ComboActionEvent->TriggerEvent = (*PinIt).GetTriggerEvent();
ComboActionEvent->EventReference.SetExternalDelegateMember(FName(TEXT("ComboActionHandlerDynamicSignature__DelegateSignature")));
ComboActionEvent->bInternalEvent = true;
ComboActionEvent->AllocateDefaultPins();
// Create assignment nodes to assign the key
UK2Node_AssignmentStatement *ComboActionObjectInitialize = CompilerContext.SpawnIntermediateNode<UK2Node_AssignmentStatement>(this, SourceGraph);
ComboActionObjectInitialize->AllocateDefaultPins();
Schema->TryCreateConnection(ComboActionObjectVar->GetVariablePin(), ComboActionObjectInitialize->GetVariablePin());
Schema->TryCreateConnection(ComboActionObjectInitialize->GetValuePin(), ComboActionEvent->FindPinChecked(COMBO_ACTION_OBJECT_PIN_NAME));
// Connect the events to the assign key nodes
Schema->TryCreateConnection(Schema->FindExecutionPin(*ComboActionEvent, EGPD_Output), ComboActionObjectInitialize->GetExecPin());
// Move the original event connections to the then pin of the key assign
CompilerContext.MovePinLinksToIntermediate(*EachPin, *ComboActionObjectInitialize->GetThenPin());
// Move the original event variable connections to the intermediate nodes
CompilerContext.MovePinLinksToIntermediate(*this->FindPin(COMBO_ACTION_OBJECT_PIN_NAME), *ComboActionObjectVar->GetVariablePin());
} }
//}
//else if (ActivePins.Num() == 1)
//{
// UEdGraphPin *ComboActionPin = ActivePins[0].GetPin();
// EComboActionTriggerEvent ComboActionTriggerEvent = ActivePins[0].GetTriggerEvent();
UK2Node_ComboActionEvent *ComboActionEvent = CompilerContext.SpawnIntermediateEventNode<UK2Node_ComboActionEvent>(this, Pin, SourceGraph); // if (ComboActionPin->LinkedTo.Num() > 0)
ComboActionEvent->CustomFunctionName = FName(*FString::Printf(TEXT("ComboActionEvent_%s_%s"), *this->ComboAction->GetName(), *ComboActionEvent->GetName())); // {
ComboActionEvent->ComboAction = this->ComboAction; // UK2Node_ComboActionEvent *ComboActionEvent = CompilerContext.SpawnIntermediateEventNode<UK2Node_ComboActionEvent>(this, ComboActionPin, SourceGraph);
ComboActionEvent->TriggerEvent = TriggerEvent; // ComboActionEvent->CustomFunctionName = FName(*FString::Printf(TEXT("ComboActionEvent_%s_%s"), *this->GetActionName().ToString(), *ComboActionEvent->GetName()));
ComboActionEvent->EventReference.SetExternalDelegateMember(FName(TEXT("ComboActionHandlerDynamicSignature__DelegateSignature"))); // ComboActionEvent->ComboAction = this->ComboAction;
ComboActionEvent->AllocateDefaultPins(); // ComboActionEvent->TriggerEvent = ComboActionTriggerEvent;
return ComboActionEvent; // ComboActionEvent->EventReference.SetExternalDelegateMember(FName(TEXT("ComboActionHandlerDynamicSignature__DelegateSignature")));
}; // ComboActionEvent->bInternalEvent = true;
// ComboActionEvent->AllocateDefaultPins();
for (ActivePinData &PinData : ActivePins) // CompilerContext.MovePinLinksToIntermediate(*ComboActionPin, *Schema->FindExecutionPin(*ComboActionEvent, EGPD_Output));
{ // CompilerContext.MovePinLinksToIntermediate(*this->FindPin(COMBO_ACTION_OBJECT_PIN_NAME), *ComboActionEvent->FindPin(COMBO_ACTION_OBJECT_PIN_NAME));
UEdGraphPin *EachPin = PinData.Pin; // }
UK2Node_ComboActionEvent *ComboActionEvent = CreateComboActionEvent(EachPin, PinData.TriggerEvent); //}
if (!ComboActionEvent)
{
continue;
}
}
} }
void UK2Node_ComboAction::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const void UK2Node_ComboAction::GetMenuActions(FBlueprintActionDatabaseRegistrar &ActionRegistrar) const
{ {
auto CustomizeComboActionNodeLambda = [](UEdGraphNode *NewNode, bool bIsTemplateNode, TWeakObjectPtr<const UComboAction> Action) auto CustomizeComboActionNodeLambda = [](UEdGraphNode *NewNode, bool bIsTemplateNode, TWeakObjectPtr<const UComboAction> Action)
{ {
UK2Node_ComboAction* ComboActionNode = CastChecked<UK2Node_ComboAction>(NewNode); UK2Node_ComboAction *ComboActionNode = CastChecked<UK2Node_ComboAction>(NewNode);
ComboActionNode->ComboAction = Action.Get(); ComboActionNode->ComboAction = Action.Get();
}; };
// Do a first time registration using the node's class to pull in all existing actions // Do a first time registration using the node's class to pull in all existing actions
if (ActionRegistrar.IsOpenForRegistration(GetClass())) if (ActionRegistrar.IsOpenForRegistration(GetClass()))
{ {
IAssetRegistry& AssetRegistry = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry")).Get(); IAssetRegistry &AssetRegistry = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry")).Get();
static bool bRegisterOnce = true; static bool bRegisterOnce = true;
if (bRegisterOnce) if (bRegisterOnce)
@ -245,15 +230,18 @@ void UK2Node_ComboAction::GetMenuActions(FBlueprintActionDatabaseRegistrar& Acti
bRegisterOnce = false; bRegisterOnce = false;
if (AssetRegistry.IsLoadingAssets()) if (AssetRegistry.IsLoadingAssets())
{ {
AssetRegistry.OnFilesLoaded().AddLambda([]() { FBlueprintActionDatabase::Get().RefreshClassActions(StaticClass()); }); AssetRegistry.OnFilesLoaded().AddLambda([]()
{
FBlueprintActionDatabase::Get().RefreshClassActions(StaticClass());
});
} }
} }
TArray<FAssetData> ActionAssets; TArray<FAssetData> ActionAssets;
AssetRegistry.GetAssetsByClass(UComboAction::StaticClass()->GetClassPathName(), ActionAssets, true); AssetRegistry.GetAssetsByClass(UComboAction::StaticClass()->GetClassPathName(), ActionAssets, true);
for (const FAssetData& ActionAsset : ActionAssets) for (const FAssetData &ActionAsset : ActionAssets)
{ {
UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass()); UBlueprintNodeSpawner *NodeSpawner = UBlueprintNodeSpawner::Create(GetClass());
check(NodeSpawner != nullptr); check(NodeSpawner != nullptr);
if (FPackageName::GetPackageMountPoint(ActionAsset.PackageName.ToString()) != NAME_None) if (FPackageName::GetPackageMountPoint(ActionAsset.PackageName.ToString()) != NAME_None)
@ -269,7 +257,7 @@ void UK2Node_ComboAction::GetMenuActions(FBlueprintActionDatabaseRegistrar& Acti
else if (const UComboAction *Action = Cast<const UComboAction>(ActionRegistrar.GetActionKeyFilter())) else if (const UComboAction *Action = Cast<const UComboAction>(ActionRegistrar.GetActionKeyFilter()))
{ {
// If this is a specific UComboAction asset update it. // If this is a specific UComboAction asset update it.
UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass()); UBlueprintNodeSpawner *NodeSpawner = UBlueprintNodeSpawner::Create(GetClass());
check(NodeSpawner != nullptr); check(NodeSpawner != nullptr);
NodeSpawner->CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateStatic(CustomizeComboActionNodeLambda, TWeakObjectPtr<const UComboAction>(Action)); NodeSpawner->CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateStatic(CustomizeComboActionNodeLambda, TWeakObjectPtr<const UComboAction>(Action));
@ -296,7 +284,7 @@ FBlueprintNodeSignature UK2Node_ComboAction::GetSignature() const
return NodeSignature; return NodeSignature;
} }
TSharedPtr<FEdGraphSchemaAction> UK2Node_ComboAction::GetEventNodeAction(const FText& ActionCategory) TSharedPtr<FEdGraphSchemaAction> UK2Node_ComboAction::GetEventNodeAction(const FText &ActionCategory)
{ {
// TODO: Custom EdGraphSchemaAction required? // TODO: Custom EdGraphSchemaAction required?
TSharedPtr<FEdGraphSchemaAction_K2InputAction> EventNodeAction = MakeShareable(new FEdGraphSchemaAction_K2InputAction(ActionCategory, GetNodeTitle(ENodeTitleType::EditableTitle), GetTooltipText(), 0)); TSharedPtr<FEdGraphSchemaAction_K2InputAction> EventNodeAction = MakeShareable(new FEdGraphSchemaAction_K2InputAction(ActionCategory, GetNodeTitle(ENodeTitleType::EditableTitle), GetTooltipText(), 0));
@ -305,4 +293,3 @@ TSharedPtr<FEdGraphSchemaAction> UK2Node_ComboAction::GetEventNodeAction(const F
} }
#undef LOCTEXT_NAMESPACE #undef LOCTEXT_NAMESPACE

View File

@ -26,6 +26,7 @@ void UK2Node_ComboActionEvent::RegisterDynamicBinding(UDynamicBlueprintBinding *
FBlueprintComboActionBinding Binding; FBlueprintComboActionBinding Binding;
Binding.FunctionNameToBind = this->CustomFunctionName; Binding.FunctionNameToBind = this->CustomFunctionName;
Binding.ComboAction = this->ComboAction;
Binding.TriggerEvent = this->TriggerEvent; Binding.TriggerEvent = this->TriggerEvent;
ComboActionBindingObject->ComboActionDelegateBindings.Add(Binding); ComboActionBindingObject->ComboActionDelegateBindings.Add(Binding);

View File

@ -12,9 +12,29 @@
DECLARE_LOG_CATEGORY_EXTERN(LogComboManagerComponent, Log, All); DECLARE_LOG_CATEGORY_EXTERN(LogComboManagerComponent, Log, All);
DECLARE_DYNAMIC_DELEGATE/*_OneParam*/(FComboActionHandlerDynamicSignature/*, EComboActionTriggerEvent, TriggerEvent*/); DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FComboActionHandlerDelegate, const class UComboAction *, ComboAction, const bool, Activated);
DECLARE_DYNAMIC_DELEGATE_TwoParams(FComboActionHandlerDynamicSignature, const class UComboAction *, ComboAction, const EComboActionTriggerEvent &, TriggerEvent);
struct COMBOINPUT_API FComboDelegateKey
{
public:
FComboDelegateKey(const class UComboAction *ComboAction, const EComboActionTriggerEvent &TriggerEvent)
{
this->Key = FName(ComboAction->GetName() + TEXT("_") + StaticEnum<EComboActionTriggerEvent>()->GetDisplayNameTextByIndex((uint8)TriggerEvent).ToString());
}
FName Key;
};
USTRUCT(BlueprintType)
struct COMBOINPUT_API FComboOffsetMap
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadOnly, EditAnywhere) TObjectPtr<const class UComboInputAsset> ComboInput;
UPROPERTY(BlueprintReadOnly, EditAnywhere) TObjectPtr<const class UComboAction> ComboAction;
};
UCLASS(BlueprintType, ClassGroup=(Input), meta=(BlueprintSpawnableComponent)) UCLASS(BlueprintType, ClassGroup=(Input), meta=(BlueprintSpawnableComponent))
class COMBOINPUT_API UComboManagerComponent : public UActorComponent class COMBOINPUT_API UComboManagerComponent : public UActorComponent
{ {
@ -27,11 +47,12 @@ public:
UFUNCTION(BlueprintCallable) UFUNCTION(BlueprintCallable)
void ActivateComboInput(const class UComboInputAsset *Input); void ActivateComboInput(const class UComboInputAsset *Input);
void BindAction(UObject *ObjectToBindTo, FName FunctionName) void BindAction(UObject *ObjectToBindTo, FName FunctionName, const class UComboAction *ComboAction, const EComboActionTriggerEvent &TriggerEvent)
{ {
FComboActionHandlerDynamicSignature Delegate; FComboActionHandlerDynamicSignature Delegate;
Delegate.BindUFunction(ObjectToBindTo, FunctionName); Delegate.BindUFunction(ObjectToBindTo, FunctionName);
this->ComboActionEventBindings.Add(MoveTemp(Delegate)); const FComboDelegateKey Key(ComboAction, TriggerEvent);
this->ComboActionEventBindings.Emplace(Key.Key, MoveTemp(Delegate));
} }
protected: protected:
@ -41,23 +62,29 @@ protected:
// This input will be recognised as an offset input, which cancels a node transition // This input will be recognised as an offset input, which cancels a node transition
// if activated early enough in the transition. This locks the current place in the // if activated early enough in the transition. This locks the current place in the
// combo and allows this combo string to be continued after this input has executed. // combo and allows this combo string to be continued after this input has executed.
// The associated combo action will then be activated.
UPROPERTY(BlueprintReadOnly, EditDefaultsOnly) UPROPERTY(BlueprintReadOnly, EditDefaultsOnly)
TObjectPtr<const class UComboInputAsset> OffsetInput; FComboOffsetMap OffsetMap;
UPROPERTY(BlueprintReadOnly, EditDefaultsOnly) UPROPERTY(BlueprintReadOnly, EditDefaultsOnly)
TMap<TObjectPtr<const class UComboInputAsset>, float> DEBUG__UnlockTimers; TMap<TObjectPtr<const class UComboInputAsset>, float> DEBUG__UnlockTimers;
UPROPERTY(BlueprintReadWrite, BlueprintAssignable)
FComboActionHandlerDelegate OnComboAction;
private: private:
void BeginNodeTransition(const class UComboSequenceNode *NextNode); void BeginNodeTransition(const class UComboSequenceNode *NextNode);
void FinishTransition(); void FinishTransition();
void BroadcastDelegates(const class UComboAction *ComboAction, const EComboActionTriggerEvent &TriggerEvent);
void DEBUG__UnlockAction(TObjectPtr<const class UComboInputAsset> Unlock); void DEBUG__UnlockAction(TObjectPtr<const class UComboInputAsset> Unlock);
void DEBUG__ResetCombo(); void DEBUG__ResetCombo();
const class UComboSequenceNode *ActiveNode = nullptr; const class UComboSequenceNode *ActiveNode = nullptr;
const class UComboSequenceNode *PreviousNode = nullptr; const class UComboSequenceNode *PreviousNode = nullptr;
TArray<FComboActionHandlerDynamicSignature> ComboActionEventBindings; TMap<FName, FComboActionHandlerDynamicSignature> ComboActionEventBindings;
TObjectPtr<class UInputBufferComponent> AttachedInputBuffer; TObjectPtr<class UInputBufferComponent> AttachedInputBuffer;

View File

@ -15,6 +15,7 @@ struct COMBOINPUT_API FBlueprintComboActionBinding
public: public:
UPROPERTY() FName FunctionNameToBind = NAME_None; UPROPERTY() FName FunctionNameToBind = NAME_None;
UPROPERTY() TObjectPtr<const class UComboAction> ComboAction;
UPROPERTY() EComboActionTriggerEvent TriggerEvent = EComboActionTriggerEvent::None; UPROPERTY() EComboActionTriggerEvent TriggerEvent = EComboActionTriggerEvent::None;
}; };

View File

@ -24,7 +24,7 @@ class COMBOINPUT_API UK2Node_ComboAction : public UK2Node, public IK2Node_EventN
GENERATED_BODY() GENERATED_BODY()
public: public:
UK2Node_ComboAction(const FObjectInitializer &ObjectInitializer); UK2Node_ComboAction(const FObjectInitializer &ObjectInitializer) : Super(ObjectInitializer){}
UPROPERTY() TObjectPtr<const class UComboAction> ComboAction; UPROPERTY() TObjectPtr<const class UComboAction> ComboAction;