為了不變更原本程式的運作,首先定義另一個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進行原本的射線追蹤。如果打到地形的話,才去找出是哪一層在擊中點的權重最大,再修改傳回的材質。
沒有留言:
張貼留言