2014年2月23日 星期日

改善對地形的射線追縱

Actor提供的Trace函式若打到地形,並不會傳回材質資訊而且傳回的物理材質也都是引擎的預設材質,並無參考價值。如果想要依據不同材質播放不同特效音效,就得想辦法得知擊中的材質為何。本篇文章告訴你如何修改射線追蹤傳回的材質,反映出實際擊中的材質。

為了不變更原本程式的運作,首先定義另一個Trace函式:
class Actor extends Object
...

native noexport function Actor TraceEx
(
    out vector                  HitLocation,
    out vector                  HitNormal,
    vector                      TraceEnd,
    optional vector             TraceStart,
    optional bool               bTraceActors,
    optional vector             Extent,
    optional out TraceHitInfo   HitInfo,
    optional int                ExtraTraceFlags
);
為了呼叫原本的execTrace,將此函式宣告為noexport並且自行實作:
void AActor::execTraceEx(FFrame& Stack, RESULT_DECL)
{
    // Keep program counter.
    BYTE* CodePtr = Stack.Code;
    
    // Extract parameters.
    P_GET_VECTOR_REF(HitLocation);
    P_GET_VECTOR_REF(HitNormal);
    P_GET_VECTOR(TraceEnd);
    P_GET_VECTOR_OPTX(TraceStart,Location);
    P_GET_UBOOL_OPTX(bTraceActors,bCollideActors);
    P_GET_VECTOR_OPTX(TraceExtent,FVector(0.f));
    P_GET_STRUCT_OPTX_REF(FTraceHitInfo,HitInfo,FTraceHitInfo());
    
    // Restore program counter.
    Stack.Code = CodePtr;
    // Invoke the original function.
    this->execTrace(Stack, Result);
    
    // If terrain hit, find the material with the highest weight at the hit location.
    if( pHitInfo )
    {
        ATerrain* Terrain = Cast<ATerrain>(*(AActor**)Result);
        if( Terrain && Terrain->WeightedMaterials.Num() > 0 )
        {
            FVector LocalPos = Terrain->WorldToLocal().TransformFVector(*pHitLocation);
            INT XI = appFloor(LocalPos.X);
            FLOAT XF = LocalPos.X - XI;
            INT YI = appFloor(LocalPos.Y);
            FLOAT YF = LocalPos.Y - YI;
            
            INT MajorMaterial = 0;
            INT MajorWeight = Terrain->WeightedMaterials(0).FilteredWeight(XI, XF, YI, YF);
            
            for(INT i = 1; i < Terrain->WeightedMaterials.Num(); i++)
            {
                INT Weight = Terrain->WeightedMaterials(i).FilteredWeight(XI, XF, YI, YF);
                if(Weight > MajorWeight)
                {
                 MajorMaterial = i;
                 MajorWeight = Weight;
                }
            }
            
            UMaterialInterface* Material = Terrain->WeightedMaterials(MajorMaterial).Material->Material;
            if( Material )
            {
                pHitInfo->PhysMaterial = Material->GetPhysicalMaterial();
                pHitInfo->Material = Material->GetMaterial();
            }
        }
    }
}
一開始先記錄目前的指令位置,取得參數後再回復指令位置,然後呼叫execTrace進行原本的射線追蹤。如果打到地形的話,才去找出是哪一層在擊中點的權重最大,再修改傳回的材質。

沒有留言:

張貼留言