목차
캐릭터
UE3를 활용하여 자신의 프로젝트에 맞는 캐릭터를 구현하기에 앞서 UE3에 이미 구현되어 있는 캐릭터 작동 방식을 분석하기로 한다. 그리하여 캐릭터 구현에 필요한 기능이 중복구현됨을 막고 효율적으로 UE3에 통합될 수 있도록 한다.
여기서 살펴볼 내용은 Core기능에 해당하는 AnimTree, AnimSet, Morph 등의 기능이 아니라, 로직에 해당하는 Pawn, PlayerController, PlayerReplicationInfo 등의 오버뷰와 이들이 엔진 내 어떤 포지션에 해당하는지를 중점으로 살펴본다.
캐릭터 Bone 구조
분석 요소
UE3에서 캐릭터를 구현하는데 필요한 요소들을 살펴본다.
Pawn
- 특징
- Actor→Pawn 상속
- player 나 AI 가 컨트롤하기 위한 Base Actor 클래스에 해당한다.
- mesh, collision, physics 등의 기능이 있고, 데미지를 주며 소리를 내고 무기나 다른 소지품을 들고 다니며, 월드상 다른 Pawn 과의 물리적 인터렉션 등을 담당한다.
- 기능
- 캐릭터가 돌아 다닐 수 있는 floor 에 대한 성격을 정의한다.
- Pawn 이 서있는 지면의 Normal ( Floor, PHYS_Spider, PHYS_Walking 에서만 쓰인다. )
- 올라갈 수 있는 floor 높이를 정의. ( MaxStepHeight, MaxJumpHeight )
- 내려갈 수 있는 floor 높이를 정의. ( LedgeCheckThreshold )
- 이동 가능한 floor 경사면 값 지정. ( WalkableFloorZ, 이는 floor 의 normal 벡터와 UpVector 를 내적한 스칼라 값 이며 Pawn은 기본적으로 0.7 (~= 45 degree) 로 초기화 되어 있다. )
- 상기 3가지 사항은 캐릭터 이동 및 PathFinding 등에 활용된다.
- 모든 Pawn을 링크드리스트로 관리. ( NextPawn, 월드 상 모든 Pawn 순회에 용이하다. )
- Crouch 관련 정보 정의 가능
- 웅크릴 수 있다. ( bCanCrouch )
- 웅크리길 원한다. ( bWantsToCrouch )
- 웅크린 상태이다. ( bIsCrouched )
- 다시 일어서고 싶다. ( bTryToUncrouch )
- Crouch 시, 실린더 정보 ( CrouchHeight, CrouchRadius )
- 웅크리기 & 일어서기 이벤트 함수 제공.
// APawn::performPhysics 로부터 호출 void APawn::Crouch(INT bClientSimulation) { ... } void APawn::UnCrouch(INT bClientSimulation) { ... }
- 이동 목적지에 다다르면 smooth하게 속도를 줄이면서 멈춘다. ( bReducedSpeed )
- 여러 행위들에 대한 플래그
- 점프기능이 있다. ( bJumpCapable )
- 점프할 수 있다. ( bCanJump )
- 데미지 입을 수 있다. ( bCanBeDamaged )
- 웅크릴 수 있다. ( bCanCrouch )
- 날 수 있다. ( bCanFly )
- 수영할 수 있다. ( bCanSwim )
- 텔레포트할 수 있다. ( bCanTeleport )
- 걸을 수 있다. ( bCanWalk )
- 사다리에 오를 수 있다. ( bCanClimbLadders )
- 횡이동 할 수 있다. ( bCanStrafe )
- 이 밖에 다양한 플래그 들 ( bAvoidLedges, bStopAtLedges, bAllowLedgeOverhang, bSimulatedGravity, bIgnoreForces, bCanWalkOffLedges, bCanBeBaseForPawns, bSimGravityDisabled, bDirectHitWall, bPushesRigidBodies, bForceFloorCheck, bForceKeepAnchor )
- 곧 Pawn 에서 제거될 것 같은? 플래그 들 ( bCanMantle, bCanClimbUp, bCanClimbCeilings, bCanSwatTurn, bCanLeap, bCanCoverSlip )
- AI 관련 변수
- 성별 ( bIsFemale )
- 아이템을 집을 수 있다. ( bCanPickupInventory )
- AI가 날 무시하게 할 수 있다. ( bAmbientCreature )
- 소리를 들을 수 있다. ( bLOSHearing )
- 벽을 통해 들려오는 숨죽인 소리를 들을 수 있다. ( bMuffledHearing )
- 게임 시작 시 controller 에 의해 소유되지 않도록 한다. vehicle 들은 게임 시작 시 controller 에 의해 선점되면 안되겠지.. ( bDontPossess )
- 난 움직일 수 없다. ( bStationary )
- 무기 사용할 수 없다. ( bNoWeaponFiring )
- 뭔가를 사용할 수 있다. ( bCanUse )
- 이 밖에 다양한 플래그 들 ( bModifyReachSpecCost, bModifyNavPointDest, bPathfindingsAsVehicle, )
- Path Finding 관련
- 내가 길을 찾는 방법 ( PathSearchType )
- 기타 관련 변수 들 ( PathConstraintList, PathGoalList )
- 들을 수 있는 거리 ( HearingThreshold )
- 소리를 들을 수 있는 각성도 ( Alertness, -1 ~ 1 사이의 값으로써 높을 수록 소리를 더 잘 들을 수 있다. )
- 시야 거리 ( SightRadius )
- 시야 각 ( PeripheralVision, degree 의 cosine 값, default 로 -0.75 ~= 140 도 )
- 물리적 업데이트할 모니터링 시간 ( AvgPhysicsTime, AI 가 목적지로 가기 위해 업데이트할 시간? 구불구불하게 가거나 똑바로 가게 하는 등의 처리를 위한 변수 )
- 이동 관련 속성
- 희망 무브먼트 속도 ( DesiredSpeed & MaxDesiredSpeed, GroundSpeed 에 곱해지므로 실질적으로 Pawn 의 전체적 무브먼트 스피드라고 보아도 무방 )
// Pawn.uc defaultproperties { ... DesiredSpeed=+00001.00000 ... } // UnController.cpp void AController::MoveTo( ... ) { ... Pawn->DesiredSpeed = Pawn->MaxDesiredSpeed; ... } void AController::MoveToward( ... ) { ... Pawn->DesiredSpeed = Pawn->MaxDesiredSpeed; ... } // UnPhysic.cpp FLOAT APawn::MaxSpeedModifier() { ... if ( !IsHumanControlled() ) { Result *= DesiredSpeed; // 사람이 조종하지 않는 녀석에 한해서만 적용 } ... }
- 가장 가까운 path ( Anchor )
- nav mesh 인덱싱 ( AnchorItem, 헌데 사용되지는 않음 )
- 최근에 도달한 가까운 path ( LastAnchor )
- 마지막 path finding 실패 시기 ( FindAnchorFailedTime, FindPath() 함수 시도가 실패한 마지막 시간 )
- 마지막 유효 path 발견 시기 ( LastValidAnchorTime )
- 도착점 offset ( DestinationOffset )
- 루트상 다음 지점의 반지름 ( NextPathRadius )
- 구불구불 이동
- 방향 ( SerpentineDir )
- 거리 ( SerpentineDist )
- 시간 ( SerpentineTime, 구불구불 이동 시 횡이동 시도하기 전까지 직진할 시간 )
- 물리?
- Pawn 의 질량 ( Mass )
// Pawn.uc function CrushedBy( Pawn OtherPawn ) { TakeDamage( ( 1 - OtherPawn.Velocity.Z / 400 ) * OtherPawn.Mass / Mass, // 데미지, 위에서 내리누를 때 상대방과의 질량에 대비하여 데미지를 가감하는 용도. OtherPawn.Controller, // 유발자 Controller Location, // HitLocation vect( 0, 0, 0 ), // Momentum class'DmgType_Crushed' ); // 데미지 타입 } event TakeDamage( ... ) { ... momentum = momentum / Mass; // 데미지가 가해질 때 전해진 충격량으로부터 움직일 속도를 구함. F=ma -> a=F/m ... }
- 수영할 때 적용할 물 부양성 ( Buoyancy, 1=자연스러운 부양성 0=no부양성 )
// UnPhysic.cpp void APawn::CalcVelocity( ... ) { ... if ( bBuoyant ) { Velocity.Z += GetGravityZ() * DeltaTime * ( 1.f - Buoyancy ); // Buoyancy 가 0 이면 중력을 그대로 적용한다. } ... }
- 밀리어택 최대거리 ( MeleeRange, 일반적인 밀리어택이 아니라 이동하는 도중 목적지까지의 거리를 가늠하는데 사용하는 것 같음 )
- 일반
- 스폰 시간 ( SpawnTime, 스폰 후 일정시간동안 데미지 감소따위를 하는 데 사용. 관련변수 UTGame.SpawnProtectionTime )
- view pitching 제한 ( MaxPitchLimit )
- 무브먼트
- controller 없이도 physics 돌려라~ ( bRunPhysicsWithNoController, acceleration 이 아닌 velocity 에 의해서만 움직이게 되겠다. )
- 풀 악셀 ( bForceMaxAccel, 기존 acceleration 무시하고 풀악셀로 최대 velocity 를 이끌어 낸다. )
- 최대 지형 이동 속도 ( GroundSpeed )
- 최대 수영 이동 속도 ( WaterSpeed )
- 최대 활강 이동 속도 ( AirSpeed )
- 최대 등반 이동 속도 ( LadderSpeed )
- 가속 비율 ( AccelRate, 이것이 곱해져 acceleration 을 구한다. )
- 점프 속도 ( JumpZ, 수직 up 방향 속도 )
- 수중 이탈 속도 ( OutofWaterZ, 점프로 물 밖으로 이탈할 때 z up 방향 속도. 물 근처 난간위로 올라가는 것을 보장하기 위해 설정하는 값인듯 )
// Pawn.uc function JumpOutOfWater( vector jumpDir ) { ... velocity.Z = OutofWaterZ; // set here so physics uses this for remainder of tick ... }
- 수중 이탈 가능 높이 ( MaxOutOfWaterStepHeight, 수영하며 수중이탈 가능한올라갈 수 있는 높이 )
- 낙하 최대 가속도 제한할까? ( bLimitFallAccel )
- 공중에서의 컨트롤 시간 factor ( AirControl, acceleration 을 이 시간만큼 적용한 후 테스트하는 용도 )
// UnPhysic.cpp void APawn::physFalling( FLOAT deltaTime, INT Iterations ) { ... if ( !bDoRootMotion && TickAirControl > 0.05f ) { // 현재 velocity 에 TickAirControl 시간만큼 경과 후 delta velocity 까지 더한 후 이동거리를 체크한다. FVector TestWalk = ( TickAirControl * AccelRate * Acceleration.SafeNormal() + Velocity ) * deltaTime; TestWalk.Z = 0.f; ... // 이후는 현재 Location 으로부터 TestWalk 만큼 이동한 곳에 특정 world 오브젝트가 있는지 (지형 포함) 체크한다. } ... }
- 걷기&웅크리기 속도 퍼센티지 ( WalkingPct & CrouchedPct, 기본 이동 속도에 곱하여 걷기속도 및 웅크리기속도를 구하는 방식에 사용 )
// UnPhysic.cpp FLOAT APawn::MaxSpeedModifier() { ... if ( bIsCrouched ) { Result *= CrouchedPct; } else if ( bIsWalking ) { Result *= WalkingPct; // 바로 위에서 Pawn 의 무브먼트 속도를 Result 에 누적하여 구하고 그것을 Walking 상태여부에 따라 곱하여 현재 무브먼트 속도를 구한다. } ... }
- 데미지 없이 낙하 가능한 속도 ( MaxFallSpeed, velocity 와 비교된다. )
- AI들은 이보다 적은 낙하속도가 가능한 길을 택할 것이다. ( AIMaxFallSpeedFactor, 바로 위 변수와 곱하여 AI를 위한 낙하 속도를 구함 MaxFallSpeed * AIMaxFallSpeedFactor )
- Camera 관련
- Pawn 카메라 높이 ( BaseEyeHeight )
// UnPawn.cpp FVector APawn::GetPawnViewLocation() { return Location + FVector( 0.f, 0.f, 1.f ) * BaseEyeHeight; }
- 계산된/조절된 Pawn 카메라 높이 ( EyeHeight )
- 숨쉬기/HP
- 숨을 쉬기 위한 머리 ( HeadVolume )
- HP ( Health )
- 최대 HP ( HealthMax )
- HP 를 모든 클라에게 복제할까? ( bReplicateHealthToAll )
- 숨쉬기 타이머 ( BreathTime, 물에 빠졌을 때 일정 시간마다 데미지를 주기 위한 주기 )
// UnLevTic.cpp void APawn::TickSpecial( FLOAT DeltaSeconds ) { // Authority 이고 BreathTime 중이라면 if ( Role == ROLE_Authority && BreathTime > 0.f ) { BreathTime -= DeltaSeconds; if ( BreathTime < 0.001f ) { // 때가 됐다면 BreathTimer 호출 (바로 아래) BreathTime = 0.0f; eventBreathTimer(); } } ... } // Pawn.uc event BreathTimer() { if ( HeadVolume.bWaterVolume ) { if ( Health < 0 || WorldInfo.NetMode == NM_Client || DrivenVehicle != None ) return; // 죽었거나 클라이언트거나 무엇인가를 타고있다면 무시 TakeDrowningDamage(); // 익사 피해 if ( Health > 0 ) BreathTime = 2.0; // 2초 후 다시 BreathTimer 호출 } else { BreathTime = 0.0; // 더 이상 피해는 없음 } }
- 얼마만큼 숨을 참을 수 있는가? ( UnderWaterTime, 최초 입수 시 BreathTime 에 대입되는 값. 이 시간이 지나면 위의 BreathTimer 함수에 나와있는대로 2초마다 데미지 )
// UTPawn.uc event HeadVolumeChange( PhysicsVolume newHeadVolume ) { ... else if ( ... ) { BreathTime = UnderWaterTime; // 입수 시 숨쉬기 타이머 발동 } } defaultproperties { ... UnderWaterTime=+00020.000000 // 20초 ... }
- 마지막으로 피해를 입은 시간 ( LastPainTime, BOT 의 경우 피격받고 일정 시간동안 Aim 을 불안정하게 하기위한 용도로써 활용된다. )
// Pawn.uc function PlayHit( ... ) { ... LastPainTime = WorldInfo.TimeSeconds; }
- 루트모션
- 루트모션 속도 ( RMVelocity, 클라이언트에서 루트모션을 재현하기 위한 용도의 변수. 또는 Controller.bPreciseDestination정확한 이동 기능이 활성화일 때 사용되기도 함. )
- 강제 루트모션 속도 사용 ( bForceRMVelocity, 이 값이 true 면 APawn::CalcVelocity() 에서 RMVelocity 값을 직접적으로 사용 )
// UnPhysic.cpp void APawn::CalcVelocity( ... ) { ... if ( bForceRMVelocity ) { Velocity = RMVelocity; return; } ... }
- 강제 일반적인 속도계산 ( bForceRegularVelocity, 이 값이 true 면 APawn::CalcVelocity() 에서 RMVelocity 값을 절대로 사용하지 않는다. )
- 사운드와 노이즈
- 여러 변수들 ( noise1spot, noise1time, noise1other, noise1loudness, noise2spot, noise2time, noise2other, noise2loudness )
- 사운드 음 꺾기 ( SoundDampening )
- 데미지
- 데미지 증가 ( DamageScaling ) *
Controller
PlayerController
PlayerReplicationInfo
UTGame 관련
샘플로 제공되는 언리얼토너먼트3 (이하 UTGame) 의 캐릭터를 구현하는데 활용된 여러 요소들을 추가적으로 살펴본다.
UTFamilyInfo
UTPlayerReplicationInfo
이동
정확한 이동
엔진에서 목표지점destination까지 정확하게 이동시켜주는 로직이 존재한다.
대략적인 방법은 이렇다.
- 정확히 도달해야 하는 목표지점을 설정한다. (by controller)
- 매 프레임마다 도착했는지 여부를 검사하고 아직 도달하지 못했다면 적절한 velocity 를 계산한다.
- 목표지점destination에서의 허용 Offset 안에 도달하면 이벤트함수를 호출한다. ( Controller.ReachedPreciseDestination() )
관련 변수/함수는 아래와 같다.
- 변수
// Pawn.uc var float DestinationOffset; // 목표지점으로부터의 허용 Offset // Controller.uc var bool bPreciseDestination; // 목표지점에 맞는 velocity 를 강제할 것인지의 여부, 정확한 이동을 수행할 것인지 여부와 상통 var BasedPosition DestinationPosition; // 목표지점
- 함수
// UnPhysic.cpp void Pawn::CalcVelocity( ... ) { ... // RooMotion 일 경우를 제외하고 '정확한 이동' 처리를 수행한다. if ( !bDoRootMotionAccel && Controller && Controller->bPreciseDestination ) { FVector Dest = controller->GetDestinationPosition(); // Controller.DestinationPosition 을 Vector 로 형변환하여 리턴 if ( ReachedDestination( Location, Dest, NULL ) ) { Controller->bPreciseDestination = FALSE; // '정확한 이동'을 종료 Controller->eventReachedPreciseDestination(); // 종료 이벤트 호출 Velocity = FVector( 0.f ); Acceleration = FVector( 0.f ); } else if ( bForceMaxAccel ) { const FVector Dir = (Dest - Location).SafeNormal(); Acceleration = Dir * MaxAccel; Velocity = Dir * MaxSpeed; } else { Velocity = (Dest - Location) / DeltaTime; } ... } ... } // UnPawn.cpp UBOOL APawn::ReachedDestination( ... ) { ... return ReachThresholdTest( ... ); } UBOOL APawn::ReachThresholdTest( ... ) { ... FLOAT Threshold = ThresholdAdjust + CylinerComponent->CollisionRadius + DestinationOffset; // 도착으로 인정할 유효 반지름을 계산 ... if ( Dir.SizeSquared() > Threshold * Threshold ) return FALSE; ... // 적절하게 테스트하고 return TRUE; // 도착했다고 판정 }
상기 APawn::ReachThresholdTest 함수의 동입부에 도착지점으로부터의 허용 반지름을 계산하는 부분이 있는데, 이 값에 음수를 주어 좀 더 정확한 목표지점에 도달하게 할 수 있다.
무브먼트
언리얼엔진3에 기본적으로 제공되는 UTGame 을 기반으로 한 분석내용이다.
웅크리기 Crouch
코드플로우
- 발생
- DefaultInput.ini
- .Bindings=(Name=“C”,Command=“GBA_Duck”)
- .Bindings=(Name=“GBA_Duck”,Command=“Duck | onrelease UnDuck | Axis aUp Speed=-1.0 AbsoluteAxis=100”)
- simulated exec function Duck() (UTPlayerInput.uc)
- …
- bDuck = true;
- 루프1
- state PlayerWalking::ProcessMove(…) (PlayerController.uc) ← state PlayerWalking::ProcessMove(…) (UTPlayerController.uc) ← state PlayerWalking::PlayerMove(float DeltaTime) (PlayerController.uc) ← state PlayerWalking::PlayerMove(float DeltaTime) (UTPlayerController.uc) ← event PlyaerTick(float DeltaTime) (PlayerController.uc) ← event PlayerTick(float DeltaTime) (UTPlayerController.uc)
- …
- CheckJumpOrDuck();
- function CheckJumpOrDuck() (UTPlayerController.uc)
- 점프나 더블점프를 수행하는 함수를 호출
- Pawn.ShouldCrouch(bDuck != 0);
- funciton SouldCrouch( bool bCrouch ) (Pawn.uc)
- bWantsToCrouch = bCrouch;
- 루프2
- AActor::TickAuthoritative ← AActor::Tick ← APawn::Tick ← TickActors<FDeferredTickList::FGlobalActorIterator>
- …
- performPhysics 호출
- APawn::performPhysics(float DeltaSeconds) (UnPhysic.cpp)
- …
- 적절한 조건을 체크한 후 (bWantsToCrouch, bIsCrouched 따위) Crouch() 나 UnCrouch() 를 호출
- …
- APawn::Crouch(INT bClientSimulation) (UnPawn.cpp)
- Collision Cylinder 의 반지름과 높이를 CrouchRadius 와 CrouchHeight 로 변경
- Crouch Collision Cylinder 가 더 커졌다면 지면을 침범했는지 체크한다.
- eventStartCrouch( 높이차이 ) 호출
- simulated event StartCrouch(float HeightAdjust) (UTPawn.uc)
- Super.StartCrouch(HeightAdjust);
- CrouchMeshZOffset = HeightAdjust;
- simulated event StartCrouch(float HeightAdjust) (Pawn.uc)
- EyeHeight 조절
- OldZ 조절
- BaseEyeHeight 조절
특이사항
- 웅크리기에 사용될 충돌영역 정보인 CrouchRadius, CrouchHeight 를 세팅해야 한다.
- 이때 MaxStepHeight 가 CollisionHeight - CrouchHeight 보다는 커야 한다. MaxStepHeight 는 캐릭터가 자연스럽게 이동 가능한 최대 허용높이이다. 웅크리기가 발동되면 내부적으로 캐릭터 바운딩실린더 높이를 조절하는데 그 갭이 MaxStepHeight 보다 크면 캐릭터는 Falling 운동을 하게끔 처리된다. (엔진 내부적으로) 때문에 주의가 필요하다.
점프 Jump
코드 플로우
- 발동
// DefaultInput.ini .Bindings=(Name="GBA_Jump",Command="Jump | Axis aUp Speed=+1.0 AbsoluteAxis=100")
- 플래그 체크
// UTPlayerInput.uc exec function Jump() { ... Super.Jump(); ... } // PlayerInput.uc exec function Jump() { ... bPressedJump = true; }
- 지연 점프
// PlayerController.uc state PlayerWalking { ... function PlayerMove( float DeltaTime ) { ... // 지금 점프할 수 없는 상황이면 지연시킨다. if ( bPressedJump && Pawn.CannotJumpNow() ) { bSaveJump = true; bPressedJump = false; } ... } ... }
- 점프 시도여부 체크 (매 프레임)
// UTPlayerController.uc function CheckJumpOrDuck() { ... else if ( bPressedJump ) { Pawn.DoJump( bUpdateing ); } ... } // PlayerController.uc function CheckJumpOrDuck() { if ( bPressedJump && (Pawn != None) ) { Pawn.DoJump( bUpdating ); } }
- 실제 점프로직 처리
// UTPawn.uc function bool DoJump( bool bUpdating ) { ... if ( Physics ==PHYS_Spider ) Velocity = JumpZ * Floor; else if ( Physics == PHYS_Ladder ) Velocity.Z = 0 else if ( bIsWalking ) Velocity.Z = Default.JumpZ; else // 보통 이부분에 걸린다. Velocity.Z = JumpZ; ... }
부가기능
TurnInPlace
Pawn 의 현재 Rotation 을 기준으로 좌우 일정 반경을 회전할 동안 발이 땅에 접지한 상태로 있는 기능을 지원한다. UnrealEngine3 에서는 이 기능을 Turn-In-Place 라고 지칭한다. 이를 수행하는 레이어는 UDKPawn 이다.
분석 포인트
- AnimTree 노드 캐싱
// UTPawn.uc simulated event PostInitAnimTree(SkeletalMeshComponent SkelComp) { ... // 허리쪽 Bone 을 제어하는 컨트롤 캐싱 (RootRot 이란 이름을 가진 SkelControlSingleBone 컨트롤) RootRotControl = SkelControlSingleBone( mesh.FindSkelControl( 'RootRot' ) ); ... // 조준 노드 캐싱 AimNode = AnimNodeAimOffset( mesh.FindAnimNode( 'AimNode' ) ); ... }
- 업데이트 로직
// UDKPawn.cpp void AUDKPawn::TickSpecial( FLOAT DeltaSeconds ) { ... // 현재 Aim pitch 와 yaw 를 얻는다. INT PawnAimPitch; if ( Controller ) { // 컨트롤러의 Pitch를 얻는다. PawnAimPitch = Controller->Rotation.Pitch; } else { // Pawn 의 Pitch를 얻는다. PawnAimPitch = Rotation.Pitch; if ( PawnAimPitch == 0 ) { PawnAimPitch = RemoteViewPitch << 8; } } // Pawn 의 최종 조준 Pitch PawnAimPitch = UnwindRot( PawnAimPitch ); INT PawnAimYaw = UnwindRot( Rotation.Yaw ); // Pawn 의 최종 조준 Yaw (가 될 값) INT AimYaw = 0; if ( Physics == PHYS_Walking && Velocity.Size() < KINDA_SMALL_NUMBER ) { // PawnAimYaw 는 손이 향하는 방향, RootYaw 는 발이 향하는 방향이라 생각하면 이해가 쉽다. INT CurrentAimYaw = UnwindRot( PawnAimYaw - RootYaw ); INT RootRot = 0; if ( CurrentAimYaw > MaxYawAim ) { RootRot = ( CurrentAimYaw - MaxYawAim ); } else if ( CurrentAimYaw < -MaxYawAim ) { RootRot = ( CurrentAimYaw - (-MaxYawAim) ); } RootYaw += RootRot; RootYawSpeed += ( (FLOAT)RootRot ) / DeltaSeconds; // 최종 손과 발의 offset. 이것이 곧 Aim 노드에 적용될 Yaw AimYaw = UnwindRot( PawnAimYaw - RootYaw ); } else { RootYaw = Rotation.Yaw; RootYawSpeed = 0.f; AimYaw = 0; } // 좌우 90 도 회전을 각각 -1, 1 로 매핑시킨 값으로 변환 if ( !bNoWeaponFiring ) { CurrentSkelAim.X = Clamp<FLOAT>( ( (FLOAT)AimYaw / 16384.f ), -1.f, 1.f ); CurrentSkelAim.Y = Clamp<FLOAT>( ( (FLOAT)PawnAimPitch / 16384.f ), -1.f, 1.f ); } // 허리를 Aim 의 반대쪽으로 회전. 즉, 손의 방향이 Pawn Rotation 과 일치하고 발의 방향을 보정하는 방식 if ( RootRotControl ) { RootRotControl->BoneRotation.Yaw = -AimYaw; } // Aim 업데이트 if ( AimNode ) { AimNode->Aim = CurrentSkelAim; } ... }
분석
참고
'게임엔진(GameEngine) > Unreal3' 카테고리의 다른 글
AnimTree 이해영상 (0) | 2016.02.29 |
---|---|
웹html5 에서 언리얼이 나왔네요 (0) | 2013.05.03 |
글루 코드 (=C++ 과 언리얼 스크립트 연결하기위한 작업코드) (0) | 2012.10.27 |
네이티브 패키지 목록에 패키지 추가하기 (0) | 2012.10.27 |
[UDK] 프로그래밍 초보를 위한 언리얼 스크립트 코드 10선 (0) | 2012.10.27 |