차이

이 문서의 선택한 이전 버전과 현재 버전 사이의 차이를 보여줍니다.

차이 보기로 연결

gearsofwar2:고어적표현 [2010/12/29 22:50]
z3moon
gearsofwar2:고어적표현 [2012/03/02 22:53] (현재)
z3moon
줄 1: 줄 1:
-====== 개요 ======+====== 고어적 표현 ======
 GoW2 의 큰 특징중 하나는 '잔혹한 표현' 이다. GoW2 의 큰 특징중 하나는 '잔혹한 표현' 이다.
  
 GoW 아이콘중 하나인 체인소우 (일명 랜서) 에는 전기톱이 달려있고, 이것을 이용하여 적의 사지를 절단하는 액션은 이 게임의 백미라 할 수 있다. GoW 아이콘중 하나인 체인소우 (일명 랜서) 에는 전기톱이 달려있고, 이것을 이용하여 적의 사지를 절단하는 액션은 이 게임의 백미라 할 수 있다.
  
-====== 사지 절단 ======+===== 사지 절단 =====
 {{http://farm6.static.flickr.com/5201/5226340582_d1f49f878d.jpg}} {{http://farm6.static.flickr.com/5201/5226340582_d1f49f878d.jpg}}
  
줄 11: 줄 11:
 이를 구현하기 위해서는 다음과 같은 것들이 필요하다. 이를 구현하기 위해서는 다음과 같은 것들이 필요하다.
  
-===== 리소스 =====+==== 리소스 ====
 | {{http://farm6.static.flickr.com/5282/5225797424_3610fd357a.jpg}} | {{http://farm6.static.flickr.com/5048/5302620621_89c79a9f1c.jpg}} | | {{http://farm6.static.flickr.com/5282/5225797424_3610fd357a.jpg}} | {{http://farm6.static.flickr.com/5048/5302620621_89c79a9f1c.jpg}} |
 | 이미지1 | 이미지2 | | 이미지1 | 이미지2 |
줄 35: 줄 35:
   - 이미지2와 같이, 바로 전에 사용된 조각들을 서로 꿰매서 soft weighted 된 SkeletalMesh 제작. (**이하 GoreMesh_SoftWeighted**) 단, 절단부위 vertex 들은 위, 아래 bone 모두에게 weight 영향을 받도록 할 것.   - 이미지2와 같이, 바로 전에 사용된 조각들을 서로 꿰매서 soft weighted 된 SkeletalMesh 제작. (**이하 GoreMesh_SoftWeighted**) 단, 절단부위 vertex 들은 위, 아래 bone 모두에게 weight 영향을 받도록 할 것.
  
-===== 엔진 임포트 및 절단부위를 위한 후처리 작업 =====+==== 엔진 임포트 및 절단부위를 위한 후처리 작업 ====
 이제 위에서 제작된 GoreSkeletalMesh 들을 에디터에서 불러들이고 사용할 수 있게끔 가공해야 한다. 여기서 가공한다는 뜻은 In-Game 에서 특정 이벤트 (썰기,터뜨리기 등 고어적 표현이 필요한 이벤트) 발생 시, GoreMesh_SoftWeighted -> GoreMesh 로 weight 값을 변경할 vertex 들을 추려내어((모든 vertex 의 weight 값을 교체하면 모든 절단부위가 동시에 떨어져 나가는 효과일 것이다. 하지만 보통은 이것을 기대하지 않을 것이다. 원하는 절단부위의 vertex weight 만을 교체하여 팔,다리만 떨어져 나가는 효과를 원할 것이다. 이러기 위해서는 절단부위의 vertex 만을 추려내어 부위별로 분류해둘 필요가 있다.)) metadata 로 저장해 놓는 것을 말한다. 이제 위에서 제작된 GoreSkeletalMesh 들을 에디터에서 불러들이고 사용할 수 있게끔 가공해야 한다. 여기서 가공한다는 뜻은 In-Game 에서 특정 이벤트 (썰기,터뜨리기 등 고어적 표현이 필요한 이벤트) 발생 시, GoreMesh_SoftWeighted -> GoreMesh 로 weight 값을 변경할 vertex 들을 추려내어((모든 vertex 의 weight 값을 교체하면 모든 절단부위가 동시에 떨어져 나가는 효과일 것이다. 하지만 보통은 이것을 기대하지 않을 것이다. 원하는 절단부위의 vertex weight 만을 교체하여 팔,다리만 떨어져 나가는 효과를 원할 것이다. 이러기 위해서는 절단부위의 vertex 만을 추려내어 부위별로 분류해둘 필요가 있다.)) metadata 로 저장해 놓는 것을 말한다.
  
줄 57: 줄 57:
     - 저장     - 저장
  
-===== 변수 =====+==== 변수 ====
   * 직접적인 변수<code cpp>   * 직접적인 변수<code cpp>
 // GearPawn.uc // GearPawn.uc
-var bool           bUsingNewSoftWeightedGoreWhichHasStretchiesFixed; +var   bool           bIsGore;                       // Gore 연출 할 SkeletalMesh 와 PhysicsAsset 으로 전환이 이루어졌는지 여부 
-var SkeletalMesh   GoreSkeletalMesh;              // Gore 연출 시 사용할 SkeletalMesh +var   bool           bGoreSetupForDeath;            // Gore Setup 을 완수하였는지 여부. Gore Setup 은 쪼개진 Gib 들을 위해 약간의 최적화 작업을 수행한다. 
-var PhysicsAsset   GorePhysicsAsset;              // Gore 연출 시 사용할 PhysicsAsset +var   bool           bHasBrokenConstraints;         // 하나라도 신체부위가 쪼개졌다면 TRUE 
-var Array<Name>    GoreBreakableJoints;           // Gore 연출 시 쪼갤 뼈대 이름을 담은 배열 + 
-var Array<Name>    HostageHealthBuckets;          // Hostage 상태일 때, 각 파츠가 떨어지는 Gore 효과를 위해 Health 를 가지는 뼈대 이름을 담은 배열+var   bool           bUsingNewSoftWeightedGoreWhichHasStretchiesFixed;   // Gore 연출을 위해 SoftWeighted 된 Mesh 를 사용하고 있는지 여부 
 +var   SkeletalMesh   GoreSkeletalMesh;              // Gore 연출 시 사용할 SkeletalMesh 
 +var   PhysicsAsset   GorePhysicsAsset;              // Gore 연출 시 사용할 PhysicsAsset 
 +var   Array<MorphTargetSet>  GoreMorphSets;         // Gore 연출 시 사용할 MorphSets 
 + 
 +var   Array<Name>    GoreBreakableJoints;           // Gore 연출 시 쪼갤 뼈대 이름을 담은 배열 
 +var   Array<Name>    HostageHealthBuckets;          // Hostage 상태일 때, 각 파츠가 떨어지는 Gore 효과를 위해 Health 를 가지는 뼈대 이름을 담은 배열 
 + 
 +struct native DependantBreakInfo 
 +
 +    var  Name            ParentBone;       // 부모 Bone 
 +    var  Array<Name>     DependantBones;   // 부모 Bone 이 절단될 때, 같이 절단되어야 하는 Bone 리스트 
 +}; 
 +var   Array<DependantBreakInfo>  JointsWithDependantBreaks;  // 덩달아 쪼개질 Bone 정보 리스트
 </code> </code>
   * 간접적인 변수<code cpp>   * 간접적인 변수<code cpp>
 +// GearPawn.uc
 +struct native PhysicsImpactRBRemap
 +{
 +    var()   Name      RB_FromName;     // Impulse 가 들어온 Bone
 +    var()   Name      RB_ToName;       // Impulse 를 적용시킬 Bone
 +};
 +var(HitReactions) editinline Array<PhysicsImpactRBRemap>   PhysicsImpactRBRemapTable;    // 특정 Bone 은 Impulse 에 좋게 반응하지 못한다. 때문에 더 나은 비쥬얼 결과를 얻기 위해 다른 Bone 으로 Impulse 를 전가시킬 필요가 있다.
 +var(HitReactions)            float                         PhysicsImpactMassEffectScale; // 질량에 따른 Impact 를 적용할 때 scale 되는 값
 +
 // DamageType.uc // DamageType.uc
-var(RigidBody)             float       kDeathUpKick;         // 죽었을 경우 위로 솟아오르는 힘+var(RigidBody)             float       KDamageImpulse;       // 이 데미지가 KActor 에 전가하는 impulse 세기 
 +var(RigidBody)             float       KDeathUpKick;         // 죽었을 경우 위로 솟아오르는 힘
  
 // GearDamageType.uc // GearDamageType.uc
-var config const           float      DistFromHitLocToGib;   // HitLocation 으로터 HitDirection 으로 이 길이만큼 실린더를 만들고, 그 사이에 있는 constraints 를 모두 깨버린다. 이 값의 default 는 -1 로 body 를 두 깨숴버린다.+var config const           float      DistFromHitLocToGib;   // 절단 를 계산하기 위한 Damage Cylinder 의 반지름. 이 Damage Cylinder 영역 안에 있는 절단부위는 모두 쪼개뜨리게 된다. 이 값의 default 는 -1 로 모든 절단위를 쪼갠다.
 var const config           bool       bAllowHeadShotGib;     // 이 무기로 머리를 맞출 때, 뽀개지는 (Gore) 연출을 할까? var const config           bool       bAllowHeadShotGib;     // 이 무기로 머리를 맞출 때, 뽀개지는 (Gore) 연출을 할까?
 </code> </code>
  
-===== 함수 =====+==== 함수 ====
 <code cpp> <code cpp>
 // SkeletalMeshComponent.uc // SkeletalMeshComponent.uc
줄 108: 줄 131:
 </code> </code>
  
-===== 코드 플로우 =====+==== 코드 플로우 ====
 로커스트 그런트를 예로 설명 로커스트 그런트를 예로 설명
  
-==== 초기화 ====+=== 사전작업 === 
 +  - AnimSetViewer -> SkeletonTree -> 절단할 노드에서 마우스 우클릭 후 Calculate Bone Break Vert Weightings<code cpp> 
 +// AnimSetViewerTools.cpp 
 +void WxAnimSetViewer::OnSkeletonTreeMenuHandleCommand( wxCommandEvent &In ) 
 +
 +    ... 
 +    else if ( Command == IDM_ANIMSET_SKELETONTREE_CALCULATEBONEBREAKS ) 
 +    { 
 +        ... 
 +        if ( BoneIndexPairs.Num() > 0 ) 
 +        { 
 +            // BoneIndexPairs ( Parent & Child ) 에 모두 영향을 받는 Vertices 를 색출해 내어 BonePairs to VertList 로 매핑해 둔다. 
 +            CalculateBoneWeightInfluences( PreviewSkelComp->SkeletalMesh, BoneIndexPairs ); 
 +            ... 
 +        } 
 +        ... 
 +    } 
 +    ... 
 +
 +</code> 
 +  - Parent & Child 모두 영향을 받는 Vertices 를 색출<code cpp> 
 +// AnimSetViewerTools.cpp 
 +void CalculateBoneWeightInfluences( USkeletalMesh* SkeletalMesh, TArray<FBoneIndexPair>& BonePairs ) 
 +
 +    // SkeletalMesh 의 모든 LOD 를 순회하면서 BonePairs 에 있는 Parent & Child 에 모두 스키닝 되어 있는 Vertex 를 리스트에 담아 
 +    // VertexInfluences.VertexInfluenceMapping 에 BonePairs to VertList 로 담아둔다. 
 +
 +</code> 
 + 
 +=== 초기화 ===
 <code cpp> <code cpp>
 // GearPawn_LocustDroneBase.uc // GearPawn_LocustDroneBase.uc
줄 126: 줄 178:
 </code> </code>
  
-==== 톱질 ====+=== 톱질 === 
 +  - 죽음 발동<code cpp> 
 +// 죽으면 호출되는 이벤트 함수 
 +simulated function PlayDying( class<DamageType> DamageType, vector HitLoc ) 
 +
 +    ... 
 +    if ( WorldInfo.NetMode !NM_DedicatedServer ) 
 +    { 
 +        PlayDeath( GearDamageType, HitLoc );  // 죽음 처리를 시작 
 +    } 
 +
 +</code>
   - 죽음 처리<code cpp>   - 죽음 처리<code cpp>
 // GearPawn.uc // GearPawn.uc
줄 141: 줄 204:
             if ( !bIsGore && GoreSkeletalMesh != None && GorePhysicsAsset != None )             if ( !bIsGore && GoreSkeletalMesh != None && GorePhysicsAsset != None )
             {             {
-                CreateGoreSkeleton( GoreSkeletalMesh, GorePhysicsAsset ); // 고어적 연출을 하기 위해 SkeletalMesh 를 미리 잘라놓은 것으로 교체한다.+                CreateGoreSkeleton( GoreSkeletalMesh, GorePhysicsAsset ); // 고어적 연출을 하기 위해 특수 제작된 SkeletalMesh 와 PhysicsAsset 으로 교체한다.
             }             }
                          
줄 152: 줄 215:
 } }
 </code> </code>
-  - 절단 시작<code cpp>+    * 메시 교체<code cpp> 
 +// GearPawn.uc 
 +simulated final function CreateGoreSkeleton( SkeletalMesh TheSkeletalMesh, PhysicsAsset ThePhysicsAsset ) 
 +
 +    // 교체할 고어메시가 없거나 고어표현을 하면 안되는 게임환경 (청소년 등급) 이라면 리턴 
 +    ... 
 +     
 +    // 죽음 이벤트가 발동 되었다면 에니메이션을 모두 정지한다. 
 +    ... 
 +     
 +    // 이전 메시의 Attachment 를 백업 
 +    // 고어 Material 을 위해 기존 Material 클리어 
 +    // SkeletalMesh 교체 
 +    // MorphTargets 교체 
 +    // PhysicsAsset 교체 
 +    // CastShadow 속성 제거. 퍼포먼스 위해 
 +    // soft weighted 메시를 사용하지 않는다면 모든 부위를 hard weight 로 처리 
 +    // 이전 메시의 Attachment 를 적용 
 +    // 바운딩 박스가 바뀌었으므로 Encroach 체크를 활성화 한다. 
 +    // PhysicsAsset 을 unfixed 상태로 한다. 
 +
 +</code> 
 +    * 절단 이벤트<code cpp>
 // GearPawn.uc // GearPawn.uc
 simulated function GoreExplosion( Vector Momentum, Vector HitLocation, class<GearDamageType> GearDamageType, optional bool bRandomizeGibImpluse ) simulated function GoreExplosion( Vector Momentum, Vector HitLocation, class<GearDamageType> GearDamageType, optional bool bRandomizeGibImpluse )
줄 166: 줄 251:
 } }
 </code> </code>
-  - 쪼개짐 시작 호출<code cpp>+  - 특정 부위 절단 호출<code cpp>
 // GearPawn_COGMarcus.uc // GearPawn_COGMarcus.uc
 simulated function ChainSawGore() simulated function ChainSawGore()
줄 182: 줄 267:
     Mesh.BreakConstraint( Impulse, HitLocation, InBoneName, bVelChange );     Mesh.BreakConstraint( Impulse, HitLocation, InBoneName, bVelChange );
  
-    // InBoneName 에 의존되어 있는 관련 Bone의 Constraint 를 모두 해제+    // 덩달아 쪼갤 Bone 의 Constraint 를 해제
     BreakDependantBones( Impulse, HitLocation, InBoneName );     BreakDependantBones( Impulse, HitLocation, InBoneName );
     ...     ...
 +}
 +</code>
 +    * 관련 Bone의 Constraint 모두 해제<code cpp>
 +// GearPawn.uc
 +struct native DependantBreakInfo
 +{
 +    var Name   ParentBone;           // 아래 배열에 포함된 Bone들이 존속된 부모Bone 이름
 +    var Array<Name> DependantBones;  // 부모Bone 이 쪼개질 때, 같이 쪼개져야 하는 Bone 리스트
 +};
 +var Array<DependantBreakInfo>  JointsWithDependantBreaks;    // 내가 쪼개질 때, 같이 쪼개질 Bone들의 정보를 담은 배열
 +
 +simulated final function BreakDependantBones( Vector Impulse, Vector HiLocation, Name InBoneName, optional bool bVelChange )
 +{
 +    for ( i = 0; i < JointsWithDependantBreaks.length; i++ )
 +    {
 +        if ( JointsWithDependantBreaks[i].ParentBone == InBoneName )
 +        {
 +            for ( j = 0; j < JointsWithDependantBreaks[i].DependantBones.length; J++ )
 +            {
 +                // InBoneName 에 의존적인 모든 Bone 의 Constraint 역시 해제
 +                BreakConstraint( Impulse, HitLocation, JointsWithDependantBreaks[i].DependantBones[j], bVelChange );
 +            }
 +        }
 +    }
 } }
 </code> </code>
줄 196: 줄 305:
     Constraint.TermConstraint();         // 해당부위 Constraint 를 제거     Constraint.TermConstraint();         // 해당부위 Constraint 를 제거
     UpdateMeshForBrokenConstraints();    // 쪼개진 부위에 대하여 Mesh 의 PhysicsAsset 업데이트     UpdateMeshForBrokenConstraints();    // 쪼개진 부위에 대하여 Mesh 의 PhysicsAsset 업데이트
 +    AddImpulse( ... );                   // 쪼개진 부위에 Impulse 적용
 } }
 </code> </code>
-    Instance Vertex Weights 토글<code cpp>+    Instance Vertex Weights 토글<code cpp>
 // UnSkeletalComponent.cpp // UnSkeletalComponent.cpp
 void USkeletalMeshComponent::ToggleInstanceVertexWeights( UBOOL bEnabled ) void USkeletalMeshComponent::ToggleInstanceVertexWeights( UBOOL bEnabled )
줄 204: 줄 314:
     ...     ...
     bNeedsInstanceWeightUpdate = TRUE;   // 추후 USkeletalMeshComponent::Tick 에서 UpdateInstanceVertexWeights() 를 호출하게끔 한다.     bNeedsInstanceWeightUpdate = TRUE;   // 추후 USkeletalMeshComponent::Tick 에서 UpdateInstanceVertexWeights() 를 호출하게끔 한다.
 +    InstanceVertexWeightBones.Empty();   // 이후에 이곳에 절단될 Bone Pair 이름이 추가될 것이다.
     ...     ...
 } }
 </code> </code>
-    쪼개질 Bone 을 추가<code cpp>+    쪼개질 Bone 을 추가<code cpp>
 // UnSkeletalComponent.cpp // UnSkeletalComponent.cpp
 void USkeletalMeshComponent::AddInstanceVertexWeightBoneParented( FName BoneName, UBOOL bPairWithParent ) void USkeletalMeshComponent::AddInstanceVertexWeightBoneParented( FName BoneName, UBOOL bPairWithParent )
줄 216: 줄 327:
 } }
 </code> </code>
-    - USkeletalMeshComponent::Tick<code cpp>+  - USkeletalMeshComponent::Tick<code cpp>
 // UnSkeletalComponent.cpp // UnSkeletalComponent.cpp
 +void USkeletalMeshComponent::Tick( FLOAT DeltaTime )
 +{
 +    ...
 +    if ( bNeedsInstanceWeightUpdate )
 +    {
 +        UpdateInstanceVertexWeights();   // GoreSkel_SoftWeighted 의 절단된 부위 vertex weight 값을 완전히 절단된 GoreSkel 의 것으로 교체작업을 시작한다.
 +    }
 +    ...
 +}
 +
 void USkeletalMeshComponent::UpdateInstanceVertexWeights() void USkeletalMeshComponent::UpdateInstanceVertexWeights()
 { {
줄 227: 줄 348:
             const UBOOL bResetWeights = TRUE;             const UBOOL bResetWeights = TRUE;
                          
-            // Bone 과 Parent 둘다 영향을 받는 Vertex 들의 weight 값을 다시 계산하도록 RenderingThread 에 command 전달.+            // Bone 과 Parent 둘다 영향을 받는 (절단 부위) Vertex 들의 weight 값 교체
             MeshObject->UpdateVertexInfluences( BoneIndexPairs, 0, bResetWeights );             MeshObject->UpdateVertexInfluences( BoneIndexPairs, 0, bResetWeights );
         }         }
줄 235: 줄 356:
         }         }
                  
-        bNeedsInstanceWeightUpdate = FALSE;   // 처리했으니 플래그 off, 이 함수가 이 값이 true 이어야 호출되는 함수.+        bNeedsInstanceWeightUpdate = FALSE;   // 처리했으니 플래그 off
     }     }
 } }
 </code> </code>
-  - 관련 Bone의 Constraint 모두 해제<code cpp> +  - 절단 부위 Vertex 들의 weight 값 교체<code cpp> 
-// GearPawn.uc +// UnSkeletalRenderGPUSkin.cpp 
-struct native DependantBreakInfo+void FSkeletalMeshObjectGPUSkin::UpdateVertexInfluences( 
 +    const TArray<FBoneIndexPair>& BonePairs, 
 +    INT InfluenceIdx, 
 +    UBOOL bResetInfluences )
 { {
-    var Name   ParentBone;           // 아래 배열에 포함된 Bone들이 존속된 부모Bone 이름 +    ... 
-    var Array<Name> DependantBones;  // 부모Bone 이 쪼개질 때, 같이 쪼개져야 하는 Bone 리스트 +    // 교체 작업을 RenderingThread 에 command 로 전달
-}; +
-var Array<DependantBreakInfo>  JointsWithDependantBreaks;    // 내가 쪼개질 때, 같이 쪼개질 Bone들의 정보를 담은 배열 +
- +
-simulated final function BreakDependantBones( Vector Impulse, Vector HiLocation, Name InBoneName, optional bool bVelChange ) +
-+
-    for ( i = 0; i < JointsWithDependantBreaks.length; i++ ) +
-    { +
-        if ( JointsWithDependantBreaks[i].ParentBone == InBoneName ) +
-        { +
-            for ( j = 0; j < JointsWithDependantBreaks[i].DependantBones.length; J++ ) +
-            { +
-                // InBoneName 에 의존적인 모든 Bone 의 Constraint 역시 해제 +
-                BreakConstraint( Impulse, HitLocation, JointsWithDependantBreaks[i].DependantBones[j], bVelChange ); +
-            } +
-        } +
-    }+
 } }
 </code> </code>
  
-====== 피 ======+===== 피 =====
 {{http://farm4.static.flickr.com/3458/3215812168_36d1b754c5_o.jpg}} {{http://farm4.static.flickr.com/3458/3215812168_36d1b754c5_o.jpg}}
  
줄 275: 줄 383:
 기본적으로는 엔진이 지원하는 Emitter 와 Decal 을 사용하지만 GoW2 에서는 좀 더 최적화된 모습으로 이들을 처리하고 있다. 기본적으로는 엔진이 지원하는 Emitter 와 Decal 을 사용하지만 GoW2 에서는 좀 더 최적화된 모습으로 이들을 처리하고 있다.
  
-===== 변수 =====+==== GearPawnFX ==== 
 +GearPawnFX 는 **고어적 표현에 사용되는 피<sub>blood</sub>를 편하고 효과적이고 최적화된 방법으로 남기기 위해 고안된 클래스** 이다.
  
-===== 함수 ===== +게임 내에서 사용되는 모든 핏자국은 이것을 통하여 남는다고 봐도 된다. 
-<code cpp>+ 
 +=== 함수 === 
 +  * 데칼을 남기는 핵심 함수<code cpp>
 // GearPawnFX.uc // GearPawnFX.uc
-이 클래스는 캐릭터가 를 흘리는 다양한 상황에 대한 처리를 담당하고 있다. +simulated final funciton LeaveADecal( 
-DBNO 시 면에 피가 남는 Decal 처리 +    delegate<DecalTrace>              DecalTraceFunc,                   // Trace 할 시작과 끝 위치를 구하는 함수의 delegator 
-MeatBag? 시 지면에 피가 남는 Decal 처리 +    delegate<DecalChoice>             DecalChoiceFunc,                  // 어떠한 Decal 을 사용할 것인가를 결정하는 함수의 delegator 
-치명상인 상태로 Cover 이동 시 벽에 피가 남는 Decal 처리+    delegate<DecalTimeVaryingParams>  DecalTimeVaryingParamsFunc,       // 시간의 흐름에 따라 수정해야 할 파라메터, 예를 들어 fade in/out, 시간에 민감한 효과 등 
 +    optional Vector                   ForceStartLocation ) 
 +
 +    // DecalChoiceFunc 함수로 사용할 Decal 을 선택한다. 
 +    ... 
 +     
 +    // DecalTraceFunc 함수로 Trace 의 작,끝 위치를 구한다. 
 +    ... 
 +     
 +    // 실제 Trace 함수 수행 
 +    ... 
 +     
 +    // Trace 결과 무언가 hit 했다면 그곳에 Decal 을 남긴다. 
 +    if ( TraceActor != None ) 
 +    { 
 +        ... 
 +        GD = GearGRI( WorldInfo.GRI ).GOP.GetDecal_Blood( out_HitLocation );  // GearObjectPool 서 GearDecal 하나를 꺼낸다. 
 +        if ( GD != None ) 
 +        { 
 +            // 절절하게 Parameter 를 세팅하고 TraceActor 에 Attach 한다. 
 +            ... 
 +        } 
 +    } 
 +}
 </code> </code>
 +  * LeaveADecal 의 파라메터로 넘어가는 delegator
 +    * DecalTrace<code cpp>
 +// GearPawnFX.uc
 +
 +// Tracing 의 시작/끝 위치를 구하는 함수 원형
 +simulated delegate DecalTrace( out vector out_TraceStart, out vector out_TraceDest, const float RandomOffsetRadius, optional vector ForceStartLocation )
 +{
 +    `log( "DecalTrace Delegate was not set" );
 +    ScriptTrace();
 +}
 +
 +// 실제 구현이 담긴 함수들
 +simulated final function BloodDecalTrace_TraceGibImpact( ... );                // 현재 위치에서 땅을 향하는 시작/끝 위치를 획득
 +simulated final function BloodDecalTrace_FromNeck( ... );                      // 목으로부터 대각선 아래를 향하는 시작/끝 위치를 획득
 +simulated final function BloodDecalTrace_FromNeckForFacePunch( ... );          // 죽빵을 날렸을 때 땅에 남길 피의 위치를 구하는 용도? 암튼 위와 거의 비슷
 +simulated final function BloodDecalTrace_GroundBelowThePawn( ... );            // Pawn 으로부터 그 아래 바닥으로 향하는 시작/끝 위치를 획득
 +simulated final function BloodDecalTrace_GroundBelowThePawn_Rand( ... );       // 바로 위의 Random 버전
 +simulated final function BloodDecalTrace_360AroundPawn_Forward( ... );         // Pawn 으로부터 앞을 향하는 시작/끝 위치 획득
 +simulated final function BloodDecalTrace_360AroundPawn_Left( ... );            // Pawn 으로부터 왼쪽을 향하는 시작/끝 위치 획득
 +simulated final function BloodDecalTrace_360AroundPawn_Right( ... );           // Pawn 으로부터 오른쪽을 향하는 시작/끝 위치 획득
 +simulated final function BloodDecalTrace_360AroundPawn_Backward( ... );        // Pawn 으로부터 뒤를 향하는 시작/끝 위치 획득
 +simulated final function BloodDecalTrace_360AroundPawn_Up( ... );              // Pawn 으로부터 위를 향하는 시작/끝 위치 획득
 +simulated final function BloodDecalTrace_360AroundPawn_Down( ... );            // Pawn 으로부터 아래를 향하는 시작/끝 위치 획득
 +simulated final function BloodDecalTrace_GroundBelowTheWeaponEnd_Rand( ... );  // 총구로부터 지면을 향하는 시작/끝 위치를 획득
 +simulated final function BloodDecalTrace_CoverBehindPawnMiddleOfBody( ... );   // Pelvis 부터 Cover 를 향하는 시작/끝 위치를 획득
 +simulated final function BloodDecalTrace_TraceFromEndOfChainsaw( ... );        // 총구로부터 총구방향을 향하는 시작/끝 위치를 획득
 +</code>
 +    * DecalChoice<code cpp>
 +// GearPawnFX.uc
 +
 +// 사용할 Decal 을 선택하는 함수 원형
 +simulated delegate DecalChoice( const out TraceHitInfo HitInfo, out float out_DecalRotation, out DecalData out_DecalData )
 +{
 +    `log( "DecalChoice Delegate was not set" );
 +    ScriptTrace();
 +}
 +
 +// 실제 구현이 담긴 함수들
 +simulated final function BloodDecalChoice_HeadShot( ... );                 // 헤드샷 시 뿜어져 나오는 Decal 선택
 +simulated final function BloodDecalChoice_Splat( ... );                    
 +simulated final function BloodDecalChoice_GibImpact( ... );                
 +simulated final function BloodDecalChoice_ChainsawSpray_Wall( ... );       // Chainsaw 액션 시, 벽에 남는 Decal 선택
 +simulated final function BloodDecalChoice_ChainsawSpray_Ground( ... );     // Chainsaw 액션 시, 땅에 남는 Decal 선택
 +simulated final function BloodDecalChoice_GibExplode_Ground( ... );        // 신체절단이 일어날 때 땅에 남는 Decal 선택
 +simulated final function BloodDecalChoice_Wall( ... );
 +simulated final function BloodDecalChoice_DBNO( ... );                     // DBNO 시, 땅에 남는 Decal 선택
 +simulated final function BloodDecalChoice_MeatBag( ... );
 +simulated final function BloodDecalChoice_MeatBagHeelScuff( ... );
 +simulated final function BloodDecalChoice_BloodPool( ... );
 +simulated final function BloodDecalChoice_GibExplode_Ground_SmallSplat( ... );
 +simulated final function BloodDecalChoice_PunchFace( ... );
 +simulated final function BloodDecalChoice_PawnIsReallyHurt( ... );         // 열라 아플 때?
 +simulated final function BloodDecalChoice_HitByBullet( ... );              // 총알 맞았을 때?
 +simulated final function BloodDecalChoice_LimbBreak( ... );
 +</code>
 +    * DecalTimeVaryingParams<code cpp>
 +// GearPawnFX.uc
 +
 +// Decal 에 대한 시간을 조절하는 함수 원형
 +simulated delegate DecalTimeVaryingParams( out MaterialInstance MI_Decal )
 +{
 +    `log( "DecalTimeVaryingParams Delegate was not set" );
 +    ScriptTrace();
 +}
 +
 +// 실제 구현이 담긴 함수들
 +simulated final function BloodDecalTimeVaryingParams_Default( ... );        // 일반적인 타임 세팅
 +simulated final function BloodDecalTimeVaryingParams_DBNO( ... );           // DBNO 시, 타임 세팅
 +simulated final function BloodDecalTimeVaryingParams_Wall( ... );           // 벽에 뭍는 Decal 에 대한 타임 세팅
 +</code>
 +  * LeaveADecal 을 사용하는 응용 함수들<code cpp>
 +// GearPawnFX.uc
 +
 +// DBNO 시, blood trail 효과를 남긴다.
 +simulated final function SpawnABloodTrail_DBNO()
 +{
 +    if ( WorldInfo.GRI.ShouldShowGore() )
 +    {
 +        // DBNO 상태에서 움직이면
 +        if ( VSize( Velocity ) > 10.f )
 +        {
 +            LeaveADecal( BloodDecalTrace_GroundBelowThePawn, BloodDecalChoice_DBNO, BloodDecalTimeVaryingParams_DBNO ); // 바닥에 Decal 을 남긴다.
 +        }
 +    }
 +}
 +
 +// 위와 같은 식으로 LeaveADecal 을 사용하는 함수들
 +simulated final function SpawnABloodTrail_MeatBag();
 +simulated final function SpawnABloodTrail_Wall();                   // Cover 에 있을 시 blood trail 효과를 남긴다.
 +simulated final function SpawnABloodTrail_GibExplode_Ground();      // 시체 폭파 시 지면에 blood trail 효과를 남긴다.
 +simulated final function SpawnABloodTrail_GibExplode_360();         // 시체 폭파 시 사방에 blood trail 효과를 남긴다.
 +simulated final function SpawnABloodTrail_ChainsawSpray_Ground();   // Chainsaw 액션 시 땅에 blood trail 효과를 남긴다.
 +simulated final function SpawnABloodTrail_ChainsawSpray_Wall();     // Chainsaw 액션 시 벽에 blood trail 효과를 남긴다.
 +simulated final function SpawnABloodTrail_GibImpact( ... );
 +simulated final function SpawnABloodTrail_HeadShot();               // 헤드샷 blood trail 효과를 남긴다.
 +simulated final function SpawnABloodTrail_BloodPool();
 +simulated final function SpawnABloodTrail_PawnIsReallyHurt();       // 열라 아플 시 blood trail 효과를 남긴다.
 +simulated final function SpawnABloodTrail_HitByABullet();           // 총알에 맞을 시 blood trail 효과를 남긴다.
 +simulated final function SpawnABloodTrail_LimbBreak( ... );
 +</code>
 +
 +=== 코드 플로우 ===
 +GearPawnFX 를 사용하는 주체에 특별한 제약은 없지만 보통 GearPawn 에 의해 함수가 호출이 된다. GearPawnFX 는 within GearPawn 키워드로 감싸져 있어 GearPawn 내부에서 사용되는 것이 의도되어 있다.
 +
 +코드 플로우를 정확하게 이해하려면 GearPawnFX.uc 이외에도 [[GearsOfWar2:GearObjectPool]] 을 이해해야 한다.
 +
 +전체적인 플로우는 다음과 같다. (하나의 예시, 이것 말고도 다양한 코드 흐름이 나올 수 있다.)
 +  - GearPawn 에서 GoreExplosion 함수 발동 시, 사방에 피를 남기기 위해 GearPawnFX 를 이용한다.
 +  - GearPawnFX 는 뿌릴 (물론 방향 및 타임설정까지 정하고) Decal 을 고르고, GearObjectPool 에서 껍데기 GearDecal 을 가져온 후 그곳에 Decal 정보를 넣는다.
 +  - GearDecal 을 남길 곳에 Attach 하고 마무리 짓는다.
 +
 +한눈에 흐름을 잡기 위한 이미지
  
-===== 코드 플로우 =====+[[http://farm6.static.flickr.com/5165/5345756208_17196d8ab6_o.jpg|{{http://farm6.static.flickr.com/5165/5345756208_c08f0eea8b_b.jpg}}]]
  
-====== 참조 ======+===== 참조 =====
 GoW2 소스 GoW2 소스
  
 {{tag>개발 프로그래밍 엔진 언리얼엔진 잔혹}} {{tag>개발 프로그래밍 엔진 언리얼엔진 잔혹}}