I wanted to empower my sound designer to create simple blueprints that respond to Game Events. To achieve this, I created a custom class inheriting from a UObject to encapsulate the blueprint script. However, in this object blueprint, when attempting to access a Service (or any static method with a hidden World Context Object pin), an error occurs: Class X is unsafe to call from blueprint of class Y. Pin Context Object must have a connection
.
This issue arises because, unlike actors, the UObject is not inherently parented to a World. Consequently, a hidden pin that is usually set automatically remains unset this time.
There are two possible solutions:
- Display the Pin and Connect Something Else
- Parent Your Object to a World
1. Display the Pin and Connect Something Else
In this solution, simply add the UCLASS property meta = (ShowWorldContextPin)
on top of the class executing the blueprint script.
UCLASS(Blueprintable, BlueprintType, meta = (ShowWorldContextPin))
class THEPATHTOL0_API UL0GameEventCue : public UObject
{
UFUNCTION(BlueprintImplementableEvent, BlueprintCallable)
void Execute(UWorld* World) const;
}
Now the pin appears. All you need to do is add any World Context Object to the function from the caller. In the example, I pass a UWorld, but it could be an actor, for instance.
2. Parent Your Object to a World
The other solution is to parent the UObject to a class. To achieve this, make sure to have a pointer to an actor parented to a World or have a pointer to a world. This is the case if you call it from a Subsystem (UGameInstanceSubsystem returns a valid world from GetWorld()
.
First, override the GetWorld() function. It’s crucial to do this and not call Super::GetWorld(); there is a check in this class, and it won’t work:
UWorld* UL0GameEventCue::GetWorld() const
{
// We may be called from a Subservice which is not a good candidate to be an Outer object. However UGameInstanceSubsystem have a valid GetWorld().
// Thus the Outer might be directly the world.
if (UWorld* WorldOuter = Cast<UWorld>(GetOuter()))
{
return WorldOuter;
}
const AActor* Outer = Cast<AActor>(GetOuter());
if(Outer)
{
return Outer->GetWorld();
}
return nullptr;
}
Additionally, ensure that when you instantiate the object, you pass a valid Outer Object (and not the transient package, for instance):
UObject* Outer = this; // This is an Actor
// UObject* Outer = GetWorld(); // Or use a GetWorld() accessor. Ensure that it returns a valid pointer.
UL0GameEventCue* CueInstance = NewObject<UL0GameEventCue>(Outer, Element.Get());
Now it should work smoothly.
If you encounter the error PIE: Error: Blueprint Runtime Error: PIE: Error: Blueprint Runtime Error: "Accessed None trying to read property XXX
, it means that your GetWorld() function returns null. Recheck the outer object you passed in and the body of the GetWorld function.