前言
一个简易的插槽编辑器插件
准备工作 先看编辑器扩展:自定义预览试图 , 我们已经完成了插件和预览试图的创建, 本文将进行插槽编辑相关功能的添加和扩展
插槽编辑类 参考UnrealEd
模块的SSocketManager
类, 我们自己模仿着写一个类出来作为编辑插槽的Slate
类
为什么不直接用? 首先方便我们自己修改, 另外, 该对象是private. private.. private…, 不过我们还是可以继承ISocketManager
接口来省掉一部分工作(不继承也没关系, 自己声明类似的函数即可)
代码较多, 多数是参考SSocketManager
类的实现, 不过由于我们没有用到StaticMeshEditor
类, 所以于此相关的代码都需要替换成我们自己的代码, 我这里把类似的代码都放到了我们的顶层UI对象里, 即我们的SSHud_SimpleSocket
类
1 class SSocketEdit : public ISocketManager, public FNotifyHook
同样的, 有两个数据结构也是模仿着抄写一遍, 名字改一下而已, 比如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 struct SocketListItem_SimpleSocketEdit { public : SocketListItem_SimpleSocketEdit (UStaticMeshSocket* InSocket) : Socket (InSocket) { } UStaticMeshSocket* Socket; DECLARE_DELEGATE (FOnRenameRequested); FOnRenameRequested OnRenameRequested; };
最后在顶层UI类里面添加进去这个slate
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 + SSplitter::Slot () .Value (0.9f ) [ SNew (SSplitter) .Orientation (EOrientation::Orient_Horizontal) + SSplitter::Slot () .Value (0.7f ) [ SAssignNew (ViewPort, SEditorViewport_SimpleSocket) .HUD (SharedThis (this )) .PreviewMesh (EditMesh) ] + SSplitter::Slot () .Value (0.3f ) [ SAssignNew (SocketEditor, SSocketEdit) .StaticMesh (EditMesh) .HUD (SharedThis (this )) ] ]
用的是SSplitter
而非SHorizontalBox
之类的, 毕竟SSplitter
可以自由缩放窗口
FEditorViewportClient_SimpleSocket 视图Slate
类负责渲染, 那么还需要一个与此对应的类来负责交互和数据管理, 那么就是这个类
1 class FEditorViewportClient_SimpleSocket : public FEditorViewportClient, public TSharedFromThis<FEditorViewportClient_SimpleSocket>
另外有了插槽编辑类, 那么FEditorViewportClient
需要与其进行数据交互, 我们通过hud
类来作为上层管理来对这两个对象进行数据交互, 比如在FEditorViewportClient
中选中了某个插槽, 那么需要同步更新至插槽编辑类中, 如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 void FEditorViewportClient_SimpleSocket::ProcessClick (class FSceneView& View, class HHitProxy* HitProxy, FKey Key, EInputEvent Event, uint32 HitX, uint32 HitY) { const bool bCtrlDown = Viewport->KeyState (EKeys::LeftControl) || Viewport->KeyState (EKeys::RightControl); bool ClearSelectedSockets = true ; if (HitProxy) { if (HitProxy->IsA (HSMESocketProxy_SimpleSocket::StaticGetType ())) { HSMESocketProxy_SimpleSocket* SocketProxy = (HSMESocketProxy_SimpleSocket*)HitProxy; UStaticMeshSocket* Socket = NULL ; if (SocketProxy->SocketIndex < StaticMesh->Sockets.Num ()) { Socket = StaticMesh->Sockets[SocketProxy->SocketIndex]; } if (Socket) { HudPtr.Pin ()->SetSelectedSocket (Socket); } ClearSelectedSockets = false ; } } if (ClearSelectedSockets && HudPtr.Pin ()->GetSelectedSocket ()) { HudPtr.Pin ()->SetSelectedSocket (NULL ); } Invalidate (); }
该函数实现了点击的逻辑, 我们视图中出现的像钻石形状的插槽物体其实是该类绘制出来的一个代理, 可以点击交互, 选中以后我们通知hud
我们选中了插槽
另外, 该类有很多功能函数, 比如
1 2 3 4 5 6 7 8 9 virtual bool InputKey (FViewport* Viewport, int32 ControllerId, FKey Key, EInputEvent Event, float AmountDepressed = 1.f , bool bGamepad = false ) override ;virtual void DrawCanvas (FViewport& InViewport, FSceneView& View, FCanvas& Canvas) override ;virtual bool InputWidgetDelta (FViewport* Viewport, EAxisList::Type CurrentAxis, FVector& Drag, FRotator& Rot, FVector& Scale) override ;virtual void TrackingStarted (const struct FInputEventState& InInputState, bool bIsDragging, bool bNudge) override ;virtual void TrackingStopped () override ;
还有一堆名字上带WidgetMode
的函数, 用于控制切换移动/旋转/缩放的不同操控UI
最后通过void FEditorViewportClient_SimpleSocket::Draw(const FSceneView* View, FPrimitiveDrawInterface* PDI)
函数来完成实时绘制插槽代理的任务, 我这里修改了一下代码, 让选中的插槽显示不同的颜色
1 2 3 4 5 6 7 FColor SocketColor = (Socket == HudPtr.Pin ()->GetSelectedSocket ()? SocketColor_Selected : SocketColor_Default); FMatrix SocketTM; Socket->GetSocketMatrix (SocketTM, StaticMeshComponent); PDI->SetHitProxy (new HSMESocketProxy_SimpleSocket (i)); DrawWireDiamond (PDI, SocketTM, 5.f , SocketColor, SDPG_Foreground);PDI->SetHitProxy (NULL );
最终效果
后记
场景内的模型, 包括插槽预览模型都是作为Component
添加到场景对象PreviewScene
内的
换模型从hud
对象中发起, 分别要通知到插槽编辑类SSocketEdit
和视图对象SEditorViewport_SimpleSocket
, 然后视图类再刷新场景内的模型组件,再通知到视图管理类FEditorViewportClient_SimpleSocket
StaticMeshEditor
的所有命令都是从Editor
对象发起, 我这里直接各自去绑定命令和输入了, 比如插槽编辑类右键插槽名字弹出的菜单是自己绑定的命令
模型的选择可以参考文章编辑器扩展:自定义属性界面 , 用的是SObjectPropertyEntryBox
最近一次编辑过的模型对象保存在一个编辑器subsystem中, 其实也可以用其他方式保存