2012年3月8日 星期四

更新Unreal的屬性視窗

在撰寫編輯器時,如果使用屬性視窗顯示UnrealScript物件,那麼在程式碼中去修改物件屬性時,會需要通知屬性視窗更新。雖然UE有提供更新的方法:
GCallbackEvent->Send(CALLBACK_ObjectPropertyChanged, Obj);

不過有兩個問題:
  • 如果第二個參數傳入NULL,那整個屬性視窗樹都會重建(請參考WxPropertyWindow::Rebuild()),這個動作可能會很慢。
  • 如果指定物件,WxPropertyWindow::Rebuild()只會檢查指定物件是否在第一層才更新(請參考WxPropertyWindow::ContainsObject())。但如是是第一層以下的子物件才擁被修改的屬性,那麼將不會更新視窗。

只更新單一屬性


當編輯器要提供高頻率更新的操作時(例如使用滑鼠滾輪變更屬性值),使用CALLBACK_ObjectPropertyChanged通知可能會拖慢更新速度。所以自行撰寫只更新指定屬性的程式會比較好。以下程式可以只更新指定物件的指定屬性
void UpdatePropertyControl(WxPropertyWindowHost* PropertyWindowHost, UObject* Object, FString PropertyName)
{
    WxPropertyWindow* Window = PropertyWindowHost->GetPropertyWindowForCallbacks();
    FObjectPropertyNode* Root = Window->GetRoot();

    if( FObjectPropertyNode* ObjectNode = FindPropertyNodeByObject(Root, Objmect) )
    {
         if( FPropertyNode* Node = ObjectNode->FindPropertyNode(PropertyName) )
        {
            UpdatePropertyNodeSubtree( Node );
        }
    }
}

void FObjectPropertyNode* FindPropertyNodeByObject(FPropertyNode* Node, UObject Object)
{
    FObjectPropertyNode* ObjectNode = dynamic_cast<FObjectPropertyNode*>( Node );
    if( ObjectNode )
    {
         for( FObjectPropertyNode::TObjectIterator i = ObjectNode->ObjectIterator(); i; i++)
        {
            if( *i = Object )
            {
                return ObjectNode;
            }
        }
    }

    for(INT i=0; i<Node->GetNumCHildNodes(); i++)
    {
        FObjectPropertyNode* Child = FindPropertyNodeByObject( Node->GetChildNode(i), Object );
        if( Child )
        {
            return Child;
        }
    }

    return NULL;
}

void UpdatePropertyNodeSubtree(FPropertyNode* Node)
{
    for(INT i=0; i<Node->GetNumChildNodes(); i++)
    {
        FPropertyNode* Child = Node->GetChildNode(i);
        UpdatePropertyNodeSubtree( Child );
    }
    UpdatePropertyNode( Node );
}

void UpdatePropertyNode(FPropertyNode* Node)
{
    FObjectPropertyNode* ObjectNode = Node->FindObjectItemParent();
    if( ObjectNode && ObjectNode->GetNumObjects() )
    {
        UPropertyInputProxy* InputProxy = Node->GetNodeWindow()->InputProxy;
        if( InputProxy )
        {
            BYTE* Value = (BYTE*) Node->GetValueBaseAddress( (BYTE*) ObjectNode->GetObject(0) );
            if( UPropertyInputRotation* RotationProxy = Cast<UPropertyInputRotation>(InputProxy) )
            {
                if( RotationProxy->TextCtrl )
                {
                    INT UnrRot = *((INT*) Value);
                    RotationProxy->TextCtrl->SetValue( *MakeRotationText(UnrRot) );
                    RotationProxy->Equation = appItoa(UnrRot);
                }
            }
            else
            {
                InputProxy->RefreshControlValue( Node->GetProperty(), Value );
            }
        }
    } 
}

FString MakeRotationText(INT UnrRot)
{
    FLOAT Degree = 360.f * (UnrRot / 65536.f);
    if( Abs(Degree) > 359.f )
    {
        INT Revolutions = Degree / 360.f;
        Degree -= Revolutins * 360;
        return FString::Printf( TEXT("%.2f%c %s %d"), Degree, 176, (Revolutions<0) ? TEXT("-") : TEXT("+"), Abs(Revolutions) );
    }
    else
    {
        return FString::Printf( TEXT("%.2f%c"), Degree, 176 );
    }
}

沒有留言:

張貼留言