FEditorLevelViewportClient
在一般的資源編輯器中,預覽畫面大多是繼承FEditorLevelViewportClient類別加以改寫,它代表用來看預覽畫面的一個視埠,可說是撰寫預覽視窗時的主要框架。其實它不限用於預覽,Matinee編輯器的軌道視窗也是用它寫的。FEditorLevelViewportClient類別並不包括視窗的部分,所以需要把它放進一個視窗。它以虛擬函式的方式提供輸入、繪製、更新的擴充界面。以下列出幾個主要的成員:
- ViewportType [ELevelViewportType]:可設為XY正交、XZ正交、YZ正交或透視。當只要2D繪圖不需要3D時,可設為LVT_None。
- SetRealtime(bNewValue, ...):設定是否即時更新。
- GetScene():可覆載此函式指定使用的場景,場景內的物件也會被繪出。預設使用GWorld的場景。
- InputKey( Viewport, ... ):可覆載此函式取得輸入事件。
- InputAxis( Viewport, ... ):可覆載此函式取得類比輸入。
- MouseMove( Viewport, X, Y ):可覆載此函式取得滑鼠座標變動。
- Draw( Viewport, Canvas ):可覆載此函式繪製2D圖形。
- Draw( SceneView, PDI ):可覆載此函式繪製3D圖形。
- Tick( DeltaSeconds ):可覆載此函式更新物件。
FPreviewScene
預覽視埠通常會需要一個用來放置預覽物件的預覽場景,UE提供FPreviewScene類別擔當此任。它只提供加入ActorComponent的功能,不支援Actor,而加入FPreviewScene的ActorComponent自然也沒有Owner。所以有些功能(例如AnimNotify_PlayParticleEffect)會針對沒有Owner的情況特別處理來支援預覽。然而有些功能(例如AnimNotify_PlayFaceFxAnim)就沒有支援在沒有Owner的情況下預覽。
- GetScene():傳回場景界面。一般自訂的FEditorLevelViewportClient類別會覆寫GetScene()改成傳回這個場景界面。
- AddComponent(ActorComponent, LocalToWorldMatrix):加入元件到指定的位置,此元件會沒有Owner。
- RemoveComponent(ActorComponent):移除場景中的元件。
- operator << (Archive, PreviewScene):序列化場景。序列化時必須呼叫此函式,以避免預覽場景被視為未被使用的物件而被回收掉。
FEditorCommonDrawHelper
有時3D預覽視埠會需要繪製格線和座標軸讓使用者更有方向感,UEd提供FEditorCommonDrawHelper類別方便開發者撰寫這些功能。
範例
以下程式碼展示一個典型的預覽視埠:
class FMyAssetViewportClient : public FEditorLevelViewportClient { public: FMyAssetViewportClient( class WxMyAssetEditor* MyAssetEditor ); void Serialize(FArchive& Archive); // FEditorLevelViewportClient interfaces virtual FSceneInterface* GetScene() { return m_PreviewScene.GetScene(); } virtual void InputKey( FViewport* Viewport, INT ControllerID, FName Key, EInputEvent Event, FLOAT AmountDepressed = 1.f, UBOOL bGamepad = FALSE ); virtual void InputAxis( FViewport* Viewport, INT ControllerID, FName Key, FLOAT Delta, FLOAT DeltaTime, UBOOL bGamepad = FALSE ); virtual void MouseMove( FViewport* Viewport, INT X, INT Y ); virtual void Draw( FViewport* Viewport, FCanvas* Canvas ); virtual void Draw( const FSceneView* View, FPrimitiveDrawInterface* PDI ); virtual void Tick( FLOAT DeltaSeconds ); ... private: WxMyAssetEditor* m_MyAssetEditor; FPrewviewScene m_PreviewScene; FEditorCommonDrawHelper m_DrawHelper; ... };預覽視埠類別的實作:
FMyAssetViewportClient::FMyAssetViewportClient( WxMyAssetEditor* MyAssetEditor ) : m_MyAssetEditor(MyAssetEditor) { ViewportType = LVT_Perspective; NearPlane = 1.f; SetRealtime(TRUE); UActorComponent* PreviewComponent = NULL; ... // Create PreviewComponent m_PreviewScene.AddComponent( PreviewComponent, FMatrix::Identity ); ... } void FMyAssetViewportClient::Serialize(FArchive& Archive) { Archive << Input; Archive << PreviewScene; } void FMyAssetViewportClient::Draw( const FSceneView* View, FPrimitiveDrawInterface* PDI ) { m_DrawHelper.Draw( View, PDI ); ... }預覽視埠需要放進一個預覽視窗裡:
class WxMyAssetPreviewWindow : public wxWindow { public: WxMyAssetPreviewWindow(wxWindow* Parent, wxWindowID ID, class WxMyAssetEditor* MyAssetEditor); ~WxMyAssetPreviewWindow(); private: DECLARE_EVENT_TABLE() void OnSize( wxSizeEvent& Event ); FMyAssetViewportClient* m_ViewportClient; };預覽視窗類別的實作:
BEGIN_EVENT_TABLE( WxMyAssetPreviewWindow, wxWindow ) EVT_SIZE( WxMyAssetPreviewWindow::OnSize ) END_EVENT_TABLE() WxMyAssetPreviewWindow::WxMyAssetPreviewWindow(wxWindow* Parent, wxWindowID ID, class WxMyAssetEditor* MyAssetEditor) : wxWindow( Parent, ID ) { m_ViewportClient = new FMyAssetViewportClient( MyAssetEditor ); m_ViewportClient->Viewport = GEngine->Client->CreateWindowChildViewport( m_ViewportClient, (HWND)GetHandle() ); m_ViewportClient->Viewport->CaptureJoystickInput(FALSE); } WxMyAssetPreviewWindow::~WxMyAssetPreviewWindow() { GEngine->Client->CloseViewport( m_ViewportClient->Viewport ); m_ViewportClient->Viewport = NULL; delete m_ViewportClient; } void WxMyAssetPreviewWindow::OnSize( wxSizeEvent& Event ) { wxRect rc = GetClientRect(); ::MoveWindow( (HWND)m_ViewportClient->Viewport->GetWindow(), 0, 0, rc.GetWidth(), rc.GetHeight(), 1 ); }
沒有留言:
張貼留言