在UE4中存在内置的五种编辑器模式,如Place模式,Paint, Landscape, Foliage, GeometryEdit模式,在每一种模式下,我们的编辑器视口有不同的各种操作,比如Place模式下就是摆放和编辑各种Actor,而Landscape模式下就是创建地形以及编辑地形,在Landscape模式下无法选中Actor。总之各种模式下,视口操作Actor的行为是不一样的,这就是“模式(Mode)”.
我们可以通过FEdMode定制我们的模式。
(1)在模块注册(注销)模式
void FMyTestModeModule::StartupModule()
{
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
FEditorModeRegistry::Get().RegisterMode(FMyTestModeEdMode::EM_MyTestModeEdModeId, LOCTEXT("MyTestModeEdModeName", "MyTestModeEdMode"), FSlateIcon(), true);
}
void FMyTestModeModule::ShutdownModule()
{
// This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
// we call this function before unloading the module.
FEditorModeRegistry::Get().UnregisterMode(FMyTestModeEdMode::EM_MyTestModeEdModeId);
}
(2)继承FEdMode,定制我们的模式:
class FMyTestModeEdMode : public FEdMode
{
public:
const static FEditorModeID EM_MyTestModeEdModeId;
public:
FMyTestModeEdMode();
virtual ~FMyTestModeEdMode();
// FEdMode interface
virtual void Enter() override;
virtual void Exit() override;
bool UsesToolkits() const override;
// End of FEdMode interface
};
const FEditorModeID FMyTestModeEdMode::EM_MyTestModeEdModeId = TEXT("EM_MyTestModeEdMode");
void FMyTestModeEdMode::Enter()
{
FEdMode::Enter();
if (!Toolkit.IsValid() && UsesToolkits())
{
Toolkit = MakeShareable(new FMyTestModeEdModeToolkit);
Toolkit->Init(Owner->GetToolkitHost());
}
}
void FMyTestModeEdMode::Exit()
{
if (Toolkit.IsValid())
{
FToolkitManager::Get().CloseToolkit(Toolkit.ToSharedRef());
Toolkit.Reset();
}
// Call base Exit method to ensure proper cleanup
FEdMode::Exit();
}
bool FMyTestModeEdMode::UsesToolkits() const
{
return true;
}
(3)在FModeToolkit中扩展我们的模式内容
class SCompoundWidget;
class FMyTestModeEdModeToolkit : public FModeToolkit
{
public:
FMyTestModeEdModeToolkit();
/** FModeToolkit interface */
virtual void Init(const TSharedPtr& InitToolkitHost) override;
/** IToolkit interface */
virtual class FEdMode* GetEditorMode() const override;
virtual TSharedPtr GetInlineContent() const override;
private:
TSharedPtr ToolkitWidget;
};
void FMyTestModeEdModeToolkit::Init(const TSharedPtr& InitToolkitHost)
{
ToolkitWidget = SNew(SMyCompoundWidget);
FModeToolkit::Init(InitToolkitHost);
}
class FEdMode* FMyTestModeEdModeToolkit::GetEditorMode() const
{
return GLevelEditorModeTools().GetActiveMode(FMyTestModeEdMode::EM_MyTestModeEdModeId);
}
TSharedPtr FMyTestModeEdModeToolkit::GetInlineContent() const
{
return ToolkitWidget;
}
其中GetInlineContent返回的就是我们在模式面板定制的SWidget内容,
Enter(进入模式),Exit(退出模式),Tick(在模式中Tick)
// FEdMode interface
//进入模式函数
virtual void Enter() override;
//退出模式函数
virtual void Exit() override;
//Tick函数
virtual void Tick(FEditorViewportClient* ViewportClient, float DeltaTime) override;
(1)控制视口鼠标样式,,返回False代表用默认的,返回True则使用OutCursor指定的
virtual bool GetCursor(EMouseCursor::Type& OutCursor) const;
(2)控制选中一个Actor的TransformWidget是否显示, 返回True,则可以显示,反之不显示。TransformWidget消失后不能在移动Actor
virtual bool UsesTransformWidget() const;
(3)在编辑器Viewport绘制基本图元,图形等
FCanvas* Canvas的DrawItem可以绘制继承FCanvasItem的各种图元()
virtual void DrawHUD(FEditorViewportClient* ViewportClient, FViewport* Viewport, const FSceneView* View, FCanvas* Canvas);
void FMyTestModeEdMode::DrawHUD(FEditorViewportClient* ViewportClient, FViewport* Viewport, const FSceneView* View, FCanvas* Canvas)
{
FCanvasLineItem CanvasLineItem(FVector2D(0, 0), FVector2D(100.0f, 100.0f));
Canvas->DrawItem(CanvasLineItem);
}
可绘制的基本图元
//FCanvasTriangleItem
//FCanvasTextItem
//FCanvasBorderItem
//FCanvasTileItem
//FCanvasLineItem
(1) 在编辑器视口鼠键的输入
返回True,则鼠标右键按下无法在控制视口旋转,反之返回False可以。
virtual bool InputKey(FEditorViewportClient* ViewportClient, FViewport* Viewport, FKey Key, EInputEvent Event);
按键(包括了鼠标和键盘,限于篇幅,只截取部分),在InputCoreType.h文件
struct INPUTCORE_API EKeys
{
static const FKey AnyKey;
static const FKey MouseX;
static const FKey MouseY;
static const FKey MouseScrollUp;
static const FKey MouseScrollDown;
static const FKey MouseWheelAxis;
static const FKey LeftMouseButton;
static const FKey RightMouseButton;
static const FKey MiddleMouseButton;
static const FKey ThumbMouseButton;
static const FKey ThumbMouseButton2;
static const FKey BackSpace;
static const FKey Tab;
static const FKey Enter;
static const FKey Pause;
static const FKey CapsLock;
static const FKey Escape;
static const FKey SpaceBar;
static const FKey PageUp;
static const FKey PageDown;
static const FKey End;
static const FKey Home;
static const FKey Left;
static const FKey Up;
static const FKey Right;
static const FKey Down;
static const FKey Insert;
static const FKey Delete;
static const FKey Zero;
static const FKey One;
static const FKey Two;
按键事件
enum EInputEvent
{
IE_Pressed =0,
IE_Released =1,
IE_Repeat =2,
IE_DoubleClick =3,
IE_Axis =4,
IE_MAX =5,
};
至于出现了 “鼠键同时和Ctrl,Shift,Alt同时按下”,也就是“Ctr+”, "Shift+","Alt+", 得通过FViewport* Viewport来判断,在UnrealClient.cpp提供了判断Ctrl,Shift,Alt三个键是否按下的判断函数
ENGINE_API bool IsCtrlDown(FViewport* Viewport) { return (Viewport->KeyState(EKeys::LeftControl) || Viewport->KeyState(EKeys::RightControl)); }
ENGINE_API bool IsShiftDown(FViewport* Viewport) { return (Viewport->KeyState(EKeys::LeftShift) || Viewport->KeyState(EKeys::RightShift)); }
ENGINE_API bool IsAltDown(FViewport* Viewport) { return (Viewport->KeyState(EKeys::LeftAlt) || Viewport->KeyState(EKeys::RightAlt)); }
(2)按下鼠标键的瞬间
virtual bool MouseEnter(FEditorViewportClient* ViewportClient, FViewport* Viewport, int32 x, int32 y);
(3)释放鼠标键的瞬间
virtual bool MouseLeave(FEditorViewportClient* ViewportClient, FViewport* Viewport);
(4)释放状态的鼠标键在视口中移动
//按下鼠标键在视口移动
virtual bool CapturedMouseMove(FEditorViewportClient* InViewportClient, FViewport* InViewport, int32 InMouseX, int32 InMouseY);
(5)按下鼠标键在视口移动
virtual bool CapturedMouseMove(FEditorViewportClient* InViewportClient, FViewport* InViewport, int32 InMouseX, int32 InMouseY);
在定制我们的FEdMode,经常包含了各种UObject,FEdMode继承了FGCObject,因此在FEdMode创建的各种UObject对象在
AddReferencedObjects函数中添加到“引用对象集合”,具体可参考博客(UE4 4.20 )UE4的GC(垃圾回收)编程规范
class UNREALED_API FEdMode : public TSharedFromThis, public FGCObject, public FEditorCommonDrawHelper
virtual void AddReferencedObjects(FReferenceCollector& Collector) override {}
(1)利用模式声明的ID获取模式, 方便获取模式中的各种变量
FMyTestModeEdMode* aaa = (FMyTestModeEdMode*)GLevelEditorModeTools().GetActiveMode(FMyTestModeEdMode::EM_MyTestModeEdModeId);
(2)获取视口鼠标点击的mesh或Actor,也就是鼠标在视口的光线求交
将鼠标在视口的位置转为世界空间的位置
/** Trace under the mouse cursor and return the landscape hit and the hit location (in landscape quad space) */
bool FEdModeLandscape::LandscapeMouseTrace(FEditorViewportClient* ViewportClient, float& OutHitX, float& OutHitY)
{
int32 MouseX = ViewportClient->Viewport->GetMouseX();
int32 MouseY = ViewportClient->Viewport->GetMouseY();
return LandscapeMouseTrace(ViewportClient, MouseX, MouseY, OutHitX, OutHitY);
}
bool FEdModeLandscape::LandscapeMouseTrace(FEditorViewportClient* ViewportClient, int32 MouseX, int32 MouseY, FVector& OutHitLocation)
{
// Cache a copy of the world pointer
UWorld* World = ViewportClient->GetWorld();
// Compute a world space ray from the screen space mouse coordinates
FSceneViewFamilyContext ViewFamily(FSceneViewFamilyContext::ConstructionValues(ViewportClient->Viewport, ViewportClient->GetScene(), ViewportClient->EngineShowFlags)
.SetRealtimeUpdate(ViewportClient->IsRealtime()));
FSceneView* View = ViewportClient->CalcSceneView(&ViewFamily);
FViewportCursorLocation MouseViewportRay(View, ViewportClient, MouseX, MouseY);
FVector MouseViewportRayDirection = MouseViewportRay.GetDirection();
FVector Start = MouseViewportRay.GetOrigin();
FVector End = Start + WORLD_MAX * MouseViewportRayDirection;
if (ViewportClient->IsOrtho())
{
Start -= WORLD_MAX * MouseViewportRayDirection;
}
}
【1】UE4地形编辑器源码:Engine\Source\Editor\LandscapeEditor