TIL
06.12 TIL
2026. 6. 12. 20:00

일정표

시간 할 일 비고
08:00~10:00 코드카타, Atanny, 프로그래머스  
10:00~13:00 언리얼 3D 강의 수강  
14:00~18:00 언리얼 3D 강의 수강  
19:00~21:00 오늘의 수학, TIL  
21:00~22:00 운동  
22:00~23:00 개인 공부  

*오늘의 코드카타*

 

문제. 직사각형 별찍기

더보기

이 문제에는 표준 입력으로 두 개의 정수 n과 m이 주어집니다.
별(*) 문자를 이용해 가로의 길이가 n, 세로의 길이가 m인 직사각형 형태를 출력해보세요

#include <iostream>

using namespace std;

int main(void) {
    int n;
    int m;
    cin >> n >> m;
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            cout << "*";
        }
        cout << "\n";
    }
    return 0;
}

(1) 기본 for 반복문을 이용한 풀이

#include <iostream>
#include <string>

using namespace std;

int main(void) {
    int n, m;
    cin >> n >> m;
    for (int i = 0; i < m; i++) {
        cout << string(n, '*') << '\n';
    }
    return 0;
}

(2) string 헤더의 문자 반복기능 사용하기

string(반복 횟수, '반복 문자') 형태로 사용할 수 있다. 단 사용할 때 작은 따옴표 사용해야 한다는 것을 조심.


*프로그래머스*

1. [입문] 편지

더보기

머쓱이는 할머니께 생신 축하 편지를 쓰려고 합니다. 할머니가 보시기 편하도록 글자 한 자 한 자를 가로 2cm 크기로 적으려고 하며, 편지를 가로로만 적을 때, 축하 문구 message를 적기 위해 필요한 편지지의 최소 가로길이를 return 하도록 solution 함수를 완성해주세요

#include <string>

using namespace std;

int solution(string message) {
    int answer = 0;
    int count = message.length();
    answer = count * 2;
    return answer;
}

(1) 가장 기초적인 풀이

 

2. [입문] 가장 큰 수 찾기

더보기

정수 배열 array가 매개변수로 주어질 때, 가장 큰 수와 그 수의 인덱스를 담은 배열을 return 하도록 solution 함수를 완성해보세요.

#include <vector>

using namespace std;

vector<int> solution(vector<int> array) {
    int max_value = array[0];
    int max_index = 0;
    
    for (int i = 1; i < array.size(); i++) {
        if (array[i] > max_value) {
            max_value = array[i];
            max_index = i;
        }
    }
    return {max_value, max_index};
}

(1) compare 방식 사용해서 큰 값이 나올때마다 갱신하기

 

#include <vector>
#include <algorithm>

using namespace std;

vector<int> solution(vector<int> array) {
    vector<pair<int, int>> v;
    for (int i = 0; i < array.size(); i++) {
        v.push_back({array[i], i});
    }
    
    sort(v.begin(), v.end());
    
    return {v.back().first, v.back().second};
}

(2) sort 오름차순 사용하기

이 경우 주의할 점은 오름차순으로 정렬하면 인덱스를 잃어버리게 된다는 것. 따라서 값과 원래 인덱스를 묶어서 저장한 뒤 정렬해야 한다. 이때, pair<>가 사용된다. 이번 코딩테스트에 맞는 문제는 아니지만 데이터 매핑 구조를 이해하고 연습하기 위해서 활용해보았다. 

오름차순 정렬된 v에서 맨 마지막 원소를 꺼내오면 된다. 이때 v.back().first, v.back().second 를 사용한다.

 

#include <vector>
#include <algorithm>
#include <iterator>

using namespace std;

vector<int> solution(vector<int> array) {
    auto it = max_element(array.begin(), array.end());
    
    int max_val = *it; 
    
    int max_idx = distance(array.begin(), it); 
    
    return {max_val, max_idx};
}

(3) max_element 사용하기 (반대 case는 min_element)

algorithm 헤더의 max_element와 인덱스를 구하기 위한 iterator 헤더의 distance()를 사용하는 방법.

max_element는 해당 인덱스를 반환하는 것이 아니라 해당 배열의 최댓값을 가리키는 반복자를 반환한다.

이후 max_val = *it를 하면 해당 반복자가 가리키는 값을 max_val에 담을 수 있게 된다. 이후 distance(array.begin(), it)로 최댓값을 가지고 있는 인덱스를 구한뒤, 두 값을 담은 배열로 정답을 반환한다.

 

 

3. [입문] 문자열 계산하기

더보기

my_string은 "3 + 5"처럼 문자열로 된 수식입니다. 문자열 my_string이 매개변수로 주어질 때, 수식을 계산한 값을 return 하는 solution 함수를 완성해주세요

연산자는 +, -만 존재합니다.

문자열의 시작과 끝에는 공백이 없습니다.

0으로 시작하는 숫자는 주어지지 않습니다.

잘못된 수식은 주어지지 않습니다.

5 ≤ my_string의 길이 ≤ 100

my_string을 계산한 결과값은 1 이상 100,000 이하입니다.

my_string의 중간 계산 값은 -100,000 이상 100,000 이하입니다.

계산에 사용하는 숫자는 1 이상 20,000 이하인 자연수입니다.

my_string에는 연산자가 적어도 하나 포함되어 있습니다.

return type 은 정수형입니다.

my_string의 숫자와 연산자는 공백 하나로 구분되어 있습니다.

#include <string>
#include <vector>
#include <sstream>

using namespace std;

int solution(string my_string) {
    stringstream ss(my_string);
    int answer = 0;
    int num_next;
    char op;
    
    ss >> answer;
    
    while (ss >> op >> num_next) {
        if (op == '+') {
            answer += num_next;
        } else if (op == '-') {
            answer -= num_next;
        }
    }
    return answer;
}

(1) stringstream을 이용하여 문자열을 파싱할 수 있다. 공백을 기준으로 알아서 문자열을 분리해서 cin >> a >> b 형식으로 사용할 수 있게 만든다.

while (ss >> op >> num_next) 조건문으로, op 다음에 숫자가 온다면 계속 계산을 진행하게 한다. 만약 읽어올 데이터가 더이상 없으면 계산을 종료하고 최종 answer을 반환한다.

 

 

4. [입문] 배열의 유사도

더보기

두 배열이 얼마나 유사한지 확인해보려고 합니다. 문자열 배열 s1과 s2가 주어질 때 같은 원소의 개수를 return하도록 solution 함수를 완성해주세요

#include <string>
#include <vector>

using namespace std;

int solution(vector<string> s1, vector<string> s2) {
    int answer = 0;
    
    for (int i = 0; i < s1.size(); ++i) {
        for (int j = 0; j < s2.size(); ++j) {
            if (s1[i] == s2[j]) {
                answer++;
                break;
            }
        }
    }
    return answer;
}

(1) 반복문 두개를 겹쳐서 풀기.

#include <string>
#include <vector>
#include <algorithm>

using namespace std;

int solution(vector<string> s1, vector<string> s2) {
    int answer = 0;
    
    for (const string& str : s1) {
        if (find(s2.begin(), s2.end(), str) != s2.end()) {
            answer++;
        }
    }
    return answer;
}

(2) find를 사용해서 좀 더 C++ 스럽게 풀자.

 

5. [기초] 홀수 vs 짝수

더보기

정수 리스트 num_list가 주어집니다. 가장 첫 번째 원소를 1번 원소라고 할 때, 홀수 번째 원소들의 합과 짝수 번째 원소들의 합 중 큰 값을 return 하도록 solution 함수를 완성해주세요. 두 값이 같을 경우 그 값을 return합니다.

#include <vector>

using namespace std;

int solution(vector<int> num_list) {
    int answer = 0;
    
    int odd_sum = 0;
    int even_sum = 0;
    
    for (int i = 0; i < num_list.size(); i+=2) {
        odd_sum += num_list[i];
    }
    for (int j = 1; j < num_list.size(); j+=2) {
        even_sum += num_list[j];
    }
    
    if (odd_sum >= even_sum) {
        answer = odd_sum;
    } else {
        answer = even_sum;
    }
    return answer;
}

(1) 기초 방식으로 푸는 방법

#include <vector>
#include <algorithm>

using namespace std;

int solution(vector<int> num_list) {
    int odd_sum = 0;
    int even_sum = 0;
    
    for (int i = 0; i < num_list.size(); ++i) {
        if (i % 2 == 0) {
            odd_sum += num_list[i];
        } else {
            even_sum += num_list[i];
        }
    }
    return max(odd_sum, even_sum);
}

(2) 홀수 짝수는 % 2 == 0 으로도 나눌 수 있으므로 사용해서 코드를 줄이고, max(a, b)를 사용해서 더 큰 값을 반환받는다. 이 때 algorithm 헤더가 필요하다.

 

 

6. [기초] 5명씩

더보기

최대 5명씩 탑승가능한 놀이기구를 타기 위해 줄을 서있는 사람들의 이름이 담긴 문자열 리스트 names가 주어질 때, 앞에서 부터 5명씩 묶은 그룹의 가장 앞에 서있는 사람들의 이름을 담은 리스트를 return하도록 solution 함수를 완성해주세요. 마지막 그룹이 5명이 되지 않더라도 가장 앞에 있는 사람의 이름을 포함합니다

#include <string>
#include <vector>

using namespace std;

vector<string> solution(vector<string> names) {
    vector<string> answer;
    
    for (int i = 0; i < names.size(); i+=5) {
        answer.emplace_back(names[i]);
    }
    return answer;
}

 

 

 

7. [기초] 할 일 목록

더보기

오늘 해야 할 일이 담긴 문자열 배열 todo_list와 각각의 일을 지금 마쳤는지를 나타내는 boolean 배열 finished가 매개변수로 주어질 때, todo_list에서 아직 마치지 못한 일들을 순서대로 담은 문자열 배열을 return 하는 solution 함수를 작성해 주세요.

1 ≤ todo_list의 길이 1 ≤ 100

2 ≤ todo_list의 원소의 길이 ≤ 20

todo_list의 원소는 영소문자로만 이루어져 있습니다.

todo_list의 원소는 모두 서로 다릅니다.

finished[i]는 true 또는 false이고 true는 todo_list[i]를 마쳤음을, false는 아직 마치지 못했음을 나타냅니다.

아직 마치지 못한 일이 적어도 하나 있습니다

#include <string>
#include <vector>

using namespace std;

vector<string> solution(vector<string> todo_list, vector<bool> finished) {
    vector<string> answer;
    answer.reserve(todo_list.size());
    
    for (int i = 0; i < finished.size(); ++i) {
        if (finished[i] == false) {
            answer.emplace_back(todo_list[i]);
        }
    } 
    return answer;
}

finished[i] == false 이렇게 쓰지 말고 !finished[i] 라고 쓰는 습관을 들이자. 가독성도 좋아지고, 실제 언리얼 C++ 작성할 때에도 많이 사용된다.

 

 

8. [기초] n보다 커질 때까지 구하기

더보기

정수 배열 numbers와 정수 n이 매개변수로 주어집니다. numbers의 원소를 앞에서부터 하나씩 더하다가 그 합이 n보다 커지는 순간 이때까지 더했던 원소들의 합을 return 하는 solution 함수를 작성해 주세요

1 ≤ numbers의 길이 ≤ 100

1 ≤ numbers의 원소 ≤ 100

0 ≤ n < numbers의 모든 원소의 합

#include <vector>

using namespace std;

int solution(vector<int> numbers, int n) {
    int answer = 0;
    int sum = 0;
    
    for (int i = 0; i < numbers.size(); i++) {
        sum += numbers[i];
        if (sum > n) {
            answer = sum;
            break;
        }
    }
    return answer;
}

(1) 원래 풀이

 

#include <vector>

using namespace std;

int solution(vector<int> numbers, int n) {
    int sum = 0;
    
    for (int num : numbers) {
        sum += num;
        
        if (sum > n) {
            return sum;
        }
    }
    return sum;
}

(2) 개선안

 

9. [기초] 수열과 구간 쿼리 1

더보기

정수 배열 arr와 2차원 정수 배열 queries이 주어집니다. queries의 원소는 각각 하나의 query를 나타내며, [s, e] 꼴입니다

각 query마다 순서대로 s ≤ i ≤ e인 모든 i에 대해 arr[i]에 1을 더합니다

위 규칙에 따라 queries를 처리한 이후의 arr를 return 하는 solution 함수를 완성해 주세요

1 ≤ arr의 길이 ≤ 1,000

0 ≤ arr의 원소 ≤ 1,000,000

1 ≤ queries의 길이 ≤ 1,000

0 ≤ s ≤ e < arr의 길이

#include <vector>

using namespace std;

vector<int> solution(vector<int> arr, vector<vector<int>> queries) {
    for (const auto& query : queries) {
        int s = query[0];
        int e = query[1];
        
        for (int i = s; i <= e; ++i) {
            arr[i]++;
        }
    }
    
    return arr;
}

배열 안에 배열이 있으면 항상 생각하는게 어려운 것 같다. Range-for 문을 사용해서 풀어보았다.


*Atanny*

1. 언리얼 에디터 구성 요소와 기능 이해

 

핵심 내용 

뷰포트, 콘텐츠 브라우저, 아웃라이너, 디테일 패널, 툴바, 에셋 관리와 레벨 편집.

 

2. 액터/컴포넌트와 트랜스폼 이해

 

핵심 내용 

액터, 컴포넌트, 트랜스폼, 로컬 트랜스폼 vs 월드 트랜스폼,

자식 컴포넌트의 월드 트랜스폼은 부모의 트랜스폼을 먼저 적용한 좌표계 위에서 자식의 로컬 트랜스폼이 작동하는 것이므로 "곱하기" 관계이다.

 

3. 블루프린트 노드 연결과 동작 구현

 

핵심 내용 

블루프린트는 노드 기반의 비주얼 스크립팅 시스템. 실행 핀과 데이터핀은 각각의 역할에 맞게 같은 타입끼리만 연결. 이벤트 노드들은 특정 상황을 감지해 동작을 시작하는 진입점 역할을 한다.

 

4. 콜리전 채널과 오버랩/블록 이해

 

핵심 내용 

콜리전 채널에는 언리얼 엔진이 기본 제공하는 채널이 있다. 기본 제공 채널만으로 부족하면 커스텀 채널을 직접 생성해서 쓸 수 있고, 오브젝트가 다른 오브젝트와 만날 때 어느 채널에 속하는지에 따라 충돌 반응이 결정된다.

 

WorldStatic은 움직이지 않는 벽이나 지형에, WorldDynamic은 움직이는 박스나 바위에, Pawn은 플레이어나 AI 캐릭터에, PhysicsBody는 물리 시뮬레이션이 필요한 객체에 주로 사용된다.


*수학 공부*

이번 주제 : 기초 미적분과 물리(Calculus & Kinematics)

 

1. 미분, 적분 기본

미분(Derivative) : 시간에 따른 '변화량'을 구하는 것. 위치를 미분하면 속도가 되고, 속도를 미분하면 가속도가 된다.

적분(Integral) : 시간에 따른 변화량을 '누적'하는 것. 가속도를 적분하면 속도가 되고, 속도를 적분하면 위치가 된다.

 

2. 수치 적분(Tick)과 vs 해석적 해답(Formula)

 

1) Tick과 Formula 구분

 

수치 적분은 Tick() 함수라고 생각하면 된다. 매 프레임마다 계산하는 것이다.

예를 들어, 날아가는 화살의 가속도, 속도, 위치를 1프레임마다 컴퓨터가 계산한다. 매 순간마다 델타 타임을 곱해가며 계산하는 현재 진행형 계산법이다. 모든 물리 연산을 이렇게 매 프레임 Tick 안에서 델타 타임으로 계산하려고 하지만, 사실 매우 무거운 연산을 프레임마다 하도록 만드는 것이다.

 

해석적 해답(Formula)수학 공식으로 미래의 정답을 미리 계산해 놓는 것이다.

예를 들어, 화살을 쏘기 전에 포물선 방정식 (P(f) = P(i) + V(i) * t + 1/2 * a * t^2)에 초기 속도와 각도를 대입하는 것이다. Tick을 사용하지 않고 수학 공식을 활용하여 미래의 정답을 도출해 내는 미래 예측형 계산법이다.

 

2) 구분하는 이유

유저가 조종할 때에는 Tick을 써야하고,

AI와 시스템 예측에는 Formula를 쓴다.

 

유저가 조종하는 플레이어 캐릭터는 공식으로 계산할 수 없다. 실시간으로 상호작용해야 하는 경우에는 결국 Tick을 사용하게 된다. Tick은 플레이어의 이동, 자동차 운전, 불규칙하게 튕기는 물리 상자 등 매 순간 변수가 생기는 모든 실시간 상호작용에 사용한다.

 

AI가 포물선 궤적을 예측하거나, 이미 정해진 값이 있는 경우에는 Formula를 사용한다. 궤적 예측에 수치 적분을 사용하게 되면 연산량이 폭발하고 프레임 드랍이 발생한다. 또한 AI에 버그가 생기는 경우가 잦아진다. 이럴 경우에 물리 공식을 한줄로 풀어서 미리 계산해두고 AI는 해당 위치로 공격을 수행한다.

void AMyCharacter::DrawGrenadeTrajectory()
{
    FVector StartPos = GetActorLocation(); // 현재 위치
    FVector InitialVelocity = GetActorForwardVector() * 1500.0f; // 초기 던지는 속도
    FVector Gravity = FVector(0, 0, GetWorld()->GetGravityZ()); // 중력 (가속도)

    // Tick을 기다리지 않고, 미래의 0.1초~2.0초 사이의 궤적을 공식으로 즉시 계산해서 그림
    for (float time = 0.1f; time <= 2.0f; time += 0.1f)
    {
        // 등가속도 2법칙 공식: P = P0 + Vt + 0.5 * a * t^2
        FVector FuturePosition = StartPos 
                               + (InitialVelocity * time) 
                               + (0.5f * Gravity * (time * time));

        // 계산된 미래의 위치(정답)에 빨간색 구슬을 그려서 궤적을 보여줌
        DrawDebugSphere(GetWorld(), FuturePosition, 10.0f, 8, FColor::Red, false, 0.1f);
    }
}

이런식으로 수류탄의 궤적을 for반복문에 Formula를 넣는 방식으로 수류탄이 날아갈 미래의 궤적을 찍어낼 수 있다.

 

3. 점프 높이 C++ 코드

스페이스바를 누르면 정확히 500 높이까지 점프했다가 떨어지게 하려면 어떻게 해야 할까? 처음에는 수직 속도에 1000정도를 주는 하드코딩을 하지만, 수학을 이용한다면 C++ 코드를 좀 더 세련되게 작성할 수 있다.

수학적 원리 :

1) 캐릭터가 점프한 후 최고점(Apex)에 도달하는 순간, 수직 속도는 정확히 0이 된다.

2) 가속도(a)는 언리얼 엔진의 중력 값(-GravityZ)이다.

3) 이동해야 할 목표 거리(delta P)는 기획자가 요구한 높이 500 이다.

4) 방정식을 활용해보면 초기 점프 속도 V(i) 를 구할 수 있다.

 

#include "GameFramework/CharacterMovementComponent.h"
#include "Math/UnrealMathUtility.h"

void AMyCharacter::PerformExactHeightJump(float DesiredHeight)
{
    // 현재 월드의 중력 가속도(절댓값)를 가져옴 (기본값 약 980 cm/s^2)
    float Gravity = FMath::Abs(GetWorld()->GetGravityZ());

    // 미적분학 기반 물리 공식: V = sqrt(2 * g * h)
    // 이 공식을 쓰면 무조건 DesiredHeight(500)까지 정확히 올라감
    float RequiredJumpVelocity = FMath::Sqrt(2.0f * Gravity * DesiredHeight);

    // 계산된 완벽한 속도를 캐릭터의 Z축 속도에 덮어씌움
    FVector CurrentVelocity = GetCharacterMovement()->Velocity;
    CurrentVelocity.Z = RequiredJumpVelocity;
    
    GetCharacterMovement()->Velocity = CurrentVelocity;
}

이렇게 코드를 작성해두면, 나중에 게임 시스템이 변해서 게임의 중력값이 바뀌더라도 코드를 수정할 필요가 없게 된다.

 

4. 면적을 활용한 데미지 누적 C++ 코드

적분은 그래프 아래의 면적을 구하는 데에도 사용한다. 예를 들어, 범위 공격의 외곽으로 갈 수록 데미지가 감소하는 시스템을 구현하고 싶다고 하자. 매 프레임 캐릭터가 받는 데미지량이 곡선을 그리며 변하게 되는데, 캐릭터가 장판 위에 머문 시간동안 총 얼마의 데미지를 받았는지 서버가 검증하기 위해서는 시간에 따른 데미지 곡선을 적분해서 최종 수치를 도출해내게 된다.

 

5. PID(비례-적분-미분) 제어기를 활용한 호버링 및 드론 AI

공중에 떠다니는 드론이나 호버보드를 구현할 때, 단순히 목표 높이를 향해 속도를 주면 목표를 지나쳤다가(Overshoot) 다시 내려오는 등 위아래로 심하게 요동치게 된다. 이때 로봇 공학과 자율주행의 핵심 수학 원리이기도 한 PID 제어(Proportional-Integral-Derivative Control)가 쓰인다.

P(비례 - 오차) : 목표 위치까지 남은 거리에 비례해서 힘을 준다.

I(적분 - 과거 오차의 누적) : 시간이 지나도 목표에 도달하지 못하고있다면, 그 작은 오차들을 적분해서 추가적인 힘을 밀어 넣는다.

D(미분 - 오차의 변화율) : 목표에 다가가는 속도를 미분하여 너무 빨리 다가가고 있으면 힘을 미리 줄여서 부드럽게 안착하게 만든다.

void AMyDrone::UpdateHovering(float DeltaTime)
{
    float TargetZ = 500.0f; // 목표 고도
    float CurrentZ = GetActorLocation().Z;
    
    // 1. 현재 오차 (비례, P)
    float Error = TargetZ - CurrentZ;
    
    // 2. 오차의 누적 (적분, I) -> 틱마다 오차를 더함
    AccumulatedError += Error * DeltaTime;
    
    // 3. 오차의 변화율 (미분, D) -> (현재 오차 - 이전 오차) / 시간
    float ErrorDerivative = (Error - LastError) / DeltaTime;
    LastError = Error; // 다음 프레임을 위해 저장
    
    // PID 상수 (튜닝값)
    float Kp = 2.0f;  // 스프링처럼 당기는 힘
    float Ki = 0.5f;  // 중력을 이겨내는 누적 힘
    float Kd = 0.8f;  // 흔들림을 잡아주는 감쇠(브레이크)
    
    // 최종 가속도(힘) 도출
    float HoverForceZ = (Kp * Error) + (Ki * AccumulatedError) + (Kd * ErrorDerivative);
    
    // 드론에 힘(가속도) 적용 (오일러 적분 활용 생략)
    ApplyForce(FVector(0.0f, 0.0f, HoverForceZ));
}

 

6. 가속도 곡선을 활용한 스플라인 카메라 무빙

컷신 연출을 할 때 자주 사용되는 기법. 카메라의 속도 벡터가 각 점에서 앞뒤 점들과 부드럽게 이어지게 하는 기법이다.

스플라인 곡선 알고리즘 내부에서는 각 지점의 위치 공식을 미분하여 진입 속도와 진출 속도를 일치시키는(부드럽게 이어지게 하기 위해서) 수학 연산이 계속 돌아가고 있다고 생각하면 된다.

void AMyCameraDirector::UpdateCameraSpline()
{
    // 스플라인 상의 현재 거리(0.0 ~ 총 길이)
    float CurrentDistance = CurrentTime * MoveSpeed;

    // 스플라인 위치 공식 (적분된 결과물 형태)
    FVector NewLocation = CameraSpline->GetLocationAtDistanceAlongSpline(CurrentDistance, ESplineCoordinateSpace::World);
    
    // 스플라인 위치 공식을 '미분'하여 얻은 방향 벡터 (접선, 속도의 방향)
    // 이 미분값 덕분에 카메라가 궤도의 진행 방향을 정확히 바라보게 됨
    FVector TangentDirection = CameraSpline->GetDirectionAtDistanceAlongSpline(CurrentDistance, ESplineCoordinateSpace::World);
    
    // 접선 방향을 회전값(Rotator)으로 변환
    FRotator NewRotation = TangentDirection.Rotation();

    CameraActor->SetActorLocationAndRotation(NewLocation, NewRotation);
}

언리얼의 USplineComponent 내부 로직이 해당 미분 원리를 기반으로 한다. 프로그래머로서 미분된 접선 벡터를 제어해서 곡선을 다룰 수 있어야 한다.

 

7. 유체 저항과 공기역학(Aerodynamics & Drag)

공기 저항은 단순히 속도에 비례하지 않고 속도의 제곱에 비례해서 매우 커진다. 즉, 속도가 빨라질수록 가속도에 반하는 마찰력의 크기가 2차 곡선을 그리며 폭발적으로 증가한다. 이 역시 속도와 가속도의 복잡한 미분 방정식을 실시간으로 계산하여 표현할 수 있다.

void AMySkyDiver::UpdateAerodynamics(float DeltaTime)
{
    FVector CurrentVelocity = GetVelocity();
    float Speed = CurrentVelocity.Size();
    
    // 방향 벡터 정규화 (방향만 추출)
    FVector MoveDirection = CurrentVelocity.GetSafeNormal();

    // 공기 저항 공식: F_drag = -0.5 * 밀도 * 단면적 * 저항계수 * 속도의 제곱(V^2)
    // 속도가 빠를수록 저항력(가속도를 깎아먹는 힘)이 기하급수적으로 커짐
    float DragCoefficient = 0.05f; 
    float DragMagnitude = DragCoefficient * (Speed * Speed);
    
    // 마찰력 벡터 생성 (이동의 반대 방향)
    FVector DragForce = -MoveDirection * DragMagnitude;

    // 중력 가속도와 저항력을 더해 최종 가속도 도출
    FVector Gravity(0.0f, 0.0f, -980.0f);
    FVector FinalAcceleration = Gravity + DragForce; // 속도가 무한히 빨라지면 결국 이 둘의 합이 0이 됨(종단 속도)

    // 오일러 적분 적용
    CurrentVelocity += FinalAcceleration * DeltaTime;
    SetVelocity(CurrentVelocity); // 내부 이동 컴포넌트에 적용
}

 



*오늘의 총평*

언리얼 3D 게임 만들기 강의를 수강 완료했고, 주말에 한번 더 따라하며 복습할 예정이다. 이후 5번 과제를 마무리하고 팀프로젝트에 집중할 계획이다. 팀프로젝트가 본격적으로 시작하지는 않았기 때문에, 중간에 빈 시간 동안에는 C++ 기본기를 더 단단히 다듬고, 언리얼 C++ 문법과 실제 코드를 살펴보면서 코드를 깔끔하게 작성하는 연습을 하려고 한다.

'TIL' 카테고리의 다른 글

06.18 TIL  (0) 2026.06.18
06.17 TIL  (0) 2026.06.17
06.11 TIL  (0) 2026.06.11
06.10 TIL  (0) 2026.06.10
06.09 TIL  (0) 2026.06.09