Download

Override Actor Component functions in Owning Actor?

I’m building a series of actor components that are designed to act like ‘Modules’ which are supposed to interact with and control each other - but in a scriptable fashion. An Actor class implements a bunch of these modules and their behaviour needs to be tied together in the actors node graph.

The modules themselves have some native code that defines some basic behaviour and in many cases events are fired off. These events can be created in the actor blueprint and talk to other components just fine. Unfortunately however, there doesn’t seem to be a way to do this for Native/Implementable functions. I’m trying to find a way to ‘Override’ a BlueprintNativeEvent UFUNCTION that belongs to an Actor Component, in the owning Actor’s Blueprint Class. So far - it seems as though there is no native way to achieve this.

UMG Property Bindings seem to exist to get around this problem, but that system looks pretty complex and doesn’t look very portable either so porting that to my special ‘module’ actor components looks like an uphill battle.

My other idea is to try and hook into the Blueprint Editor directly, and create an extension which looks for all BlueprintNativeEvents on the Actor Components of an Actor, and create the override function in the Actor’s Event Graph Blueprint. I’m not sure if the latter is even possible or if that will even work as expected, but that’s the end result I’m looking for.

TL;DR - What can I do to allow an Actor Blueprint to create overridden functions for it’s components, so that my modules work together, or is there some UPROPERTY-type that allows me to do things like this?

Blueprint Interface’s NativeEvents can have default function body in C++ and can be overriden on both Component and Actor implementing that Interface natively.

They can but the point here is I don’t want the actor or other components to necessarily have to know about all the functionality of other components - so interfaces are out of the question there.

Think of it like Particle Modules or Widgets - I want them to be able to interact with and talk to one another, without knowing exactly what each is doing. E.g, an ‘Ammo’ module and a ‘Firing’ module. The ‘Firing’ module would override ‘CanFire()’ in the Actor Blueprint, and check if the ‘Ammo’ module has enough ammo.

In the same way, a Widget can be added to a Widget Blueprint, and it’s properties can be adjusted in the main Widget Blueprint. You don’t have to create a new sub-class of a UCanvasPanel when you want to change the way it’s visibility is controlled.

I’ve been trying to extend the Blueprint Editor, with a drop-down menu of all overrideable functions in the actors’ components. Getting the list of those functions seems to be possible with some trickery, and under the hood it looks as though native functions are just re-routed to BP ones and serialized - but so far I’ve not been able to implement them since the Blueprint Editor is a pain in the *** to extend.

This is how I’m gathering the functions so far.



void FHT_ExtendedOverrideToolbar::GetModuleFunctions(const UBlueprint* Blueprint, TMap<UObjectProperty*, TArray<TSharedPtr<FEdGraphSchemaAction_K2Graph>>>& ComponentFunctionActionsMap)
{
    ComponentFunctionActionsMap.Reset();

    check(Blueprint);
    check(FBlueprintEditorUtils::DoesSupportEventGraphs(Blueprint));

    // Need this for some reason...
    const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();

    // Find all UObjectProperty Pointers on the Actor Class
    UClass* ParentClass = Blueprint->SkeletonGeneratedClass ? Blueprint->SkeletonGeneratedClass->GetSuperClass() : *Blueprint->ParentClass;
    for (TFieldIterator<UObjectProperty> PropertyItr(ParentClass, EFieldIteratorFlags::IncludeSuper); PropertyItr; ++PropertyItr)
    {
        const UObjectProperty* Property = *PropertyItr;
        const FName PropertyName = Property->GetFName();
        const UClass* PropClass = Property->PropertyClass;

        // Find all Override-able UFunctions in the Weapon Module
        if (PropClass && PropClass->IsChildOf(UHT_WeaponModule::StaticClass()))
        {
            for (TFieldIterator<UFunction> FunctionItr(PropClass, EFieldIteratorFlags::IncludeSuper); FunctionItr; ++FunctionItr)
            {
                const UFunction* Function = *FunctionItr;
                const FName FunctionName = Function->GetFName();

                // Check if we can override this function
                if (UEdGraphSchema_K2::CanKismetOverrideFunction(Function) && !FObjectEditorUtils::IsFunctionHiddenFromClass(Function, PropClass))
                {
                    const FText FunctionTooltip = FText::FromString(UK2Node_CallFunction::GetDefaultTooltipForFunction(Function));
                    FText FunctionDesc = K2Schema->GetFriendlySignatureName(Function);
                    if (FunctionDesc.IsEmpty())
                    {
                        FunctionDesc = FText::FromString(Function->GetName());
                    }

                    FText FunctionCategory = Function->GetMetaDataText(FBlueprintMetadata::MD_FunctionCategory, TEXT("UObjectCategory"), Function->GetFullGroupName(false));

                    TSharedPtr<FEdGraphSchemaAction_K2Graph> NewFuncAction = MakeShareable(new FEdGraphSchemaAction_K2Graph(EEdGraphSchemaAction_K2Graph::Function, FunctionCategory, FunctionDesc, FunctionTooltip, 1, NodeSectionID::FUNCTION_OVERRIDABLE));
                    NewFuncAction->FuncName = FunctionName;

                    // Add Items
                    ComponentFunctionActionsMap.FindOrAdd(Property).Add(NewFuncAction);
                }
            }
        }
    }
}


But are you trying to do this to work on runtime or just for Blueprint Assets in Editor?
I mean, doing it that way you’ll only be able to call those functions through UProperty Reflection…

The Actor would have to check it has that function, if so, then call it… Otherwise search its Components and if found the function there then invoke it.
That wouldn’t be too much headache if you use the “ProcessEvent” API once you have found the function and decided if Actor or Component should call it.

The desired result here is to create a modular weapon system, and avoid a hugely bloated weapon actor class and improve flexibility.

You would have a shell actor that is the “Weapon” class. The actor acts as a convenient container for components. The idea is that the “Weapon” can specify a number of modules, and then the Weapon blueprint determines how those modules interact with each other. The actor weapon doesn’t care what each componetn does, it just acts as a central place for modules to talk to each other in a scriptable fashion.

Here’s an example:

A weapon actor has a “Shoot Projectile” component, which contains common C++ code for firing a projectile. Before it can fire a projectile, it calls it’s own function “CanFire()” - which is tagged as a BlueprintNativeEvent. The important part here is that the function is contained in the component so logic is compartmentalized, and so that the weapon ‘Actor’ does not become bloated with functions that any possible module would need to call.

So lets say I also have an ‘Ammo’ module added to that weapon actor. The ‘Shoot’ module has a BlueprintAssignable event called “OnShotFired”, which in stock UE4 I can add directly into the actors event graph, and tell the ‘Ammo’ module to remove some ammo. Just like this:

What I Can’t do right now however, is override the ‘Shoot’ modules ‘CanFire()’ function directly in the Actors event graph, to ask the ‘Ammo’ module if it has enough ammo. The idea is that end users can add any number of modules they like to a ‘Weapon’ blueprint, and the ‘Weapon BP’ then defines how those modules behave with each other.

This way, I don’t have to create module blueprints for every weapon, and the weapon actor doesn’t need to know about all the possible functionality of every possible module.


If you can think of another way to achieve this I’m all ears - but right now it feels like allowing the actor to create a blueprint implementation of it’s components’ functions is the only way to do this, but I’m not sure.

I’m about to just give up and create the CanFire() etc. functions in the Weapon Actor class, and modules will have to call the respective functions from the actor and pass themselves along, e.g. CanFire(UHT_FireModule* CallingModule)

Yeah, I think I get it;
The “Shoot” module, instead of having a hard reference to a “CanFire()”, could do it like Particles and Animation Blueprints do it:



bool myArgs;
Actor->ProcessEvent("CanFire",myArgs);

if (myArgs)
{
Shoot();
}


But, in this case the Shoot Component could simply iterate through all attached Ammo Components and ask itself if there’s enough ammo to shoot.

​​​​

Is there a reason why you’re so concerned with overriding existing functions?

I could be missing something, but I would approach this using delegates rather than being concerned with overriding functions etc. Your shell class could fire events that simply broadcast delegates, which the modules would be subscribed to. When you add a module, the module itself would subscribe its functions to the shell’s delegates, causing the functions to fire when the delegates are broadcast. This would also allow multiple functions (or modules) to be bound to one of your shell’s events, too.

I’ll have a look into that. It seems like in a roundabout way it’s kind of doing the same thing anyway, just using reflection to search through all the UFunctions by name. Hopefully it’ll pick up BP ones.

Delegates don’t support return values when used with Blueprint. You can see in the pic above I’ve set up a few for actually broadcasting events, but it’s the opposite part that’s the problem. So I can’t for example trigger a ‘CanFire()’ delegate with a return value of whether it can in fact fire.

The problem there is also that the shell class needs to know about all possible module functionality - at which point I may as well abandon modules and create one large class (which I’m trying to avoid)

I still think it’s possible, just not in the way you were envisioning, particularly with editor integration.

To take your example, you don’t need to interrogate the module to return whether or not it can fire. You can store simple variables on the shell class, such as “CanFire,” and modules can be prompted to reach back in and change that variable by event.

When your shell attempts to fire, it interrogates its own CanFire bool. If true, it fires and broadcasts to any module that is concerned with ammo and whatnot. (No iterating, just using delegates.) Said modules do their functional voodoo, and set the shell’s CanFire accordingly. A module can also do the firing at that point, with any kind of additional condition and behavior changes you want.

If you really REALLY want extensibility and interconnection between the modules, you can use a tagging system. Module A is attached, adds Tags X and Y to shell. Module B changes its behavior based on tags X and Y on shell.

If going the “tag route” I’d particularly use GameplayTags instead =]

One thing I hadn’t considered when starting this thread was that even if I create the module function implementation in the shell class blueprint, It’s highly likely that the variables and data I want to access so easily won’t be in scope anyway - so that venture is probably not worth continuing.

I did explore the idea of gameplay tags early on, they are flexible but on the flipside you lose a lot of control over the cost of replication etc - but I’m sure I can find creative ways ways to work around those.

I may have to return to that approach. Data modules are fairly straightforward, but modules that actually have behaviour and control state make life much more difficult… sigh I’ll get there eventually.

I am currently solving exactly some questions. One idea which I have is to create UModuleComponent and also UModuleComponentOwnerInterface. UModuleComponentOwnerInterface can be optionally implemented by owner and UModuleComponent’s function check if owner implements UModuleComponentOwnerInterface and call it’s function. Not very nice because of additional interface class but It should work.