TIL — 2026-04-20
오늘 한 일 요약
- 7번 과제 도전 구현 마무리 — C++ 6DOF 드론 Pawn 물리 구현 완성
- 8번 과제 Void 구현계획 + VoidUnreal C++ 모듈 분리
- Ch3 팀플 MVP 브레인스토밍 작성
IA Value Type 변경
생성 직후 기본값이 Digital(bool) 이므로 먼저 바꿔야 입력값이 제대로 넘어온다.
| IA | Value Type |
|---|
| IA_Move | Axis2D (Vector2D) |
| IA_Look | Axis2D (Vector2D) |
| IA_MoveUp | Axis1D (float) |
| IA_Roll | Axis1D (float) |
IMC_Drone 키 매핑
| IA | 키 | Modifier |
|---|
| IA_Move | W | 없음 |
| | S | Negate |
| | D | Swizzle (YXZ) |
| | A | Swizzle (YXZ) + Negate |
| IA_Look | Mouse XY 2D-Axis | 없음 |
| IA_MoveUp | Space | 없음 (+1) |
| | Left Shift | Negate (-1) |
| IA_Roll | Mouse Wheel Axis | 없음 |
핵심: Triggers / Modifiers는 IA 에셋이 아닌 IMC에서 키별로 설정한다.
ETriggerEvent::Triggered = 누르는 매 프레임 호출 (기본값, IA에서 따로 설정 안 해도 됨)
2. C++ Pawn 구조 정리
1
2
3
4
| BoxComp (루트, 반크기 XY=50 / Z=15 — 드론 형태에 맞게 납작한 박스)
└─ MeshComp (SM_Drone)
└─ SpringArmComp (400cm)
└─ CameraComp
|
AutoPossessPlayer = EAutoReceiveInput::Player0 — PlayerController 없이 바로 빙의
bUsePawnControlRotation = false — 카메라가 Pawn 회전을 따라가지 않음
3. 물리 구현 — 중력 + 착지 스냅
CheckGround
1
2
3
4
5
6
7
8
9
10
11
| // 발밑 (박스 반높이 Z + 1cm) LineTrace로 착지 여부 반환
bool ANBC_AssignmentPawn::CheckGround(FHitResult& OutHit)
{
const float HH = BoxComp->GetScaledBoxExtent().Z;
FCollisionQueryParams Params;
Params.AddIgnoredActor(this);
return GetWorld()->LineTraceSingleByChannel(
OutHit, GetActorLocation(),
GetActorLocation() + FVector(0, 0, -(HH + 1.f)),
ECC_Visibility, Params);
}
|
Tick 물리 루프
1
2
3
4
5
6
7
8
9
10
11
12
13
| 매 프레임:
CheckGround → bIsGrounded
if 착지:
FallVelocity = 0, Z 스냅
else:
if bAscending:
중력 누적 억제 (Space 누르는 동안)
else:
FallVelocity += GravityZ * DeltaTime
예측 위치(PredictedLoc)에서 LineTrace
충돌 → 이동 전 스냅 (바닥 뚫기 방지)
없으면 → AddActorWorldOffset
bAscending = false (매 프레임 리셋)
|
4. 헤맨 점과 해결
Space 바 통통 튀는 현상
- 원인:
FallVelocity가 한 프레임에 크게 누적돼 드론이 바닥 아래로 터널링 → 스냅이 위로 밀어내면서 반복 - 해결: 다음 프레임 위치를 먼저 예측(
PredictedLoc)하고 그 지점에서 LineTrace → 충돌 시 이동 전에 스냅
착지 중 Shift 하강 / 마우스 Pitch 차단
- Shift 하강 차단:
if (bIsGrounded && V < 0.f) return; — 지상에서 아래로 파고드는 입력 무시 - Pitch 차단:
const float Pitch = bIsGrounded ? 0.f : V.Y; — 착지 중 상하 회전 억제, 좌우(Yaw)는 허용
Space 바 지속 상승 불가
- 원인:
GravityZ = -980이 MoveSpeed = 600을 이겨서 약 0.6초 후 하강 전환 - 해결:
bAscending 플래그 — Space 입력 프레임에 true 세팅, Tick에서 중력 누적 건너뜀
Space 바 기울기 방향이 아닌 월드 Z로만 이동
- 원인:
AddActorLocalOffset(GetActorUpVector() * ...) — GetActorUpVector()는 월드 공간 벡터인데 로컬 오프셋으로 넘겨서 이중 회전 발생. 드론이 수평일 때는 우연히 일치해 보임 - 해결:
AddActorWorldOffset(GetActorUpVector() * V * MoveSpeed * DT, true) 로 변경
WASD 방향이 이상함 (카메라 회전 후)
- 원인:
GetActorForwardVector()가 Pitch 기울기를 포함 → 드론이 기울어지면 W가 비스듬하게 이동 - 해결: Yaw만 추출한 회전 행렬에서 Forward/Right 계산
1
2
3
4
| const FRotator YawOnly = FRotator(0.f, GetActorRotation().Yaw, 0.f);
const FVector Forward = FRotationMatrix(YawOnly).GetScaledAxis(EAxis::X);
const FVector Right = FRotationMatrix(YawOnly).GetScaledAxis(EAxis::Y);
AddActorWorldOffset((Forward * V.X + Right * V.Y) * MoveSpeed * DeltaTime, true);
|
CapsuleComponent → BoxComponent 교체
- 이유: 캡슐은 반지름·반높이만 조절 가능 — 드론처럼 X/Y/Z를 각각 다르게 맞춰야 할 때 부적합
- 방법:
#include "Components/BoxComponent.h", SetBoxExtent(FVector(50, 50, 15)), 반높이 참조는 GetScaledBoxExtent().Z - 장점: 에디터 Details 패널에서 XYZ 개별 조정 가능 → 메시에 정확히 피팅
LineTrace 시작점이 액터 중심 → 바닥 뚫기 가능성
- 원인:
CheckGround 트레이스가 GetActorLocation()(중심)에서 시작 — 드론이 기울어지면 실제 박스 바닥이 중심보다 낮은 다른 위치에 있어서 감지가 틀어짐 - 해결: 박스 로컬 바닥점
(0, 0, -HH) 을 GetActorTransform().TransformPosition() 으로 월드 변환한 위치에서 1cm만 내려서 트레이스
1
2
| const FVector WorldBottom = GetActorTransform().TransformPosition(FVector(0.f, 0.f, -BoxComp->GetScaledBoxExtent().Z));
GetWorld()->LineTraceSingleByChannel(OutHit, WorldBottom, WorldBottom + FVector(0, 0, -1.f), ...);
|
- 스냅 오프셋도 수정: 기울어진 상태에서는
HH(로컬 Z 반크기)가 실제 월드 Z 거리와 다름 → BottomOffset = Location.Z - WorldBottom.Z 로 계산
1
2
| const float BottomOffset = Location.Z - WorldBottom.Z; // 기울기 반영
SnapLoc.Z = ImpactPoint.Z + BottomOffset;
|
5. 오늘 배운 것 정리
- LineTrace 충돌 채널:
ECC_Visibility 기준으로 바닥이 막혀있어야 감지됨. 콜리전 없는 Static Mesh는 무시된다. - 바닥 뚫기 방지는 예측이 핵심: 이동 후 스냅이 아니라 이동 전에 예측 위치를 체크해야 바운스가 없다.
- bAscending 패턴: 입력 콜백에서 플래그만 세팅하고 실제 처리는 Tick에서 — 입력과 물리를 분리하는 깔끔한 구조.
- FRotationMatrix(YawOnly): 6DOF 회전 중 수평 이동만 뽑아낼 때 유용. Pitch/Roll 기울어짐과 무관하게 카메라 방향으로 이동 가능.
- AddActorLocalOffset vs AddActorWorldOffset:
GetActorUpVector()는 월드 공간 벡터이므로 AddActorLocalOffset에 넘기면 이중 회전이 적용된다. 액터 Up 방향 이동은 반드시 AddActorWorldOffset(GetActorUpVector() * ...) 으로 써야 한다. - CapsuleComponent → BoxComponent: 드론처럼 납작한 형태는 캡슐보다 박스가 적합.
SetBoxExtent(FVector(X, Y, Z))로 반크기를 지정하고, 반높이는 GetScaledBoxExtent().Z로 읽는다. 에디터 Details 패널에서 XYZ를 개별 조정 가능해 메시에 정확히 맞출 수 있다. - 착지 중 입력 제한:
bIsGrounded 플래그를 입력 핸들러에서 체크해 Shift(하강)와 마우스 Pitch(상하 회전)를 차단. 단순한 early return / 조건식으로 자연스러운 지상 동작을 구현할 수 있다. - LineTrace는 반드시 월드 변환된 바닥점에서:
GetActorLocation()(중심)에서 쏘면 기울어진 액터에서 실제 바닥 접촉점을 놓친다. GetActorTransform().TransformPosition(로컬_바닥점) 으로 월드 바닥 위치를 구한 뒤 트레이스해야 정확하다. 스냅 오프셋도 Location.Z - WorldBottom.Z 로 기울기를 반영해야 한다.
6. 8번 과제 — Void Tier 0 프로토타입 구현계획
개요
| 항목 | 내용 |
|---|
| 프로젝트 | Void — 쿼터뷰 좀비 서바이벌 익스트랙션 |
| 카메라 | 쿼터뷰 Top-Down 45° |
| 마감 | 2026-04-30 (10일) |
3웨이브 구조 (익스트랙션 루프 재해석)
| Wave | 내용 | 시간 |
|---|
| Wave 1 진입 | 저위험 파밍, 좀비 소수 | 3분 |
| Wave 2 심층 | 중위험 파밍, 사이렌 이벤트(좀비 몰림) | 4분 |
| Wave 3 탈출 | 차량 부품 3개 수집 → 장착 → 시동 | 4~5분 |
탈출 성공 시만 점수 인정 — Void 익스트랙션 철학.
타르코프식 무게 시스템
1
2
3
4
5
| WeightRatio = TotalWeight / MaxCarry (MaxCarry = 30kg)
이동속도 배수 = 1.0 - (WeightRatio × 0.6) // 최대 60% 감소
소음 배수 = 1.0 + (WeightRatio × 1.2) // 최대 120% 증가
스태미나 소모 = 1.0 + (WeightRatio × 1.0) // 최대 2배
|
- 배터리(8kg) + 연료통(12kg) 동시 소지 = 이속 −40%, 소음 +80%
- 부품 합산 22kg → 한 번에 못 옮김 → 전략적 동선 필수
VoidUnreal C++ 모듈 구조 (36개 파일)
1
2
3
4
5
6
7
8
| Source/VoidUnreal/
├── Core/ AVOIDGameMode / AVOIDGameState / AVOIDPlayerController / UVOIDGameInstance
├── Characters/ AVOIDBaseCharacter / AVOIDPlayerCharacter / AVOIDZombieCharacter
├── AI/ AVOIDZombieAIController
├── Components/ UVOIDHealthComponent / UVOIDInventoryComponent / UVOIDNoiseComponent / UVOIDDebuffComponent
├── Items/ UVOIDItemDataAsset / IVOIDItemInterface / AVOIDPickupBase / AVOIDVehiclePart
├── Waves/ FVOIDWaveData / AVOIDSpawnVolume
└── UI/ UVOIDHUDWidget
|
네임 컨벤션: VOID 대문자 접두사, VOIDUNREAL_API 매크로, TObjectPtr<T> 포인터 기본형.
“Noise is Currency” 구현 흐름:
1
2
3
4
| 입력 → PlayerCharacter (발걸음·사격·픽업)
→ NoiseComponent::EmitNoise(Source, WeightMultiplier)
→ OnNoiseChanged → HUD 업데이트
→ BroadcastNoise(Radius) → 좀비 탐지
|
과적 상태이면 소음이 최대 2.2× 증폭 → 좀비가 더 잘 몰림.
7. Ch3 팀플 MVP 브레인스토밍
현재 상태 진단
- ✅ 컨셉: 좀비 TPS + 뱀서 장르 융합
- ⚠️ 문제: “이 게임만의 시그니처 훅”이 없음
추천 조합
| 축 | 선택 | 이유 |
|---|
| 세계관 (A) | A2 K-좀비 (서울 지하철/아파트) | 한국 개발자 팀 — 익숙한 공포 |
| 주인공 (B) | B3 감염된 각성자 | 체력 시스템에 서사 내장, 감염도 메카닉 직결 |
| 시그니처 (C) | C1 처형 연쇄 + C3 감염도 타이머 | SM2 타격감 + 뱀서 생존 리듬 |
| 무기 (F) | F3 하이브리드 (근접 고정 + 원거리 카드) | 4주 스코프에 적합 |
| 이동 (G) | G1 단순 대시 (쿨다운 1초, 무적 없음) | 감염 타이머로 긴장감 이미 확보 |
| 카드 트리거 (H) | H1 웨이브 클리어 후 | 감염 타이머 리듬 보존 |
| 워크플로우 (W) | W3 병렬 타임박스 | 04/21~23 에셋 탐색 + 초안 병행 |
게임 가제 “Infected”
서울 한복판, 당신도 이미 감염되었다. 좀비를 처형할수록 인간성이 회복되지만, 시간이 흐를수록 감염도는 올라간다. 5웨이브를 버텨 살아남거나 — 스스로 괴물이 되거나.
한 줄 피치: “감염 타이머를 안은 처형 액션 서바이벌”
다음 액션
- 04/21~23: 에셋 탐색 타임박스 (좀비/환경/무기/VFX — 각자 후보 3개)
- 04/23: 킥오프 회의 — A~H 11개 항목 확정