UDN
Search public documentation:
CameraTechnicalGuideKR
English Translation
日本語訳
中国翻译
Interested in the Unreal Engine?
Visit the Unreal Technology site.
Looking for jobs and company info?
Check out the Epic games site.
Questions about support via UDN?
Contact the UDN Staff
日本語訳
中国翻译
Interested in the Unreal Engine?
Visit the Unreal Technology site.
Looking for jobs and company info?
Check out the Epic games site.
Questions about support via UDN?
Contact the UDN Staff
UE3 홈 > 게임플레이 프로그래밍 > 카메라 테크니컬 가이드
카메라 테크니컬 가이드
문서 변경사항: Jeff Wilson 작성. 홍성진 번역.
개요
카메라
- 일반
- PCOwner - 이 카메라를 소유하는 PlayerController로의 참조
- CameraStyle - 이 카메라의 현재 모드. 뷰 타겟에 의해 시점이 덮어쓰이지 않았을 때 (1인칭, 3인칭, 자유 등의) 카메라 종류를 결정하는데 사용
- ViewTarget - 뷰 타겟을 정의하는 현재 데이터. 유형은 TViewTarget
- TViewTarget
- Target - 카메라가 "따르고" 있는 타겟 액터
- Controller - 폰인 경우 타겟의 콘트롤러
- POV - 타겟에 이상적인 시점. 유형은 TPOV
- TPOV
- Location - 시점의 위치
- Rotation - 시점의 회전
- FOV - 시점의 시야각
- AspectRatio - 타겟에 사용할 상 비율
- PRI - Player Replication Info, 폰 전이를 통한 동일 플레이어 추적에 사용되는 플레이어 자가복제 정보
- TPOV
- POV - 타겟에 이상적인 시점. 유형은 TPOV
- 시야
- DefaultFOV - 카메라의 기본 시야
- bLockedFOV - 참이면, 카메라의 시야가 LockedFOV 값에 잠김
- LockedFOV - FOV가 잠겼을 때 사용할 시야
- 상 비율
- DefaultAspectRatio - 카메라의 상 비율 기본값
- bConstrainAspectRatio - 참이면 카메라의 상 비율이 ConstrainedAspectRatio 값에 제한됨
- ConstrainedAspectRatio - 카메라의 상 비율이 제한되었을 때 사용할 상 비율
- 일반
- UpdateCamera [DeltaTime] - 카메라 업데이트을 수행하기 위해 프레임마다 호출
- DeltaTime - 지난 업데이트 이후 경과 시간
- GetCameraViewPoint [OutCamLoc] [OutCamRot] - 카메라의 위치와 회전값을 구함. 직접 호출해야 함. PCOwner의 GetPlayerViewPoint() 함수 대리 호출 가능
- OutCamLoc - 카메라 위치 출력
- OutCamRot - 카메라 회전 출력
- ProcessViewRotation [DeltaTime] [OutViewRotation] [OutDeltaRot] - 카메라에 이 프레임의 뷰 회전 변경사항을 바꿀 수 있는 기회를 주기 위해 PCOwner에 의해 호출됨
- DeltaTime - 지난 업데이트 이후 경과 시간
- OutViewRotation - 조절된 카메라 뷰 회전 출력
- OutDeltaRot - 조절된 카메라 델타 회전 출력
- UpdateCamera [DeltaTime] - 카메라 업데이트을 수행하기 위해 프레임마다 호출
- 시야
- GetFOVAngle - 카메라의 현 시야각 반환
- SetFOV [NewFOV] - 카메라의 시야를 NewFOV 값으로 설정
- NewFOV - 카메라의 시야로 설정할 값
- 뷰 타겟
- SetViewTarget [NewViewTarget] [TransitionParams] - 카메라의 뷰 타겟 설정
- NewViewTarget - 카메라의 새로운 뷰 타겟으로 설정할 액터
- TransitionParams - 새로운 뷰 타겟으로의 전이시 사용할 혼합 파라미터
- UpdateViewTarget [OutVT] [DeltaTime] - 뷰타겟을 새 위치, 회전, 시야로 업데이트하기 위해 프레임별로 호출됨. (뷰 타겟 액터에서 CalcCamera?() 오버라이딩에 상반되는) 커스텀 카메라를 사용중이라면, 이게 바로 동작을 바라는 대로 구현하기 위한 오버라이딩의 주요 함수가 됩니다.
- OutVT - 카메라의 시점과 뷰 타겟을 담는 데이터 구조체 출력
- DeltaTime - 지난 업데이트 이후 경과 시간
- SetViewTarget [NewViewTarget] [TransitionParams] - 카메라의 뷰 타겟 설정
포스트 프로세스 이펙트
포스트 프로세스 이펙트는 렌더링된 씬이 플레이어에게 표시되기 전에 적용되는 이펙트를 말합니다. 각 카메라는 월드, 볼륨, 기본 포스트 프로세스 설정을 덮어쓸 수 있는 자체 포스트 프로세스 이펙트 세트를 적용할 수 있습니다. 포스트 프로세스 이펙트에 대한 상세 정보는, 포스트 프로세스 에디터 가이드 , 포스트 프로세스 테크니컬 가이드 , 포스트 프로세스 머티리얼 등을 참고하십시오. 포스트 프로세스 이펙트 프로퍼티- CameOverridePostProcessAlpha - 카메라의 포스트 프로세스 설정 영향력을 월드, 볼륨, 기본 포스트 프로세스 설정 등에 맞게 설정. 값 0.0 은 월드, 볼륨, 기본 포스트 프로세스 이펙트가, 1.0 은 카메라의 포스트 프로세스가 최대 영향력을 행사함을 의미
- CamPostProcessSettings - 카메라가 월드, 볼륨, 기본 포스트 프로세스 이펙트를 덮어쓸 때 사용할 포스트 프로세스 설정
- bEnableColorScaling - 참이면 최종 이미지의 색 채널이 ColorScale 값을 사용하여 조절
- ColorScale - 최종 이미지의 개별 색 채널 조절용 벡터
- bEnableColorScaleInterp - 참이면 SetDesiredColorScale() 함수를 통해 색 스케일 값이 새로 설정될 때, 카메라가 색 스케일 중간값으로 보간
- bEnableFading - 참이면 카메라가 화면에 FadeColor를 FadeAmount만큼 적용
- FadeColor - 카메라가 페이드할 때 화면에 적용할 색
- FadeAmount - 페이드에 적용할 양. 근본적으로는 페이드의 알파값
- SetDesiredColorScale [NewColorScale] [InterpTime] - 색 스케일 값을 새로 설정하고 bEnableColorScaleInterp 값에 따라 보간
- NewColorScale - 색 스케일에 사용할 새로운 값
- InterpTime - 새로운 색 스케일값 보간에 걸리는 시간
렌즈 이펙트
렌즈 이펙트는 플레이어의 카메라 렌즈에 적용되는 파티클 이펙트입니다. 렌즈 이펙트는 카메라 렌드에 떨어지는 빗방울이나 핏자국, 내려앉는 먼지나 가루 등을 만드는데 사용합니다. 카메라 클래스에 이런 이펙트 유형을 적용하는 함수가 포함됩니다. 파티클 시스템 및 이펙트에 대한 상세 정보는, 파티클 시스템 참고서 를 참고하십시오. 렌즈 이펙트 프로퍼티-
- CameraLensEffects - 현재 카메라에 적용된 파티클 이펙트 전체의 배열
- FindCameraLensEffect [LensEffectEmitterClass] - 현재 카메라에 적용된 렌즈 이펙트 검색 및 일치 유형 반환
- LensEffectEmitterType - 검색할 렌즈 이펙트 클래스
- AddCameraLensEffect [LensEffectEmitterClass] - 지정된 유형의 렌즈 이펙트를 카메라에 새로 적용
- LensEffectEmitterClass - 카메라에 적용할 렌즈 이펙트 클래스
- RemoveCameraLensEffect [Emitter] - 카메라에서 렌즈 이펙트 제거
- Emitter - 카메라에서 제거할 렌즈 이펙트
- ClearCameraLensEffects - 카메라에 현재 적용된 렌즈 이펙트 전부 제거
카메라 애니메이션
카메라 애니메이션이란 마티네에서 생성 가능한 (혹은 외부 애니메이션 에디터로 만든 것을 가져온) 애니메이션을 말하며, 애니메이션의 이동 및 회전 정보를 사용하여 플레이 도중 카메라를 맞춥(offset)니다. 또한 FOV나 포스트프로세스 이펙트처럼 보통 마티네로 애니메이트 가능한 카메라의 기타 프로퍼티도 애니메이트시킬 수 있습니다. 카메라 흔들기나 핸드헬드 카메라의 손떨림은 물론 여타 애니메이션 이펙트 생성에 꽤나 유용합니다. 카메라 애니메이션 설정에 대한 상세 정보는, 카메라 설정하기 를 참고하십시오. 카메라 애니메이션 함수- PlayCameraAnim [CameraAnim] [Rate] [Scale] [BlendInTime] [BlendOutTime] [bLoop] [bRandomStartTime] [Duration] [bSingleInstance] - 카메라에서 카메라 애니메이션을 재생
- CameraAnim - 재생할 카메라 애니메이션
- Rate - 옵션. 카메라 애니메이션 재생 속도
- Scale - 옵션. 카메라 애니메이션 변형에 적용할 강도
- BlendInTime - 옵션. 카메라 애니메이션 속으로의 혼합에 걸리는 시간
- BlendOutTime - 옵션. 카메라 애니메이션 밖으로의 혼합에 걸리는 시간
- bLoop - 옵션. 참이면 카메라 애니메이션을 명시적으로 멈출 때까지 지속
- bRandomStartTime - 옵션. 참이면 카메라 애니메이션이 시간선 내의 임의 지점에서부터 재생 시작
- Duration - 옵션. 애니메이션 재생 기간. 설정하지 않으면 전체 애니메이션 재생
- bSingleInstance - 옵션. 참이면 한 번에 하나의 카메라 흔들림 인스턴스만 가능
- StopAllCameraAnims [bImmediate] - 현재 재생중인 카메라 애니메이션 모두 정지
- bImmediate - 옵션. 참이면 애니메이션이 즉시 정지, 설정된 혼합 시간도 무시
- StopAllCameraAnimsByType [Anim] [bImmediate] - 지정된 유형의 카메라 애니메이션 인스턴스를 전부 정지
- Anim - 정지시킬 카메라 애니메이션 유형
- bImmediate - 옵션. 참이면 애니메이션이 바로 정지, 설정된 혼합 시간도 무시
- StopCameraAnim [AnimInst] [bImmediate] - 지정된 카메라 애니메이션 인스턴스를 정지
- AnimInst - 정지시킬 카메라 애니메이션 인스턴스
- bImmediate - 옵션. 참이면 애니메이션이 바로 정지, 설정된 혼합 시간도 무시
카메라 모디파이어
카메라 모디파이어란 카메라에 적용했을 때 그 프로퍼티를 바꿀 수 있는 객체를 말합니다. CameraModifier 클래스가 해당 이펙트의 기본 클래스입니다. 이 클래스를 하위클래스화하고 내부 함수를 오버라이드하면 완전한 맞춤 모디파이어를 만들 수 있습니다. CameraModifier_CameraShake 클래스가 카메라 모디파이어로 만든 좋은 예입니다. 카메라 모디파이어 프로퍼티-
- ModifierList - 카메라에 현재 적용된 모든 카메라 모디파이어 배열
- CameraShakeModClass - cone-driven 카메라 흔들기, 즉 키즈멧에서의 카메라 애니메이션이 아닌 화면 흔들기 등에 사용하는 클래스
- PlayCameraShake [Shake] [Scale] [PlaySpace] [UserPlaySpaceRot] - 카메라에 카메라 흔들기 이펙트를 재생
- Shake - 카메라 흔들기 이펙트에 사용할 CameraShake 설정
- Scale - 카메라 흔들기 설정에 곱해줄 스케일 인자
- PlaySpace - 옵션. 카메라 흔들기에 사용할 재생 공간
- UserPlaySpaceRot - 옵션. 사용자 지정 재생 공간에 사용할 회전
- StopCameraShake [Shake] - 카메라의 카메라 흔들기 이펙트 재생 중지
- Shake - 재생을 멈출 카메라 흔들기
- CalcRadialCameraShake [Cam] [Epicenter] [InnerRadius] [OuterRadius] [Falloff] - 지정된 카메라의 반경 흔들기 강도를 계산하여 반환
- Cam - 강도를 계산할 카메라
- Epicenter - 카메라 흔들기 발생 진원지
- InnerRadius - 감쇠가 시작될 진원지로부터의 거리
- OuterRadius - 카메라 흔들기 이펙트가 끝나게 될 진원지로부터의 거리
- Falloff - 강도 감쇠를 계산하는데 사용할 지수
- PlayWorldCameraShake [Shake] [ShakeInstigator] [Epicenter] [InnerRadius] [OuterRadius] [Falloff] [bTrForceFeedback] [bOrientShakeTowardEpicenter] - 근처 카메라 전부에 영향을 끼치는 월드내 카메라 흔들기 재생
- Shake - 재생할 카메라 흔들기
- ShakeInstigator - 카메라 흔들기를 실시하게 한 액터
- Epicenter - 카메라 흔들기 발생 진원지
- InnerRadius - 감쇠가 시작될 진원지로부터의 거리
- OuterRadius - 카메라 흔들기 이펙트가 끝나게 될 진원지로부터의 거리
- Falloff - 감쇠 강도 계산에 사용될 지수
- bTryForceFeedback - 참이면 힘 피드백 적용을 영향받는 콘트롤러 전부에 시도
- bOrientShakeTowardEpicenter - 옵션. 참이면 카메라 흔들기의 오프셋 모두가 진원지를 향하는데 관계되어 적용, 양수 X축이 진원지를 향하는 채로.
- ClearAllCameraShakes - 카메라에 현재 적용된 카메라 흔들기를 모두 제거
플레이어 콘트롤러
- PlayerCamera - 플레이어 카메라로의 참조
- CameraClass - 플레이어 용으로 사용할 카메라 클래스
- ViewTarget - 플레이어 카메라의 현재 뷰 타겟
- RealViewTarget - 플레이어 카메라 뷰 타겟의 플레이어 자가복제 정보
- FOVangle - 플레이어 카메라의 시야각
- DefaultFOV - 플레이어 카메라에 사용할 기본 시야각
- GetPlayerViewPoint [out_Location] [out_Rotation] - 콘트롤러 폰의 시점 반환. 사람 플레이어에 대해서는 카메라의 시점. AI 플레이어에 대해서는 폰의 눈에서의 시점. 이런 기본 구현에서는 단순히 콘트롤러 자체의 위치 및 회전.
- out_Location - 플레이어의 시점 위치 출력
- out_Rotation - 플레이어의 시점 회전 출력
- GetActorEyesViewPoint [out_Location] [out_Rotation] - 콘트롤러나 있을 경우 폰의 시점 반환. 근본적으로 플레이어가 어디에서 어느 방향을 보는지를 반환
- out_Location - 플레이어 눈의 위치 출력
- out_Rotation - 플레이어 눈의 회전 출력
- UpdateRotation [DeltaTime] - 콘트롤러와 콘트롤러 폰의 회전을 플레이어 입력에 따라 업데이트
- DeltaTime - 지난 업데이트 이후 경과 시간
- ProcessViewRotation [DeltaTime] [out_ViewRotation] [DeltaRot] - 콘트롤러의 뷰 회전에 변경을 허용하기 위해 호출됩니다. (clamping 등) UpdateRotation() 에서 호출됩니다.
- DeltaTime - 지난 업데이트 이후 경과 시간
- out_ViewRotation - 플레이어 뷰 회전 출력
- DeltaRot - 플레이어 입력에 따른 회전 변화
- PlayerMove [DeltaTime] - 현 이동에 대한 가속 및 회전값을 새로 계산 후 (1인용이나 청취서버 용으로는) ProcessMove() 또는 (네트워크 클라이언트 용으로는) ReplicateMove() 호출. 단지 기본 PlayerController 클래스의 토막일 뿐이나 플레이어걷기 상태와 같은 이동 관련 특정 상태 내에서 오버라이드됨. 매 사이클마다 PlayerTick() 함수에서 호출되는 함수
- ProcessMove [DeltaTime] [newAccel] [DoubleClickMove] [DeltaRot] - 클라이언트에서의 현 이동을 처리. 이동에 대한 특수 함수기능을 요하는 특정 상태 내에서 오버라이드되는 함수
폰
- CalcCamera [DeltaTime] [out_CamLoc] [out_CamRot] [out_FOV] - 폰에서 볼 때의 카메라 시점 계산. 플레이어용 주요 카메라 계산 함수
- DeltaTime - 지난 업데이트 이후 경과 시간
- out_CamLoc - 카메라 위치 출력
- out_CamRot - 카메라 회전 출력
- out_FOV - 카메라 시야 출력
- GetDefaultCameraMode [RequestedBy] - 이 폰에 사용해야 하는 기본 카메라 모드를 이름으로 반환. 보통 콘트롤러가 폰에 빙의될 때 호출
- RequestedBy - 기본 카메라 모드를 요구하는 콘트롤러
- ProcessViewRotation [deltaTime] [out_ViewRotation] [out_DeltaRot] - 폰에게 플레이어의 뷰 회전에 영향을 끼칠 수 있는 기회를 주고, 최종 뷰 회전값을 out_ViewRotation 파라미터로 반환. PlayerController의 UpdateRotation() 함수에서 호출
- deltaTime - 지난 업데이트 이후 경과 시간
- out_ViewRotation - 폰 시점의 회전 출력
- out_DeltaRot - 델타 회전 출력
- SetViewRotation [NewRotation] - 콘트롤러가 있으면 그 회전을, 없으면 폰 자체의 회전을 설정
- NewRotation - 폰의 뷰를 새로이 설정할 회전
- GetActorEyesViewPoint [out_Location] [out_Rotation] - 폰의 눈, 또는 플레이어 시점의 위치 및 방향 반환. 1인칭 시점에서는 카메라의 위치 및 방향과 동일. 또한 대부분의 투사가 수행될 시점
- out_Location - 폰의 눈 위치 출력
- out_Rotation - 폰의 눈 방향 출력
카메라 동작 커스터마이징
예제 - CalcCamera
class UDNGame extends UTDeathMatch; defaultproperties { DefaultPawnClass=class'UDNExamples.UDNPawn' PlayerControllerClass=class'UDNExamples.UDNPlayerController' MapPrefixes[0]="UDN" }
[Engine.GameInfo] DefaultGame=UDNExamples.UDNGame DefaultServerGame=UDNExamples.UDNGame
1인칭 카메라 예제
1인칭 시점은 UTPawn에서 연장된 폰 전부의 기본 카메라 유형입니다. 이 예제는 기본 1인칭 카메라를 만드는 데 관련된 과정을 더 잘 시연해보이기 위해, 관련된 각 클래스에서 카메라 유형을 구성하는 주요 부분을 끌어낸 뒤 새로운 하위클래스로 대체하는 것입니다. UDNPawn.ucclass UDNPawn extends UTPawn; simulated function bool CalcCamera( float fDeltaTime, out vector out_CamLoc, out rotator out_CamRot, out float out_FOV ) { // 1인칭 카메라 위치 및 회전 계산 GetActorEyesViewPoint( out_CamLoc, out_CamRot ); return true; } defaultproperties { }
class UDNPlayerController extends UTPlayerController; state PlayerWalking { ignores SeePlayer, HearNoise, Bump; function ProcessMove(float DeltaTime, vector NewAccel, eDoubleClickDir DoubleClickMove, rotator DeltaRot) { if( Pawn == None ) { return; } if (Role == ROLE_Authority) { // 원격 클라이언트용 ViewPitch 업데이트 Pawn.SetRemoteViewPitch( Rotation.Pitch ); } Pawn.Acceleration = NewAccel; CheckJumpOrDuck(); } } function UpdateRotation( float DeltaTime ) { local Rotator DeltaRot, newRotation, ViewRotation; ViewRotation = Rotation; if (Pawn!=none) { Pawn.SetDesiredRotation(ViewRotation); } // Calculate Delta to be applied on ViewRotation DeltaRot.Yaw = PlayerInput.aTurn; DeltaRot.Pitch = PlayerInput.aLookUp; ProcessViewRotation( DeltaTime, ViewRotation, DeltaRot ); SetRotation(ViewRotation); NewRotation = ViewRotation; NewRotation.Roll = Rotation.Roll; if ( Pawn != None ) Pawn.FaceRotation(NewRotation, deltatime); } defaultproperties { }
3인칭 카메라 예제
3인칭 카메라 설정은 UTPawn 하위클래스 전부에 대체 카메라 유형으로 포함되어 있습니다. 이 예제는 주요 부분을 끌어내고 기본 카메라를 이 3인칭 카메라로 오버라이드한 것입니다. UDNPawn.ucclass UDNPawn extends UTPawn; //플레이어 메시를 기본으로 보이게끔 오버라이드 simulated event BecomeViewTarget( PlayerController PC ) { local UTPlayerController UTPC; Super.BecomeViewTarget(PC); if (LocalPlayer(PC.Player) != None) { UTPC = UTPlayerController(PC); if (UTPC != None) { //플레이어 콘트롤러를 등뒤 뷰에 설정 및 메시 보이게 UTPC.SetBehindView(true); SetMeshVisibility(UTPC.bBehindView); } } } simulated function bool CalcCamera( float fDeltaTime, out vector out_CamLoc, out rotator out_CamRot, out float out_FOV ) { local vector CamStart, HitLocation, HitNormal, CamDirX, CamDirY, CamDirZ, CurrentCamOffset; local float DesiredCameraZOffset; CamStart = Location; CurrentCamOffset = CamOffset; DesiredCameraZOffset = (Health > 0) - 1.2 * GetCollisionHeight() + Mesh.Translation.Z : 0.f; CameraZOffset = (fDeltaTime < 0.2) - DesiredCameraZOffset * 5 * fDeltaTime + (1 - 5*fDeltaTime) * CameraZOffset : DesiredCameraZOffset; if ( Health <= 0 ) { CurrentCamOffset = vect(0,0,0); CurrentCamOffset.X = GetCollisionRadius(); } CamStart.Z += CameraZOffset; GetAxes(out_CamRot, CamDirX, CamDirY, CamDirZ); CamDirX *= CurrentCameraScale; if ( (Health <= 0) || bFeigningDeath ) { // 월드에 찝히지 않게 카메라 위치 조절 // @todo fixmesteve. FindSpot이 실패하면 (거의 안그러지만) 계속 찝히게 될 수 있습니다. FindSpot(GetCollisionExtent(),CamStart); } if (CurrentCameraScale < CameraScale) { CurrentCameraScale = FMin(CameraScale, CurrentCameraScale + 5 * FMax(CameraScale - CurrentCameraScale, 0.3)*fDeltaTime); } else if (CurrentCameraScale > CameraScale) { CurrentCameraScale = FMax(CameraScale, CurrentCameraScale - 5 * FMax(CameraScale - CurrentCameraScale, 0.3)*fDeltaTime); } if (CamDirX.Z > GetCollisionHeight()) { CamDirX *= square(cos(out_CamRot.Pitch * 0.0000958738)); // 0.0000958738 = 2*PI/65536 } out_CamLoc = CamStart - CamDirX*CurrentCamOffset.X + CurrentCamOffset.Y*CamDirY + CurrentCamOffset.Z*CamDirZ; if (Trace(HitLocation, HitNormal, out_CamLoc, CamStart, false, vect(12,12,12)) != None) { out_CamLoc = HitLocation; } return true; } defaultproperties { }
class UDNPlayerController extends UTPlayerController; state PlayerWalking { ignores SeePlayer, HearNoise, Bump; function ProcessMove(float DeltaTime, vector NewAccel, eDoubleClickDir DoubleClickMove, rotator DeltaRot) { if( Pawn == None ) { return; } if (Role == ROLE_Authority) { // 원격 클라이언트의 ViewPitch 업데이트 Pawn.SetRemoteViewPitch( Rotation.Pitch ); } Pawn.Acceleration = NewAccel; CheckJumpOrDuck(); } } function UpdateRotation( float DeltaTime ) { local Rotator DeltaRot, newRotation, ViewRotation; ViewRotation = Rotation; if (Pawn!=none) { Pawn.SetDesiredRotation(ViewRotation); } // ViewRotation에 적용되도록 Delta를 계산 DeltaRot.Yaw = PlayerInput.aTurn; DeltaRot.Pitch = PlayerInput.aLookUp; ProcessViewRotation( DeltaTime, ViewRotation, DeltaRot ); SetRotation(ViewRotation); NewRotation = ViewRotation; NewRotation.Roll = Rotation.Roll; if ( Pawn != None ) Pawn.FaceRotation(NewRotation, deltatime); } defaultproperties { }
내려보기 카메라 예제
내려보기 카메라는 추가 변경사항을 추가하여 만들 수 있습니다. 3인칭 카메라 설정과 유사하지만, 폰의 회전을 제한할 필요가 있습니다. 특히 상하, 위아래 조준은 허용되지 않습니다. UDNPawn.ucclass UDNPawn extends UTPawn; var float CamOffsetDistance; //플레이어 위로 카메라를 띄울 거리 var bool bFollowPlayerRotation; //참이면 카메라가 플레이어와 함께 회전 //기본으로 플레이어 메시가 보이게끔 오버라이드 simulated event BecomeViewTarget( PlayerController PC ) { local UTPlayerController UTPC; Super.BecomeViewTarget(PC); if (LocalPlayer(PC.Player) != None) { UTPC = UTPlayerController(PC); if (UTPC != None) { //플레이어 콘트롤러를 등뒤 뷰에 설정 및 메시 보이게 UTPC.SetBehindView(true); SetMeshVisibility(UTPC.bBehindView); UTPC.bNoCrosshair = true; } } } simulated function bool CalcCamera( float fDeltaTime, out vector out_CamLoc, out rotator out_CamRot, out float out_FOV ) { out_CamLoc = Location; out_CamLoc.Z += CamOffsetDistance; if(!bFollowPlayerRotation) { out_CamRot.Pitch = -16384; out_CamRot.Yaw = 0; out_CamRot.Roll = 0; } else { out_CamRot.Pitch = -16384; out_CamRot.Yaw = Rotation.Yaw; out_CamRot.Roll = 0; } return true; } simulated singular event Rotator GetBaseAimRotation() { local rotator POVRot, tempRot; tempRot = Rotation; tempRot.Pitch = 0; SetRotation(tempRot); POVRot = Rotation; POVRot.Pitch = 0; return POVRot; } defaultproperties { bFollowPlayerRotation = false; CamOffsetDistance=384.0 }
class UDNPlayerController extends UTPlayerController; state PlayerWalking { ignores SeePlayer, HearNoise, Bump; function ProcessMove(float DeltaTime, vector NewAccel, eDoubleClickDir DoubleClickMove, rotator DeltaRot) { if( Pawn == None ) { return; } if (Role == ROLE_Authority) { // 원격 클라이언트용 ViewPitch 업데이트 Pawn.SetRemoteViewPitch( Rotation.Pitch ); } Pawn.Acceleration = NewAccel; CheckJumpOrDuck(); } } function UpdateRotation( float DeltaTime ) { local Rotator DeltaRot, newRotation, ViewRotation; ViewRotation = Rotation; if (Pawn!=none) { Pawn.SetDesiredRotation(ViewRotation); } // ViewRotation에 적용될 Delta 계산 DeltaRot.Yaw = PlayerInput.aTurn; DeltaRot.Pitch = 0; ProcessViewRotation( DeltaTime, ViewRotation, DeltaRot ); SetRotation(ViewRotation); NewRotation = ViewRotation; NewRotation.Roll = Rotation.Roll; if ( Pawn != None ) Pawn.FaceRotation(NewRotation, deltatime); } defaultproperties { }
등측도법(isometric) 카메라 예제
간단한 등측도법형 카메라는 전에 보여드린 내려보기 카메라 예제와 매우 유사합니다. 카메라 오프셋이 X, Z 두 축을 따라 이루어 지며, 상하는 플레이어에 맞추어 내리회전시킵니다. UDNPawn.ucclass UDNPawn extends UTPawn; var float CamOffsetDistance; //플레이어에서 카메라를 띄울 거리 var int IsoCamAngle; //카메라의 상하각 //기본으로 플레이어 메시가 보이게 오버라이드 simulated event BecomeViewTarget( PlayerController PC ) { local UTPlayerController UTPC; Super.BecomeViewTarget(PC); if (LocalPlayer(PC.Player) != None) { UTPC = UTPlayerController(PC); if (UTPC != None) { //플레이어 콘트롤러를 등뒤 뷰에 설정 및 메시 보이게 UTPC.SetBehindView(true); SetMeshVisibility(UTPC.bBehindView); UTPC.bNoCrosshair = true; } } } simulated function bool CalcCamera( float fDeltaTime, out vector out_CamLoc, out rotator out_CamRot, out float out_FOV ) { out_CamLoc = Location; out_CamLoc.X -= Cos(IsoCamAngle * UnrRotToRad) * CamOffsetDistance; out_CamLoc.Z += Sin(IsoCamAngle * UnrRotToRad) * CamOffsetDistance; out_CamRot.Pitch = -1 * IsoCamAngle; out_CamRot.Yaw = 0; out_CamRot.Roll = 0; return true; } simulated singular event Rotator GetBaseAimRotation() { local rotator POVRot, tempRot; tempRot = Rotation; tempRot.Pitch = 0; SetRotation(tempRot); POVRot = Rotation; POVRot.Pitch = 0; return POVRot; } defaultproperties { IsoCamAngle=6420 //35.264 도 CamOffsetDistance=384.0 }
class UDNPlayerController extends UTPlayerController; state PlayerWalking { ignores SeePlayer, HearNoise, Bump; function ProcessMove(float DeltaTime, vector NewAccel, eDoubleClickDir DoubleClickMove, rotator DeltaRot) { if( Pawn == None ) { return; } if (Role == ROLE_Authority) { // 원격 클라이언트용 ViewPitch 업데이트 Pawn.SetRemoteViewPitch( Rotation.Pitch ); } Pawn.Acceleration = NewAccel; CheckJumpOrDuck(); } } function UpdateRotation( float DeltaTime ) { local Rotator DeltaRot, newRotation, ViewRotation; ViewRotation = Rotation; if (Pawn!=none) { Pawn.SetDesiredRotation(ViewRotation); } // ViewRotation에 적용될 Delta 계산 DeltaRot.Yaw = PlayerInput.aTurn; DeltaRot.Pitch = 0; ProcessViewRotation( DeltaTime, ViewRotation, DeltaRot ); SetRotation(ViewRotation); NewRotation = ViewRotation; NewRotation.Roll = Rotation.Roll; if ( Pawn != None ) Pawn.FaceRotation(NewRotation, deltatime); } defaultproperties { }
횡스크롤 카메라 예제
간단한 횡스크롤 카메라는 카메라 시점 조절 뿐만 아니라 플레이어 입력 처리법을 변경할 필요도 있습니다. 플레이어는 화면상에서 왼쪽이나 오른쪽으로만 움직일 수 있으며, 움직이는 방향으로만 향하도록 고정되어 있습니다. 그래서 입력 A와 D키가 플레이어를 앞 뒤로 움직이게 해야 합니다. UDNPawn.ucclass UDNPawn extends UTPawn; var float CamOffsetDistance; //카메라를 잠글 Y축상의 위치 //기본으로 플레이어 메시가 보이게끔 오버라이드 simulated event BecomeViewTarget( PlayerController PC ) { local UTPlayerController UTPC; Super.BecomeViewTarget(PC); if (LocalPlayer(PC.Player) != None) { UTPC = UTPlayerController(PC); if (UTPC != None) { //플레이어 콘트롤러를 등뒤 뷰에 설정 및 메시 보이게 UTPC.SetBehindView(true); SetMeshVisibility(UTPC.bBehindView); UTPC.bNoCrosshair = true; } } } simulated function bool CalcCamera( float fDeltaTime, out vector out_CamLoc, out rotator out_CamRot, out float out_FOV ) { out_CamLoc = Location; out_CamLoc.Y = CamOffsetDistance; out_CamRot.Pitch = 0; out_CamRot.Yaw = 16384; out_CamRot.Roll = 0; return true; } simulated singular event Rotator GetBaseAimRotation() { local rotator POVRot; POVRot = Rotation; if( (Rotation.Yaw % 65535 > 16384 && Rotation.Yaw % 65535 < 49560) || (Rotation.Yaw % 65535 < -16384 && Rotation.Yaw % 65535 > -49560) ) { POVRot.Yaw = 32768; } else { POVRot.Yaw = 0; } if( POVRot.Pitch == 0 ) { POVRot.Pitch = RemoteViewPitch << 8; } return POVRot; } defaultproperties { CamOffsetDistance=0.0 }
class UDNPlayerController extends UTPlayerController; state PlayerWalking { ignores SeePlayer, HearNoise, Bump; function ProcessMove(float DeltaTime, vector NewAccel, eDoubleClickDir DoubleClickMove, rotator DeltaRot) { local Rotator tempRot; if( Pawn == None ) { return; } if (Role == ROLE_Authority) { // 원격 클라이언트용 ViewPitch 업데이트 Pawn.SetRemoteViewPitch( Rotation.Pitch ); } Pawn.Acceleration.X = -1 * PlayerInput.aStrafe * DeltaTime * 100 * PlayerInput.MoveForwardSpeed; Pawn.Acceleration.Y = 0; Pawn.Acceleration.Z = 0; tempRot.Pitch = Pawn.Rotation.Pitch; tempRot.Roll = 0; if(Normal(Pawn.Acceleration) Dot Vect(1,0,0) > 0) { tempRot.Yaw = 0; Pawn.SetRotation(tempRot); } else if(Normal(Pawn.Acceleration) Dot Vect(1,0,0) < 0) { tempRot.Yaw = 32768; Pawn.SetRotation(tempRot); } CheckJumpOrDuck(); } } function UpdateRotation( float DeltaTime ) { local Rotator DeltaRot, ViewRotation; ViewRotation = Rotation; // Calculate Delta to be applied on ViewRotation DeltaRot.Yaw = Pawn.Rotation.Yaw; DeltaRot.Pitch = PlayerInput.aLookUp; ProcessViewRotation( DeltaTime, ViewRotation, DeltaRot ); SetRotation(ViewRotation); } defaultproperties { }
통합 카메라 예제
이 예제는 다른 예제를 단일 구현으로 한 데 묶어 플레이어가 카메라 유형을 전환할 수 있게 하며, 실행 함수의 사용을 통해 조절합니다. UDNPawn.ucclass UDNPawn extends UTPawn; Enum CameraPerspective { CAM_FirstPerson, CAM_ThirdPerson, CAM_TopDown, CAM_SideScroller, CAM_Isometric }; var bool bFollowPlayerRotation; var CameraPerspective CameraType; var float CamOffsetDistance; var int IsoCamAngle; exec function CameraMode(CameraPerspective mode) { local UTPlayerController UTPC; CameraType = mode; UTPC = UTPlayerController(Controller); if (UTPC != None) { if(CameraType != CAM_FirstPerson) { UTPC.SetBehindView(true); if(CameraType != CAM_ThirdPerson) { UTPC.bNoCrosshair = true; } else { UTPC.bNoCrosshair = false; } } else { UTPC.bNoCrosshair = false; UTPC.SetBehindView(false); } SetMeshVisibility(UTPC.bBehindView); } } exec function IsoAngle(int angle) { IsoCamAngle = angle; } /* BecomeViewTarget 이 액터가 그 ViewTarget이 될 때 Camera에 의해 호출 */ simulated event BecomeViewTarget( PlayerController PC ) { local UTPlayerController UTPC; Super.BecomeViewTarget(PC); if (LocalPlayer(PC.Player) != None) { UTPC = UTPlayerController(PC); if (UTPC != None) { if(CameraType != CAM_FirstPerson) { UTPC.SetBehindView(true); if(CameraType != CAM_ThirdPerson) { UTPC.bNoCrosshair = true; } else { UTPC.bNoCrosshair = false; } } else { UTPC.bNoCrosshair = false; UTPC.SetBehindView(false); } SetMeshVisibility(UTPC.bBehindView); } } } /** * 이 폰을 보고있을 때, 카메라 시점 계산 * * @param fDeltaTime 지난 업데이트 이후 경과 시간 * @param out_CamLoc 카메라 위치 * @param out_CamRot 카메라 회전 * @param out_FOV 시야 * * @return 폰이 카메라 시점을 제공하면 참 */ simulated function bool CalcCamera( float fDeltaTime, out vector out_CamLoc, out rotator out_CamRot, out float out_FOV ) { // 고정 카메라 처리 if (bFixedView) { out_CamLoc = FixedViewLoc; out_CamRot = FixedViewRot; } else { if ( CameraType == CAM_ThirdPerson ) // BehindView 처리 { CalcThirdPersonCam(fDeltaTime, out_CamLoc, out_CamRot, out_FOV); } else if ( CameraType == CAM_TopDown ) // BehindView 처리 { CalcTopDownCam(fDeltaTime, out_CamLoc, out_CamRot, out_FOV); } else if ( CameraType == CAM_SideScroller ) // BehindView 처리 { CalcSideScrollerCam(fDeltaTime, out_CamLoc, out_CamRot, out_FOV); } else if ( CameraType == CAM_Isometric ) // BehindView 처리 { CalcIsometricCam(fDeltaTime, out_CamLoc, out_CamRot, out_FOV); } else { // 기본값으로 폰의 눈을 통해 보도록... GetActorEyesViewPoint( out_CamLoc, out_CamRot ); } if ( UTWeapon(Weapon) != none) { UTWeapon(Weapon).WeaponCalcCamera(fDeltaTime, out_CamLoc, out_CamRot); } } return true; } simulated function bool CalcTopDownCam( float fDeltaTime, out vector out_CamLoc, out rotator out_CamRot, out float out_FOV ) { out_CamLoc = Location; out_CamLoc.Z += CamOffsetDistance; if(!bFollowPlayerRotation) { out_CamRot.Pitch = -16384; out_CamRot.Yaw = 0; out_CamRot.Roll = 0; } else { out_CamRot.Pitch = -16384; out_CamRot.Yaw = Rotation.Yaw; out_CamRot.Roll = 0; } return true; } simulated function bool CalcSideScrollerCam( float fDeltaTime, out vector out_CamLoc, out rotator out_CamRot, out float out_FOV ) { out_CamLoc = Location; out_CamLoc.Y = CamOffsetDistance; out_CamRot.Pitch = 0; out_CamRot.Yaw = 16384; out_CamRot.Roll = 0; return true; } simulated function bool CalcIsometricCam( float fDeltaTime, out vector out_CamLoc, out rotator out_CamRot, out float out_FOV ) { out_CamLoc = Location; out_CamLoc.X -= Cos(IsoCamAngle * UnrRotToRad) * CamOffsetDistance; out_CamLoc.Z += Sin(IsoCamAngle * UnrRotToRad) * CamOffsetDistance; out_CamRot.Pitch = -1 * IsoCamAngle; out_CamRot.Yaw = 0; out_CamRot.Roll = 0; return true; } /** * 수정(조준 에러, 자동고정, 자동부착)되지 않은 순수한 기본 Aim Rotation 반환 * * @return base Aim rotation. */ simulated singular event Rotator GetBaseAimRotation() { local vector POVLoc; local rotator POVRot, tempRot; if(CameraType == CAM_TopDown || CameraType == CAM_Isometric) { tempRot = Rotation; tempRot.Pitch = 0; SetRotation(tempRot); POVRot = Rotation; POVRot.Pitch = 0; } else if(CameraType == CAM_SideScroller) { POVRot = Rotation; if( (Rotation.Yaw % 65535 > 16384 && Rotation.Yaw % 65535 < 49560) || (Rotation.Yaw % 65535 < -16384 && Rotation.Yaw % 65535 > -49560) ) { POVRot.Yaw = 32768; } else { POVRot.Yaw = 0; } if( POVRot.Pitch == 0 ) { POVRot.Pitch = RemoteViewPitch << 8; } } else { if( Controller != None && !InFreeCam() ) { Controller.GetPlayerViewPoint(POVLoc, POVRot); return POVRot; } else { POVRot = Rotation; if( POVRot.Pitch == 0 ) { POVRot.Pitch = RemoteViewPitch << 8; } } } return POVRot; } defaultproperties { CameraType=CAM_FirstPerson; bFollowPlayerRotation = false; CamOffsetDistance=384.0 IsoCamAngle=6420 //35.264 도 }
class UDNPlayerController extends UTPlayerController; state PlayerWalking { function ProcessMove(float DeltaTime, vector NewAccel, eDoubleClickDir DoubleClickMove, rotator DeltaRot) { local UDNPawn P; local Rotator tempRot; if( (Pawn != None) ) { P = UDNPawn(Pawn); if(P != none) { if(P.CameraType == CAM_SideScroller) { Pawn.Acceleration.X = -1 * PlayerInput.aStrafe * DeltaTime * 100 * PlayerInput.MoveForwardSpeed; Pawn.Acceleration.Y = 0; Pawn.Acceleration.Z = 0; tempRot.Pitch = P.Rotation.Pitch; tempRot.Roll = 0; if(Normal(Pawn.Acceleration) Dot Vect(1,0,0) > 0) { tempRot.Yaw = 0; P.SetRotation(tempRot); } else if(Normal(Pawn.Acceleration) Dot Vect(1,0,0) < 0) { tempRot.Yaw = 32768; P.SetRotation(tempRot); } } else { if ( (DoubleClickMove == DCLICK_Active) && (Pawn.Physics == PHYS_Falling) ) DoubleClickDir = DCLICK_Active; else if ( (DoubleClickMove != DCLICK_None) && (DoubleClickMove < DCLICK_Active) ) { if ( UTPawn(Pawn).Dodge(DoubleClickMove) ) DoubleClickDir = DCLICK_Active; } Pawn.Acceleration = newAccel; } if (Role == ROLE_Authority) { // 원격 클라이언트용 ViewPitch 업데이트 Pawn.SetRemoteViewPitch( Rotation.Pitch ); } } CheckJumpOrDuck(); } } } function UpdateRotation( float DeltaTime ) { local UDNPawn P; local Rotator DeltaRot, newRotation, ViewRotation; P = UDNPawn(Pawn); ViewRotation = Rotation; if (p != none && P.CameraType != CAM_SideScroller) { Pawn.SetDesiredRotation(ViewRotation); } // ViewRotation에 적용될 Delta 계산 if( P != none && P.CameraType == CAM_SideScroller ) { DeltaRot.Yaw = Pawn.Rotation.Yaw; } else { DeltaRot.Yaw = PlayerInput.aTurn; } DeltaRot.Pitch = PlayerInput.aLookUp; ProcessViewRotation( DeltaTime, ViewRotation, DeltaRot ); SetRotation(ViewRotation); ViewShake( deltaTime ); NewRotation = ViewRotation; NewRotation.Roll = Rotation.Roll; if (P != None && P.CameraType != CAM_SideScroller ) Pawn.FaceRotation(NewRotation, deltatime); } defaultproperties { }
예제 - 커스텀 카메라
기본 카메라 모듈
기본 카메라 모듈 클래스는 Object 클래스로부터 연장하며, 모든 카메라 모듈에 공통되는 프로퍼티 및 동작 전부를 정의합니다. 이를 소유하는 카메라로의 참조 뿐만 아니라 현재 마우스 커서 위치도 포함됩니다. 약간의 초기화 및 초기화해제 함수가 정의되어 있으나, 대다수의 함수는 나중에 PlayerController, Pawn, HHUD 클래스에서 오버라이딩됩니다. 이 클래스는 환경설정 가능하거나 지정된 카메라 모듈에서 일관되어야 하는 프로퍼티를 *Camera.ini 파일에서 찾을 수 있게 하기 위해 config(Camera) 지정자(specifier)를 통해 정의됩니다. 또한 실제로 사용하지는 못하게 하기 위해 abstract 로 정의됩니다. 특정 카메라에 대해 본을 떠서 만들 수 있는 템플릿에 가까우며, 그 자체를 실제로 사용할 수 있는 클래스는 아닙니다. UDNCameraModule.ucclass UDNCameraModule extends Object abstract config(Camera); //카메라 소유하기 var transient UDNPlayerCamera PlayerCamera; //모드 전용 초기화 function Init(); /** 카메라가 활성화되면 호출 */ function OnBecomeActive( UDNCameraModule OldCamera ); /** 카메라가 비활성화되면 호출 */ function OnBecomeInActive( UDNCameraModule NewCamera ); //새 카메라 위치 및 회전 계산 function UpdateCamera(Pawn P, UDNPlayerCamera CameraActor, float DeltaTime, out TViewTarget OutVT); //새 뷰 타겟 초기화 simulated function BecomeViewTarget( UDNPlayerController PC ); //줌인 처리 function ZoomIn(); //줌아웃 처리 function ZoomOut(); defaultproperties { }
커스텀 카메라
새로운 카메라 클래스는 기본 Camera 클래스에서 연장하며, 카메라 모듈을 처리하기 위해 몇몇 함수는 덮어쓰고 새 함수성을 추가합니다. 이 시스템에서 카메라의 주요 임무는, 이제 대부분의 계산을 처리하고 있기에 카메라 모듈에 대한 중간자 역할을 하는 것입니다. UDNPlayerCamera.ucclass UDNPlayerCamera extends Camera config(Camera); var UDNPlayerController PlayerOwner; //이 카메라를 소유하는 플레이어 콘트롤러 var UDNCameraModule CurrentCamera; //현재 사용중인 카메라 모드 var config string DefaultCameraClass; //기본 카메라 모드용 클래스 function PostBeginPlay() { local class<UDNCameraModule> NewClass; Super.PostBeginPlay(); // 카메라 모드 셋업 if ( (CurrentCamera == None) && (DefaultCameraClass != "") ) { //사용할 기본 카메라 클래스 구하기 NewClass = class<UDNCameraModule>( DynamicLoadObject( DefaultCameraClass, class'Class' ) ); //기본 카메라 생성 CurrentCamera = CreateCamera(NewClass); } } //소유하는 PlayerController에 대한 PlayerCamera 초기화 function InitializeFor(PlayerController PC) { //부모 초기화 Super.InitializeFor(PC); //플레이어 콘트롤러에 PlayerOwner 설정 PlayerOwner = UDNPlayerController(PC); } /** * 내부. 지정된 클래스의 새 카메라를 생성 및 초기화하고 오브젝트 참조를 반환. */ function UDNCameraModule CreateCamera(class<UDNCameraModule> CameraClass) { local UDNCameraModule NewCam; //새 카메라를 만들고 초기화 NewCam = new(Outer) CameraClass; NewCam.PlayerCamera = self; NewCam.Init(); //새/구 카메라의 활성/비활성 함수 호출 if(CurrentCamera != none) { CurrentCamera.OnBecomeInactive(NewCam); NewCam.OnBecomeActive(CurrentCamera); } else { NewCam.OnBecomeActive(None); } //새 카메라를 현재로 설정 CurrentCamera = NewCam; return NewCam; } /** * ViewTarget 질의 및 Point Of View(시점) 출력 * * @param OutVT 사용할 ViewTarget * @param DeltaTime 지난 카메라 업데이트 이후의 Delta Time(경과 시간(초)) */ function UpdateViewTarget(out TViewTarget OutVT, float DeltaTime) { local CameraActor CamActor; local TPOV OrigPOV; local Vector Loc, Pos, HitLocation, HitNormal; local Rotator Rot; local Actor HitActor; // 보간 도중엔 나가는 뷰타겟을 업데이트하지 않음 if( PendingViewTarget.Target != None && OutVT == ViewTarget && BlendParams.bLockOutgoing ) { return; } OrigPOV = OutVT.POV; // 뷰타겟의 기본 FOV(시야) OutVT.POV.FOV = DefaultFOV; // 카메라 액터를 통해 보기 CamActor = CameraActor(OutVT.Target); if( CamActor != None ) { CamActor.GetCameraView(DeltaTime, OutVT.POV); // CameraActor로부터 상 비율 집어내기 bConstrainAspectRatio = bConstrainAspectRatio || CamActor.bConstrainAspectRatio; OutVT.AspectRatio = CamActor.AspectRatio; // CameraActor가 사용된 PostProcess 세팅을 오버라이딩 하려는지 확인 CamOverridePostProcessAlpha = CamActor.CamOverridePostProcessAlpha; CamPostProcessSettings = CamActor.CamOverridePostProcess; } else { // Pawn ViewTarget에 카메라 위치를 기술할 기회를 제공 // Pawn이 카메라 뷰를 오버라이딩하지 않으면, 기본값으로 진행 if( Pawn(OutVT.Target) == None || !Pawn(OutVT.Target).CalcCamera(DeltaTime, OutVT.POV.Location, OutVT.POV.Rotation, OutVT.POV.FOV) ) { //Pawn이 제어를 원하지 않아 커스텀 모드를 가짐 if(CurrentCamera != none) { //카메라 업데이트를 처리하기 위한 모드 허용 CurrentCamera.UpdateCamera(Pawn(OutVT.Target), self, DeltaTime, OutVT); } //커스텀 모드 없음 - 기본 카메라 스타일 사용 else { switch( CameraStyle ) { case 'Fixed' : // 업데이트하지 않으며, 기존 카메라 위치는 저장하여 유지 // POV 저장, CalcCamera가 그걸 변경했는데도 여전히 거짓을 반환하는 경우 OutVT.POV = OrigPOV; break; case 'ThirdPerson' : // 간단한 3인칭 뷰 구현 case 'FreeCam' : case 'FreeCam_Default': Loc = OutVT.Target.Location; Rot = OutVT.Target.Rotation; //OutVT.Target.GetActorEyesViewPoint(Loc, Rot); if( CameraStyle == 'FreeCam' || CameraStyle == 'FreeCam_Default' ) { Rot = PCOwner.Rotation; } Loc += FreeCamOffset >> Rot; Pos = Loc - Vector(Rot) * FreeCamDistance; // @fixme, BlockingVolume.bBlockCamera=false 존중 HitActor = Trace(HitLocation, HitNormal, Pos, Loc, FALSE, vect(12,12,12)); OutVT.POV.Location = (HitActor == None) ? Pos : HitLocation; OutVT.POV.Rotation = Rot; break; case 'FirstPerson' : // 단순 일인칭, 뷰타겟 '눈'을 통한 뷰 default : OutVT.Target.GetActorEyesViewPoint(OutVT.POV.Location, OutVT.POV.Rotation); break; } } } } ApplyCameraModifiers(DeltaTime, OutVT.POV); // 카메라의 위치 및 회전 설정, 뷰타겟에 잠기지 않았을 경우를 처리하기 위해 SetRotation(OutVT.POV.Rotation); SetLocation(OutVT.POV.Location); } //카메라 모드로 뷰 타겟 초기화를 pass through simulated function BecomeViewTarget( PlayerController PC ) { CurrentCamera.BecomeViewTarget(UDNPlayerController(PC)); } //카메라 모드로 줌인을 pass through function ZoomIn() { CurrentCamera.ZoomIn(); } //카메라 모드로 줌아웃을 pass through function ZoomOut() { CurrentCamera.ZoomOut(); } defaultproperties { }
베이스 콘트롤 모듈
베이스 콘트롤 모듈 클래스는 Object 클래스로부터 연장하며, 모든 콘트롤 모듈에 공통이 될 프로퍼티와 작동 방식 전부를 정의합니다. 그를 소유하는 콘트롤러로의 참조뿐 아니라 현재 마우스 커서 위치도 포함합니다. 베이스 카메라 모듈처럼, 특정 유형에 필요할 지도 모르는 셋업이나 클린업을 허용하기 위해 초기화 및 초기화해제 함수가 약간 정의되어 있습니다. 클래스의 나머지는 플레이어의 이동과 조준도 처리하는 함수로 구성됩니다. 이 클래스는, 어느 프로퍼티도 환경설정 가능하도록 또는 *Control.ini 파일에서 보게 될 특정 콘트롤 모듈 내에서도 지속되도록 하기 위해 config(Control) 지정자를 사용하여 정의됩니다. 실제로 사용되지 않게 하기 위해 abstract 로도 정의됩니다. 특정 콘트롤 모듈의 견본으로 사용할 템플릿일 뿐이며, 그 자체를 사용할 일은 없을 클래스입니다. UDNControlModule.ucclass UDNControlModule extends Object abstract config(Control); //소유하는 콘트롤러로의 참조 var UDNPlayerController Controller; //모드-전용 초기화 function Init(); /** 카메라가 활성화될 때 호출 */ function OnBecomeActive( UDNControlModule OldModule ); /** 카메라가 비활성화될 때 호출 */ function OnBecomeInActive( UDNControlModule NewModule ); //Pawn 조준 회전 계산 simulated singular function Rotator GetBaseAimRotation(); //커스텀 플레이어 이동 처리 function ProcessMove(float DeltaTime, vector NewAccel, eDoubleClickDir DoubleClickMove, rotator DeltaRot); //콘트롤러 회전 계산 function UpdateRotation(float DeltaTime); defaultproperties { }
엔진 클래스 오버라이딩
여러 엔진 클래스, 주로 PlayerController, Pawn, HUD 클래스는 새로운 카메라 및 콘트롤 시스템과의 인터페이스 역할을 하기 위해서 연장될 필요가 있습니다. 이와 같은 새 클래스를 활용하기 위해서는 새 게임타입도 만들어야 할 겁니다.PlayerController 클래스
새로운 PlayerController 클래스는 사용되는 카메라 모듈의 유형을 바꾸기용은 물론 줌인이나 줌아웃용 exec 함수를 더합니다. (줌 함수의 작동 방법은 현재 카메라 모듈의 구현 방법에 따라 달라집니다.) 콘트롤 모듈로의 호출을 추가하기 위해 PlayerWalking (플레이어 걷기) 상태의 ProcessMove() (이동 처리) 함수 및 UpdateRotation() (회전 업데이트) 함수를 오버라이딩합니다. 마지막으로 카메라가 파괴되는 것을 막고 PlayerController가 새로운 커스텀 카메라(가 존재할 때 이)를 강제로 사용하게 하기 위해, GetPlayerViewPoint() (플레이어 시점 구하기) 함수를 오버라이딩하고 변경합니다. UDNPlayerController.ucclass UDNPlayerController extends UTPlayerController; var UDNControlModule ControlModule; //사용할 플레이어 콘트롤 모듈 var config string DefaultControlModuleClass; //플레이어 콘트롤 모듈용 기본 클래스 //클래스에 의해 다른 카메라로 전환하기 위한 exec 함수 exec function ChangeControls( string ClassName ) { local class<UDNControlModule> ControlClass; local UDNControlModule NewControlModule; ControlClass = class<UDNControlModule>( DynamicLoadObject( DefaultControlModuleClass, class'Class' ) ); if(ControlClass != none) { // 모듈을 PlayerController와 연결 NewControlModule = new(Outer) ControlClass; NewControlModule.Controller = self; NewControlModule.Init(); // 새/구 모듈의 활성/비활성 함수 호출 if(ControlModule != none) { ControlModule.OnBecomeInactive(NewControlModule); NewControlModule.OnBecomeActive(ControlModule); } else { NewControlModule.OnBecomeActive(None); } ControlModule = NewControlModule; } else { `log("Couldn't get control module class!"); // Control Class가 없는 건 괜찮습니다. PlayerController는 기본 큰트롤을 사용합니다. } } //클래스에 의해 다른 카메라로의 전환하기 위한 exec 함수 exec function ChangeCamera( string ClassName ) { local class<UDNCameraModule> NewClass; NewClass = class<UDNCameraModule>( DynamicLoadObject( ClassName, class'Class' ) ); if(NewClass != none && UDNPlayerCamera(PlayerCamera) != none) { UDNPlayerCamera(PlayerCamera).CreateCamera(NewClass); } } //줌인 exec exec function ZoomIn() { if(UDNPlayerCamera(PlayerCamera) != none) { UDNPlayerCamera(PlayerCamera).ZoomIn(); } } //줌아웃 exec exec function ZoomOut() { if(UDNPlayerCamera(PlayerCamera) != none) { UDNPlayerCamera(PlayerCamera).ZoomOut(); } } simulated function PostBeginPlay() { local class<UDNControlModule> ControlClass; local UDNControlModule NewControlModule; Super.PostBeginPlay(); ControlClass = class<UDNControlModule>( DynamicLoadObject( DefaultControlModuleClass, class'Class' ) ); if(ControlClass != none) { // 모듈을 PlayerController와 연결 NewControlModule = new(Outer) ControlClass; NewControlModule.Controller = self; NewControlModule.Init(); // 신/구 모듈의 활성/비활성 함수 호출 if(ControlModule != none) { ControlModule.OnBecomeInactive(NewControlModule); NewControlModule.OnBecomeActive(ControlModule); } else { NewControlModule.OnBecomeActive(None); } ControlModule = NewControlModule; } else { `log("Couldn't get control module class!"); // Control Class가 없는건 괜찮습니다. PlayerController는 기본 콘트롤을 사용합니다. } } state PlayerWalking { function ProcessMove(float DeltaTime, vector NewAccel, eDoubleClickDir DoubleClickMove, rotator DeltaRot) { //Controller가 UDNPlayerCamera를 가짐 if(ControlModule != none) { //커스텀 카메라의 플레이어 이동 덮어쓰기를 허용 ControlModule.ProcessMove(DeltaTime, NewAccel, DoubleClickMove, DeltaRot); } else { Super.ProcessMove(DeltaTime, NewAccel, DoubleClickMove, DeltaRot); } } } function UpdateRotation( float DeltaTime ) { //Controller가 UDNPlayerCamera를 가짐 if(ControlModule != none) { //커스텀 카메라의 회전 업데이트를 허용 ControlModule.UpdateRotation(DeltaTime); } else { Super.UpdateRotation(DeltaTime); } } /* GetPlayerViewPoint: 플레이어의 시점 반환 AI에게 이는 Pawn의 눈 시점 휴먼 플레이어에게 이는 카메라의 시점 */ simulated event GetPlayerViewPoint( out vector POVLocation, out Rotator POVRotation ) { local float DeltaTime; local UTPawn P; P = IsLocalPlayerController() ? UTPawn(CalcViewActor) : None; DeltaTime = WorldInfo.TimeSeconds - LastCameraTimeStamp; LastCameraTimeStamp = WorldInfo.TimeSeconds; // CameraActor 뷰 사용 지원 if ( CameraActor(ViewTarget) != None ) { if ( PlayerCamera == None ) { super.ResetCameraMode(); SpawnCamera(); } super.GetPlayerViewPoint( POVLocation, POVRotation ); } else { //카메라 부수지 말어!!! /* if ( PlayerCamera != None ) { PlayerCamera.Destroy(); PlayerCamera = None; } */ //카메라는 없고 뷰타겟이 있으면, 뷰타겟이 제어하게 if ( PlayerCamera == None && ViewTarget != None ) { POVRotation = Rotation; if ( (PlayerReplicationInfo != None) && PlayerReplicationInfo.bOnlySpectator && (UTVehicle(ViewTarget) != None) ) { UTVehicle(ViewTarget).bSpectatedView = true; ViewTarget.CalcCamera( DeltaTime, POVLocation, POVRotation, FOVAngle ); UTVehicle(ViewTarget).bSpectatedView = false; } else { ViewTarget.CalcCamera( DeltaTime, POVLocation, POVRotation, FOVAngle ); } if ( bFreeCamera ) { POVRotation = Rotation; } } //카메라도 뷰타겟도 없으면, 우리가 제어하게 else if(PlayerCamera == None) { CalcCamera( DeltaTime, POVLocation, POVRotation, FOVAngle ); return; } //카메라가 있으면, 카메라가 제어하게 else { POVLocation = PlayerCamera.ViewTarget.POV.Location; POVRotation = PlayerCamera.ViewTarget.POV.Rotation; FOVAngle = PlayerCamera.ViewTarget.POV.FOV; } } // 뷰 흔들기 적용 POVRotation = Normalize(POVRotation + ShakeRot); POVLocation += ShakeOffset >> Rotation; if( CameraEffect != none ) { CameraEffect.UpdateLocation(POVLocation, POVRotation, GetFOVAngle()); } // 결과 캐시 CalcViewActor = ViewTarget; CalcViewActorLocation = ViewTarget.Location; CalcViewActorRotation = ViewTarget.Rotation; CalcViewLocation = POVLocation; CalcViewRotation = POVRotation; if ( P != None ) { CalcEyeHeight = P.EyeHeight; CalcWalkBob = P.WalkBob; } } defaultproperties { CameraClass=class'UDNExamples.UDNPlayerCamera' MatineeCameraClass=class'UDNExamples.UDNPlayerCamera' }
Pawn 클래스
새로운 Pawn 클래스는 단지 거짓을 반환하기 위해서 CalcCamera() 함수를 오버라이딩합니다. 그래야 새로운 카메라 시스템이 항상 카메라 location 및 position을 제어할 수 있습니다. BecomeViewTarget() 및 GetBaseAimRotation() (뷰타겟 되기 및 기본 조준 회전 구하기) 함수는 그 함수성 처리를 카메라 및 콘트롤 시스템에 각각 전달하기 위해 오버라이딩됩니다. UDNPawn.ucclass UDNPawn extends UTPawn; /* BecomeViewTarget 이 액터가 ViewTarget이 될 때 Camera에 의해 호출 */ simulated event BecomeViewTarget( PlayerController PC ) { local UDNPlayerController UDNPC; UDNPC = UDNPlayerController(PC); //Pawn이 UDNPlayerController에 의해 제어되며, UDNPlayerCamera를 가짐 if(UDNPC != none && UDNPlayerCamera(UDNPC.PlayerCamera) != none) { //커스텀 카메라의 메시 표시여부 등의 제어를 허용 UDNPlayerCamera(UDNPC.PlayerCamera).BecomeViewTarget(UDNPC); } else { Super.BecomeViewTarget(PC); } } /** * 이 폰을 보고있을 때 카메라 시점 계산 * * @param fDeltaTime 지난 업데이트 이후 경과 시간 * @param out_CamLoc 카메라 위치 * @param out_CamRot 카메라 회전 * @param out_FOV 시야 * * @return Pawn이 카메라 시야를 제공해야 하면 참 */ simulated function bool CalcCamera( float fDeltaTime, out vector out_CamLoc, out rotator out_CamRot, out float out_FOV ) { //커스텀 카메라의 그 위치 및 회전을 제어를 허용하기 위해 거짓 반환 return false; } /** * 조정 없는 기본 Aim Rotation(조준 에러, 자동조준, 부착 등등 없이 깨끗한 초기 상태의 조준 회전값) 반환 * * @return base Aim rotation. */ simulated singular event Rotator GetBaseAimRotation() { local vector POVLoc; local rotator POVRot; local UDNPlayerController PC; PC = UDNPlayerController(Controller); //Pawn이 UDNPlayerController에 의해 제어되며, UDNPlayerCamera를 가짐 if(PC != none && PC.ControlModule != none) { //커스텀 카메라의 조준 회전 제어 허용 return PC.ControlModule.GetBaseAimRotation(); } else { if( Controller != None && !InFreeCam() ) { Controller.GetPlayerViewPoint(POVLoc, POVRot); return POVRot; } else { POVRot = Rotation; if( POVRot.Pitch == 0 ) { POVRot.Pitch = RemoteViewPitch << 8; } return POVRot; } } } defaultproperties { }
GameInfo 클래스
새로운 게임타입 클래스는 사용할 HUD, Pawn, PlayerController를 새로 설정하는 UTDeathMatch의 기본 연장입니다. 또한 여기에 지정된 HUD 클래스가 UTGFxHUDWrapper 대신 사용될 수 있도록 bUseClassicHUD 를 True로 설정하며, 이 불리언을 설정하지 않고서는 하드코딩해야만 이 클래스를 사용할 수 있습니다. UDNGame.ucclass UDNGame extends UTDeathMatch; defaultproperties { DefaultPawnClass=class'UDNExamples.UDNPawn' PlayerControllerClass=class'UDNExamples.UDNPlayerController' MapPrefixes[0]="UDN" }
카메라 모듈 예제
새로운 카메라 프레임워크를 사용하는 예제로써, 내려보기 카메라를 설정하겠습니다. 카메라 모듈 새로 만들기는 주로 기본 카메라 모듈 클래스에 정의된 함수를 구현하는 일입니다. 이 작업 중 대부분은 위의 CalcCamera() 예제를 살펴 본 경우라면 거의 비슷할 겁니다. UDNCameraModule_TopDown.ucclass UDNCameraModule_TopDown extends UDNCameraModule; var float CamAltitude; //플레이어로부터의 실제 카메라 높이 오프셋 var float DesiredCamAltitude; //카메라를 이동시킬 새 높이 오프셋 var float MaxCamAltitude; //카메라와 플레이어 사이의 최대 오프셋 var float MinCamAltitude; //카메라와 플레이어 사이의 최소 오프셋 var float CamZoomIncrement; //마우스휠 클릭마다 몇 유닛만큼 줌 시킬지 //새 카메라 위치 및 회전 계산 function UpdateCamera(Pawn P, UDNPlayerCamera CameraActor, float DeltaTime, out TViewTarget OutVT) { //거기 없으면 새 카메라 오프셋으로 보간 if(CamAltitude != DesiredCamAltitude) { CamAltitude += (DesiredCamAltitude - CamAltitude) * DeltaTime * 3; } //높이(Z) 오프셋으로 카메라를 플레이어에 맞춤 OutVT.POV.Location = OutVT.Target.Location; OutVT.POV.Location.Z += CamAltitude; //카메라 회전 설정 - 아래로 향하게 OutVT.POV.Rotation.Pitch = -16384; OutVT.POV.Rotation.Yaw = 0; OutVT.POV.Rotation.Roll = 0; } //새 뷰 타겟 초기화 simulated function BecomeViewTarget( UDNPlayerController PC ) { if (LocalPlayer(PC.Player) != None) { // 플레이어 메시 보이게 설정 PC.SetBehindView(true); UDNPawn(PC.Pawn).SetMeshVisibility(PC.bBehindView); PC.bNoCrosshair = true; } } function ZoomIn() { // 카메라 높이 낮추기 DesiredCamAltitude -= CamZoomIncrement; // 카메라 높이 한계치로 고정 DesiredCamAltitude = FMin(MaxCamAltitude, FMax(MinCamAltitude, DesiredCamAltitude)); } function ZoomOut() { // 카메라 높이 높이기 DesiredCamAltitude += CamZoomIncrement; // 카메라 높이 한계치로 고정 DesiredCamAltitude = FMin(MaxCamAltitude, FMax(MinCamAltitude, DesiredCamAltitude)); } defaultproperties { CamAltitude=384.0 DesiredCamAltitude=384.0 MaxCamAltitude=1024.0 MinCamAltitude=160.0 CamZoomIncrement=96.0 }
콘트롤 모듈 예제
UDNControlModule_TopDown.ucclass UDNControlModule_TopDown extends UDNControlModule; //Pawn 조준 회전 계산 simulated singular function Rotator GetBaseAimRotation() { local rotator POVRot; //Pawn이 향하는 곳을 조준 - 상하(pitch) 잠금 POVRot = Controller.Pawn.Rotation; POVRot.Pitch = 0; return POVRot; } //커스텀 플레이어 이동 처리 function ProcessMove(float DeltaTime, vector NewAccel, eDoubleClickDir DoubleClickMove, rotator DeltaRot) { if( Controller.Pawn == None ) { return; } if (Controller.Role == ROLE_Authority) { // 원격 클라이언트용 ViewPitch 업데이트 Controller.Pawn.SetRemoteViewPitch( Controller.Rotation.Pitch ); } Controller.Pawn.Acceleration = NewAccel; Controller.CheckJumpOrDuck(); } //콘트롤러 회전 계산 function UpdateRotation(float DeltaTime) { local Rotator DeltaRot, NewRotation, ViewRotation; ViewRotation = Controller.Rotation; //폰을 커서 방향으로 회전 if (Controller.Pawn!=none) Controller.Pawn.SetDesiredRotation(ViewRotation); DeltaRot.Yaw = Controller.PlayerInput.aTurn; DeltaRot.Pitch = 0; Controller.ProcessViewRotation( DeltaTime, ViewRotation, DeltaRot ); Controller.SetRotation(ViewRotation); NewRotation = ViewRotation; NewRotation.Roll = Controller.Rotation.Roll; if ( Controller.Pawn != None ) Controller.Pawn.FaceRotation(NewRotation, DeltaTime); } defaultproperties { }
환경설정 파일
아래 파일 전부는 UDKgame/Config 디렉토리에 위치해야 합니다. 새로 생긴 몇가지는 만들어야 할 겁니다. 나머지는 새 환경설정 세팅을 포함하도록 변경하기만 하면 됩니다. DefaultCamera.ini 파일은 새 카메라 클래스에 있는 다양한 환경설정 변수용 값으로 채워야 합니다. 이 예제는 기본 카메라 모듈 클래스 세팅만으로 구성되어 있습니다. DefaultCamera.ini[UDNExamples.UDNPlayerCamera] DefaultCameraClass=UDNExamples.UDNCameraModule_TopDown
... [Engine.GameInfo] DefaultGame=UDNExamples.UDNGame DefaultServerGame=UDNExamples.UDNGame ... [UDNExamples.UDNPlayerController] DefaultControlModuleClass=UDNExamples.UDNControlModule_TopDown