Hello,
We have designed a custom FEdMode that the user can Enter/Exit like the similar modes Foliage, Landscape, etc…
In that mode, some special actors of ours are allowed to be moved/rotated/scaled.
Outside that mode, we would like to prevent the users to alter the transform of these actors (only base Transform) by making it read only. We don’t want the user to alter the actor in any way, and then revert the change immediately.
After a few hours of research, I found a solution but it is not ideal and I wanted to know if there were some hooks I didn’t find, or a just simpler way to do it.
What I tried:
- GEditor->OnBeginObjectMovement() : by binding it with a method that instantly unselect the actor, it works, but only for the gyzmo. Changing values in the details panel still modifies the transform
- Any delegates for actor movement or property changed : I didn’t find one that happens before it changes, or there are some, but it is impossible to prevent the change (they only carry informative value)
- DetailsCustomization for the specific actors : could have worked, but Transform component has already its own customization (FComponentTransformDetails) with its own logic. It is private and not exported so not really customizable/extendable/overridable.
- CanEditChange() on these actors : doesn’t seem to apply to Transform either
So far my solution works but is not ideal because it still modifies the Engine and requires the actor to inherit an additional interface. I would like to prevent as much as possible to alter the Engine, and also the Actor class. Here is what I’ve done:
Created a class where a static delegate is defined in UnrealEd plugin:
DECLARE_DELEGATE_RetVal_OneParam(bool, FCanEditTransformDelegate, const USceneComponent*)
class UNREALED_API FEditorTransformLock
{
public :
static FCanEditTransformDelegate& Get()
{
return GDelegateInstance;
}
private :
static FCanEditTransformDelegate GDelegateInstance;
};
//.cpp
FCanEditTransformDelegate FEditorTransformLock::GDelegateInstance;
in FComponentTransformDetails I have added a modification to the GetIsEnabled() method:
bool FComponentTransformDetails::GetIsEnabled() const
{
// new
for (const TWeakObjectPtr<UObject>& Obj : SelectedObjects)
{
const USceneComponent* Component = Cast<USceneComponent>(Obj.Get());
if (!Component)
{
if (const AActor* Actor = Cast<AActor>(Obj.Get()))
{
Component = Actor->GetRootComponent();
}
}
if (Component && FEditorTransformLock::Get().IsBound() && !FEditorTransformLock::Get().Execute(Component))
{
return false;
}
}
//!new
return bIsEnabledCache;
}
In one of my editor module startup, I have bound the delegate
FEditorTransformLock::Get().BindStatic(&FMyEditorModeModule::CanEditTransformStatic);
and written the method associated:
bool FMyEditorModeModule::CanEditTransformStatic(const USceneComponent* Component)
{
if (!Component)
{
return true;
}
if (Component != Component->GetOwner()->GetRootComponent())
{
return true;
}
if (const AActor* Owner = Component->GetOwner())
{
if (Owner->GetClass()->ImplementsInterface(UTransformLockedInterface::StaticClass()))
{
const ITransformLockedInterface* Lockable = Cast<ITransformLockedInterface>(Owner);
return !Lockable->IsRootTransformedLocked();
}
}
return true;
}
Then I created a UTransformLockedInterface for my actor to use that only defines an abstract method bool IsRootTransformedLocked():
and then added it to the actor parent list. And the override:
bool AMyBaseActor::IsRootTransformedLocked() const
{
if (IsSpecialActor())
{
if (!GLevelEditorModeTools().IsModeActive("EM_MyCustomEditorMode"))
{
return true;
}
}
return false;
}