STL, 입출력, 반복/조건 처리
1. 스택 문제 (백준 10828)문제정수를 저장하는 스택을 구현한 다음, 입력으로 주어지는 명령을 처리하는 프로그램을 작성하시오.명령은 총 다섯 가지이다.push X: 정수 X를 스택에 넣는 연산이다.pop: 스택에서 가장 위에 있는 정수를 빼고, 그 수를 출력한다. 만약 스택에 들어있는 정수가 없는 경우에는 -1을 출력한다.size: 스택에 들어있는 정수의 개수를 출력한다.empty: 스택이 비어있으면 1, 아니면 0을 출력한다.top: 스택의 가장 위에 있는 정수를 출력한다. 만약 스택에 들어있는 정수가 없는 경우에는 -1을 출력한다.입력첫째 줄에 주어지는 명령의 수 N (1 ≤ N ≤ 10,000)이 주어진다. 둘째 줄부터 N개의 줄에는 명령이 하나씩 주어진다. 주어지는 정수는 1보다 크거나 같고..
2025.10.14
C++
no image
언리얼 엔진 퍼포먼스 최적화의 첫걸음
최적화란?퍼포먼스 최적화는 일회성 작업이 아니라 반복적인 과정이다.가장 느린 병목 지점을 해결하면, 그 다음으로 느린 부분이 병목이 되고, 이를 다시 해결하는 과정을 반복하게 된다.즉, 끝이 없는 두더지 잡기와 같다. 최적화 전 준비: 테스트 환경 정리최적화를 시작하기 전에 테스트 환경을 정리하는 것이 매우 중요하다.불필요한 요소들이 성능 측정에 영향을 줄 수 있기 때문이다.예를 들어, 언리얼 엔진 에디터의 UI 자체가 성능에 영향을 줄 수 있다.따라서 최적화 테스트는 다음과 같은 환경에서 진행하는 것이 좋다:패키지된 빌드를 사용하거나최소한 Standalone Mode에서 테스트 1프레임의 기준 시간일반적인 게임은 60fps를 목표로 한다.이는 1초(1000ms)를 60프레임으로 나눈 값인 약 16.66..
2025.07.15
no image
스팀 OSS 연동
https://partner.steamgames.com/ public class Steamworks : ModuleRules{ public Steamworks(ReadOnlyTargetRules Target) : base(Target) { // The current SDK version number. double SteamVersionNumber = 1.62; // Mark the current version of the Steam SDK string SteamVersion = String.Format(CultureInfo.InvariantCulture, "v{0}", SteamVersionNumber).Replace(".", ""); Type = M..
2025.06.24
언리얼 사운드 시스템 기초
1. 개요프로젝트에서 게임 설정 관련된 작업을 시작하게 되었습니다. 가장 먼저 구현하고 싶었던 기능은 사운드 조절이었습니다. 이를 위해 언리얼 엔진의 사운드 시스템을 간단히 조사해보았고, 특히 Sound Class, Sound Mix, 그리고 이를 제어할 수 있는 C++ 함수들에 대해 처음으로 다뤄보게 되었습니다. 2. Sound ClassSound Class는 사운드를 논리적인 그룹으로 나누어 제어할 수 있게 해주는 시스템입니다. 예를 들어, 배경음(BGM), 효과음(SFX), 음성(Voice)을 각각의 사운드 클래스에 할당해두면, 이들 그룹의 볼륨을 개별적으로 제어할 수 있습니다. SC_BGM → 배경음용 Sound ClassSC_SFX → 효과음용 Sound ClassSC_UI → UI 효과음 전용..
2025.06.16
no image
온도 시스템
2025.06.04
언리얼 엔진의 입력 모드(FInputMode)
1. 개요언리얼 엔진에서는 플레이어가 키보드/마우스를 이용해 게임과 UI 중 어디에 입력을 보낼지 제어하기 위해 FInputMode 구조체를 사용합니다. UI 위젯을 띄웠을 때 플레이어가 게임 조작도 가능해야 하는지, 아니면 UI에만 집중해야 하는지를 정하는 설정입니다. 2. 주요 구조체 종류구조체설명FInputModeGameOnly게임 조작만 가능. UI 반응 XFInputModeUIOnlyUI에만 입력. 게임 입력은 무시FInputModeGameAndUI게임 조작 + UI 조작 둘 다 가능 3. 개념 설명3-1. 포커싱(Focus)UI 위젯 중 어떤 UI가 키보드 입력을 받을지 정하는 것.예를 들어 텍스트 입력창에 커서가 깜빡이는 상태가 “포커싱된” 상태.SetWidgetToFocus() 함수로 지정..
2025.05.21
no image
Instanced Static Mesh (ISM)
1. 개요오픈 월드 생존 게임에서 상호작용이 가능한 나무나 돌을 배치해야되는데 Foliage를 활용할 것 같아 찾아보던 중, Foliage로 배치된 스태틱 매쉬는 Instanced Static Mesh의 형태로 월드에 그룹화 되어 배치된다는 사실을 알게되었습니다. 그러나 Instanced 된 Static Mesh Component는 상호작용을 구현하기 어렵다고하여 좀 더 정리해 보았습니다.2. Instanced Static Mesh (ISM)동일한 스태틱 매시를 여러 번 배치할 때 퍼포먼스를 향상시키기 위한 시스템일반적인 Static Mesh Actor는 각각 별도의 액터/컴포넌트로 동작해서 많은 Draw Call과 메모리를 차지하게 되는데, ISM은 하나의 컴포넌트에서 여러 인스턴스를 관리하여 Draw..
2025.05.15
no image
언리얼 엔진 레벨 전환 시 로딩 방식
1. 개요오픈 월드 생존 게임을 개발 중이며, 메인 메뉴에서 인게임으로 넘어갈 때 자연스럽고 끊김 없는 레벨 전환을 구현하고 싶어서 레벨 로딩 방식에 대해 정리하게 되었습니다. 이를 위해 언리얼 엔진에서 제공하는 다양한 레벨 로딩 방식을 조사하였습니다.2. 언리얼 엔진의 레벨 전환 방식들OpenLevel(): 전통적인 동기적 방식, 전체 레벨 교체Level Streaming: 비동기 방식, 필요한 레벨을 불러오거나 제거Seamless Travel:서버와 클라이언트의 상태를 유지한 채 전환하는 방식3. OpenLevel - 동기적 레벨 전환Level을 완전히 교체하는 방식호출 시 현재 레벨 언로드 후 새로운 레벨 로드새로운 레벨 로딩 완료까지 화면 렌더링 멈춤장점단점구현이 가장 간단함클린 상태에서 로딩되므..
2025.05.14

STL, 입출력, 반복/조건 처리

언리얼 거토
|2025. 10. 14. 23:19

1. 스택 문제 (백준 10828)

문제

정수를 저장하는 스택을 구현한 다음, 입력으로 주어지는 명령을 처리하는 프로그램을 작성하시오.

명령은 총 다섯 가지이다.

  • push X: 정수 X를 스택에 넣는 연산이다.
  • pop: 스택에서 가장 위에 있는 정수를 빼고, 그 수를 출력한다. 만약 스택에 들어있는 정수가 없는 경우에는 -1을 출력한다.
  • size: 스택에 들어있는 정수의 개수를 출력한다.
  • empty: 스택이 비어있으면 1, 아니면 0을 출력한다.
  • top: 스택의 가장 위에 있는 정수를 출력한다. 만약 스택에 들어있는 정수가 없는 경우에는 -1을 출력한다.

입력

첫째 줄에 주어지는 명령의 수 N (1 ≤ N ≤ 10,000)이 주어진다. 둘째 줄부터 N개의 줄에는 명령이 하나씩 주어진다. 주어지는 정수는 1보다 크거나 같고, 100,000보다 작거나 같다. 문제에 나와있지 않은 명령이 주어지는 경우는 없다.

 

출력

출력해야하는 명령이 주어질 때마다, 한 줄에 하나씩 출력한다.

 

예제 입력

7
pop
top
push 123
top
pop
top
pop

예제 출력

-1
-1
123
123
-1
-1

 

내 제출:

#include <iostream>   // 입출력을 위한 헤더
#include <vector>     // 스택을 구현하기 위해 vector 사용
#include <string>     // 명령어("push", "pop" 등)를 문자열로 처리하기 위해 사용
using namespace std;

int main() {
    // C++ 입출력 속도 향상 (백준 필수 세팅)
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n;  // 명령의 개수를 저장할 변수
    if (!(cin >> n)) return 0;  // 입력이 실패하면 프로그램 종료

    vector<int> st;  // 스택 역할을 할 vector (후입선출, LIFO 구조)
    string cmd;      // 명령어를 저장할 변수

    // 명령어 개수(n)만큼 반복
    while (n--) {
        cin >> cmd;  // 명령어 입력

        if (cmd == "push") {
            // push X : 스택에 정수 X를 넣는 명령
            int x; 
            cin >> x;         // 넣을 값 입력
            st.push_back(x);  // 벡터의 맨 뒤에 삽입 (스택의 top에 넣는 것과 동일)
        }
        else if (cmd == "pop") {
            // pop : 스택의 가장 위(top)에 있는 정수를 빼고 출력
            if (st.empty()) 
                cout << -1 << '\n';  // 스택이 비어있으면 -1 출력
            else {
                cout << st.back() << '\n';  // 맨 위 값 출력
                st.pop_back();              // 맨 위 값 제거
            }
        }
        else if (cmd == "size") {
            // size : 스택에 들어있는 정수의 개수 출력
            cout << st.size() << '\n';
        }
        else if (cmd == "empty") {
            // empty : 스택이 비어있으면 1, 아니면 0 출력
            cout << (st.empty() ? 1 : 0) << '\n';
        }
        else if (cmd == "top") {
            // top : 스택의 가장 위에 있는 정수 출력 (제거는 안 함)
            if (st.empty()) 
                cout << -1 << '\n';  // 비어있으면 -1
            else 
                cout << st.back() << '\n';  // 맨 위 값 출력
        }
    }

    return 0;  // 프로그램 정상 종료
}

2. 요세푸스 문제 (백준 1158)

문제

요세푸스 문제는 다음과 같다.

1번부터 N번까지 N명의 사람이 원을 이루면서 앉아있고, 양의 정수 K(≤ N)가 주어진다. 이제 순서대로 K번째 사람을 제거한다. 한 사람이 제거되면 남은 사람들로 이루어진 원을 따라 이 과정을 계속해 나간다. 이 과정은 N명의 사람이 모두 제거될 때까지 계속된다. 원에서 사람들이 제거되는 순서를 (N, K)-요세푸스 순열이라고 한다. 예를 들어 (7, 3)-요세푸스 순열은 <3, 6, 2, 7, 5, 1, 4>이다. N과 K가 주어지면 (N, K)-요세푸스 순열을 구하는 프로그램을 작성하시오.

 

입력

첫째 줄에 N과 K가 빈 칸을 사이에 두고 순서대로 주어진다. (1 ≤ K ≤ N ≤ 5,000)

 

출력

예제와 같이 요세푸스 순열을 출력한다.

 

예제 입력

7 3

예제 출력

<3, 6, 2, 7, 5, 1, 4>

내 제출:

#include <iostream>
#include <queue>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, k;
    cin >> n >> k;  // N과 K 입력

    queue<int> q;   // 원을 표현하기 위한 큐

    // 1 ~ N까지 큐에 순서대로 넣기
    for (int i = 1; i <= n; i++) {
        q.push(i);
    }

    cout << '<';  // 출력 형식의 시작 괄호

    // 큐가 빌 때까지 반복 (모든 사람이 제거될 때까지)
    while (!q.empty()) {
        // K-1번째 사람까지 맨 뒤로 보냄
        for (int i = 0; i < k - 1; i++) {
            q.push(q.front()); // 맨 앞의 사람을 맨 뒤로 보내기
            q.pop();           // 맨 앞 제거
        }

        // 이제 K번째 사람은 제거될 차례
        cout << q.front(); // 제거되는 사람 출력
        q.pop();           // 큐에서 제거

        if (!q.empty()) cout << ", "; // 남은 사람이 있다면 쉼표 출력
    }

    cout << '>';  // 출력 형식의 끝 괄호
    return 0;
}

3. 괄호 문제 (백준 9012)

문제

괄호 문자열(Parenthesis String, PS)은 두 개의 괄호 기호인 ‘(’ 와 ‘)’ 만으로 구성되어 있는 문자열이다. 그 중에서 괄호의 모양이 바르게 구성된 문자열을 올바른 괄호 문자열(Valid PS, VPS)이라고 부른다. 한 쌍의 괄호 기호로 된 “( )” 문자열은 기본 VPS 이라고 부른다. 만일 x 가 VPS 라면 이것을 하나의 괄호에 넣은 새로운 문자열 “(x)”도 VPS 가 된다. 그리고 두 VPS x 와 y를 접합(concatenation)시킨 새로운 문자열 xy도 VPS 가 된다. 예를 들어 “(())()”와 “((()))” 는 VPS 이지만 “(()(”, “(())()))” , 그리고 “(()” 는 모두 VPS 가 아닌 문자열이다. 

여러분은 입력으로 주어진 괄호 문자열이 VPS 인지 아닌지를 판단해서 그 결과를 YES 와 NO 로 나타내어야 한다. 

입력

입력 데이터는 표준 입력을 사용한다. 입력은 T개의 테스트 데이터로 주어진다. 입력의 첫 번째 줄에는 입력 데이터의 수를 나타내는 정수 T가 주어진다. 각 테스트 데이터의 첫째 줄에는 괄호 문자열이 한 줄에 주어진다. 하나의 괄호 문자열의 길이는 2 이상 50 이하이다. 

출력

출력은 표준 출력을 사용한다. 만일 입력 괄호 문자열이 올바른 괄호 문자열(VPS)이면 “YES”, 아니면 “NO”를 한 줄에 하나씩 차례대로 출력해야 한다. 

 

예제 입력

6
(())())
(((()())()
(()())((()))
((()()(()))(((())))()
()()()()(()()())()
(()((())()(

 

예제 출력

NO
NO
YES
NO
YES
NO

 

내 제출:

#include <iostream>
#include <string>
#include <stack>   // 스택 사용
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int t;              // 테스트 케이스 수
    cin >> t;

    while (t--) {
        string s;       // 괄호 문자열
        cin >> s;
        stack<char> st; // 괄호 검사용 스택
        bool isValid = true; // 올바른 괄호 여부 판단 플래그

        for (char c : s) {
            if (c == '(') {
                // 여는 괄호면 스택에 push
                st.push(c);
            }
            else if (c == ')') {
                // 닫는 괄호일 때
                if (st.empty()) {
                    // 짝이 맞는 여는 괄호가 없으면 잘못된 문자열
                    isValid = false;
                    break;
                }
                st.pop(); // 짝이 맞으면 pop
            }
        }

        // 모든 문자를 처리한 후에도 스택에 '('가 남아있으면 잘못된 괄호
        if (!st.empty()) isValid = false;

        cout << (isValid ? "YES" : "NO") << '\n';
    }

    return 0;
}

'C++' 카테고리의 다른 글

클린코딩  (0) 2025.03.27
브루트포스 기법  (0) 2025.03.11
STL 기본 구조  (0) 2025.02.18

최적화란?

퍼포먼스 최적화는 일회성 작업이 아니라 반복적인 과정이다.
가장 느린 병목 지점을 해결하면, 그 다음으로 느린 부분이 병목이 되고, 이를 다시 해결하는 과정을 반복하게 된다.
즉, 끝이 없는 두더지 잡기와 같다.

 

최적화 전 준비: 테스트 환경 정리

최적화를 시작하기 전에 테스트 환경을 정리하는 것이 매우 중요하다.
불필요한 요소들이 성능 측정에 영향을 줄 수 있기 때문이다.

예를 들어, 언리얼 엔진 에디터의 UI 자체가 성능에 영향을 줄 수 있다.
따라서 최적화 테스트는 다음과 같은 환경에서 진행하는 것이 좋다:

  • 패키지된 빌드를 사용하거나
  • 최소한 Standalone Mode에서 테스트

 

1프레임의 기준 시간

일반적인 게임은 60fps를 목표로 한다.
이는 1초(1000ms)를 60프레임으로 나눈 값인 약 16.66ms 안에 게임의 모든 연산이 끝나야 한다는 뜻이다.

  • 만약 특정 프레임이 10ms에 끝났다면?
    → 아직 6.66ms의 여유가 있으므로, 추가적인 로직이나 연출이 들어가도 성능상 문제가 없다.

이 기준 시간(16.66ms)은 퍼포먼스 분석의 핵심 기준점이 된다.


Stat Tools 3종 세트

1. Stat Unit

  • 병목 지점이 CPU Bound인지 GPU Bound인지 쉽게 확인 가능.
  • 명령어: ~ → stat unit

 

2. Stat FPS

  • 프레임 속도를 숫자로 확인 가능.
  • 명령어: ~ → stat fps

 

3. Stat Game

  • Game thread, Draw thread 등 상세한 게임 로직 실행 시간 확인 가능.
  • 명령어: ~ → stat game

 


Unreal Insights

  • 고급 프로파일링 툴
  • 퍼포먼스를 시스템적으로 분석하고 싶을 때 사용

 

Unreal Insights는 UE가 설치된 경로의 Engine > Binaries > Win64에서 UnrealInsights.exe를 눌러 실행할 수 있다

 

에디터를 실행 중이라면 위 그림과 같은 방법으로 쉽게 실행할 수 있다

 

두 방법으로 Unreal Insights를 실행시키면 위와 같은 창이 뜨게 된다

 

Unreal Insights 창을 띄운 채로 PIE(Play In Editor)를 실행하면, 게임의 실행 과정을 실시간으로 추적하고 세부적인 퍼포먼스 데이터를 수집하는 분석이 시작된다.

 

다음 글에서는 현재 진행 중인 프로젝트를 활용해 퍼포먼스를 개선해 나가는 과정을 공유드리겠습니다:)

스팀 OSS 연동

언리얼 거토
|2025. 6. 24. 17:50

https://partner.steamgames.com/

 

 

public class Steamworks : ModuleRules
{
	public Steamworks(ReadOnlyTargetRules Target) : base(Target)
	{
        // The current SDK version number.
        double SteamVersionNumber = 1.62;

        // Mark the current version of the Steam SDK
        string SteamVersion = String.Format(CultureInfo.InvariantCulture, "v{0}", SteamVersionNumber).Replace(".", "");

		Type = ModuleType.External;

		PublicDefinitions.Add(String.Format(CultureInfo.InvariantCulture, "STEAM_SDK_VER=TEXT(\"{0}\")", SteamVersionNumber));
		PublicDefinitions.Add("STEAM_SDK_VER_PATH=TEXT(\"Steam" + SteamVersion + "\")");

        string SdkBase = Target.UEThirdPartySourceDirectory + "Steamworks/Steam" + SteamVersion + "/sdk";
		if (!Directory.Exists(SdkBase))
		{
			string Err = string.Format("steamworks SDK not found in {0}", SdkBase);
			System.Console.WriteLine(Err);
			throw new BuildException(Err);
		}

 

DefaultEngine.ini

[/Script/Engine.GameEngine]
+NetDriverDefinitions=(DefName="GameNetDriver",DriverClassName="OnlineSubsystemSteam.SteamNetDriver",DriverClassNameFallback="OnlineSubsystemUtils.IpNetDriver")

[OnlineSubsystem]
DefaultPlatformService=Steam

[OnlineSubsystemSteam]
bEnabled=true
SteamDevAppId=480

; If using Sessions
; bInitServerOnClient=true

[/Script/OnlineSubsystemSteam.SteamNetDriver]
NetConnectionClassName="OnlineSubsystemSteam.SteamNetConnection"

1. 개요

프로젝트에서 게임 설정 관련된 작업을 시작하게 되었습니다. 가장 먼저 구현하고 싶었던 기능은 사운드 조절이었습니다. 이를 위해 언리얼 엔진의 사운드 시스템을 간단히 조사해보았고, 특히 Sound Class, Sound Mix, 그리고 이를 제어할 수 있는 C++ 함수들에 대해 처음으로 다뤄보게 되었습니다.

 

2. Sound Class

Sound Class는 사운드를 논리적인 그룹으로 나누어 제어할 수 있게 해주는 시스템입니다. 예를 들어, 배경음(BGM), 효과음(SFX), 음성(Voice)을 각각의 사운드 클래스에 할당해두면, 이들 그룹의 볼륨을 개별적으로 제어할 수 있습니다.

 

  • SC_BGM → 배경음용 Sound Class
  • SC_SFX → 효과음용 Sound Class
  • SC_UI → UI 효과음 전용 Sound Class
  • SC_Master → 위 Sound Class를 Child Class로 둔 전체 마스터 볼륨 설정용 클래스

🔧 에디터에서의 설정 방법:

  1. Content Browser에서 우클릭 → Sounds → Sound Class 생성
  2. 생성한 Sound Class의 속성에서 Parent Class를 설정하거나 계층 구조를 구성 가능
  3. 사운드 에셋(Sound Cue 등)에서 해당 Sound Class를 지정

이렇게 분리해두면 C++이나 Blueprint에서 각 그룹의 볼륨을 따로 조절할 수 있습니다.

 

 

3. Sound Mix

Sound Mix는 Sound Class에 정의된 속성(예: 볼륨, 피치 등)을 일시적으로 덮어쓰기 할 수 있도록 도와주는 시스템입니다. 예를 들어 UI를 열었을 때 배경음을 살짝 줄이거나, 특정 이벤트 발생 시 효과음을 강조하는 식의 연출이 가능합니다.

🔸 주로 사용되는 예시:

  • UI 열릴 때 BGM은 30%로 줄이고, 효과음은 그대로 유지
  • 일시정지 시 모든 사운드 볼륨 감소
  • 전투 진입 시 효과음 강조

사운드 믹스는 사운드 클래스의 볼륨을 즉시 바꾸는 것이 아니라, 오버라이드 형태로 부드럽게 변화시킬 수 있어 매우 유용합니다.

4. C++로 SoundMix 제어하기

이번에 새롭게 알게 된 부분은 사운드 믹스를 코드로도 동적으로 제어할 수 있다는 점이었습니다. UGameplayStatics에서 제공하는 몇 가지 함수를 통해 이를 처리할 수 있습니다.

🔹 1) UGameplayStatics::PushSoundMixModifier(UObject* WorldContextObject, USoundMix* InSoundMix)

  • 기능: SoundMix를 활성화합니다.
  • 효과: 해당 믹스에 정의된 Sound Class의 오버라이드가 적용됨
  • 예시:
  • cpp
     
UGameplayStatics::PushSoundMixModifier(GetWorld(), MySoundMix);

🔹 2) UGameplayStatics::PopSoundMixModifier(UObject* WorldContextObject, USoundMix* InSoundMix)

  • 기능: 적용 중인 SoundMix를 해제합니다.
  • 효과: 해당 믹스에서 설정한 오버라이드가 사라지고 원래 설정으로 복귀
  • 예시:
  • cpp
     
UGameplayStatics::PopSoundMixModifier(GetWorld(), MySoundMix);

🔹 3) UGameplayStatics::SetSoundMixClassOverride(...)

UGameplayStatics::SetSoundMixClassOverride(
    GetWorld(),              // 월드 컨텍스트
    MySoundMix,              // 적용할 SoundMix
    MySoundClass,            // 대상 SoundClass
    0.2f,                    // 볼륨 (0.0 ~ 1.0)
    1.0f,                    // 피치 (기본값 1.0)
    0.5f                     // 페이드 시간 (초 단위)
);
 
  • 기능: 특정 SoundMix 내에서 특정 SoundClass의 속성을 오버라이드
  • 장점: 에디터에서 설정한 SoundMix를 수정하지 않고도 런타임에 유연한 조절 가능
  • 사용 예시: UI가 열릴 때 BGM SoundClass만 볼륨을 0.2로 변경

5. 마무리

 

  • 처음 써보니 사운드 시스템이 꽤 강력하고 유연하다는 점
  • 다음엔 SoundConcurrency나 Audio Volume도 함께 공부하고 싶다는 마무리

 

온도 시스템

언리얼 거토
|2025. 6. 4. 21:00

'TIL > C++와 UE' 카테고리의 다른 글

언리얼엔진 멀티플레이에서의 PlayerState  (0) 2025.04.08
GameInstanceSubsystem  (0) 2025.04.02
Object Pooling  (0) 2025.03.28
언리얼 엔진 레플리케이션(Replication)  (0) 2025.03.11
네트워킹 및 멀티 플레이어 개요  (0) 2025.03.10

1. 개요

언리얼 엔진에서는 플레이어가 키보드/마우스를 이용해 게임과 UI 중 어디에 입력을 보낼지 제어하기 위해 FInputMode 구조체를 사용합니다. UI 위젯을 띄웠을 때 플레이어가 게임 조작도 가능해야 하는지, 아니면 UI에만 집중해야 하는지를 정하는 설정입니다.

 

2. 주요 구조체 종류

구조체 설명
FInputModeGameOnly 게임 조작만 가능. UI 반응 X
FInputModeUIOnly UI에만 입력. 게임 입력은 무시
FInputModeGameAndUI 게임 조작 + UI 조작 둘 다 가능

 

3. 개념 설명

3-1. 포커싱(Focus)

  • UI 위젯 중 어떤 UI가 키보드 입력을 받을지 정하는 것.
  • 예를 들어 텍스트 입력창에 커서가 깜빡이는 상태가 “포커싱된” 상태.
  • SetWidgetToFocus() 함수로 지정.
FInputModeUIOnly InputMode;
InputMode.SetWidgetToFocus(PauseWidget->TakeWidget())

PC->SetInputMode(InputMode);
PC->bShowMouseCursor = true;

 

TakeWidget()은 UUserWidget 내부의 Slate 루트 위젯을 받아옴( UUserWidget → SWidget 변환)


Slate 위젯
- UI를 구성하기 위한 저수준 UI 프레임워크
- 실질적으로 화면에 표시되는 UI의 렌더링과 입력 처리를 담당 -> 입력 포커싱도 그 일부
- 포커싱은 Slate 시스템 레벨에서 처리되는 기능

 

3-2. 마우스 락(Mouse Lock)

  • 마우스 커서를 화면 안에 가두는 것.
  • 예: FPS 게임에서 마우스가 화면 밖으로 나가는 걸 방지.
설정값 설명
DoNotLock 마우스가 화면 밖으로 자유롭게 나감
LockAlways 마우스를 항상 화면 안에 가둠
LockOnCapture 마우스를 클릭했을 때만 화면 안에 가둠
InputMode.SetLockMouseToViewportBehavior(EMouseLockMode::LockAlways);
 
 

3-3. 마우스 캡쳐(Mouse Capture)

  • 마우스 움직임이 엔진에 의해 직접 추적되고 처리되는 상태.
  • FPS처럼 커서는 안 보이지만 마우스 움직임으로 카메라가 도는 게임이 대표적.
  • 캡처된 마우스는 커서가 보이지 않고, 마우스 좌표도 고정된 상태로 처리됨.

📌 FInputModeGameAndUI에서는 아래 함수로 마우스 캡처 중 커서를 숨길지 설정 가능:

FInputModeGameAndUI InputMode;
InputMode.SetHideCursorDuringCapture(false); // 캡쳐 중 커서 숨김

 

 

Instanced Static Mesh (ISM)

언리얼 거토
|2025. 5. 15. 18:41

1. 개요

오픈 월드 생존 게임에서 상호작용이 가능한 나무나 돌을 배치해야되는데 Foliage를 활용할 것 같아 찾아보던 중, Foliage로 배치된 스태틱 매쉬는 Instanced Static Mesh의 형태로 월드에 그룹화 되어 배치된다는 사실을 알게되었습니다. 그러나 Instanced 된 Static Mesh Component는 상호작용을 구현하기 어렵다고하여 좀 더 정리해 보았습니다.

2. Instanced Static Mesh (ISM)

  • 동일한 스태틱 매시를 여러 번 배치할 때 퍼포먼스를 향상시키기 위한 시스템
  • 일반적인 Static Mesh Actor는 각각 별도의 액터/컴포넌트로 동작해서 많은 Draw Call과 메모리를 차지하게 되는데, ISM은 하나의 컴포넌트에서 여러 인스턴스를 관리하여 Draw Call을 줄이고 렌더링 효율을 높임. → 압도적인 퍼포먼스
  • 대표적인 예: 숲에 수백, 수천 그루의 나무를 배치할 때

3. ISM과 Static Mesh Component의 차이

스태틱 메시 컴포넌트
인스턴스드 스태틱 메시 컴포넌트

항목 Static Mesh Component  Instanced Static Mesh Component
트랜스폼 개별 설정 가능 인스턴스별 위치/회전/스케일만 가능
머티리얼 개별 적용 가능 전체 인스턴스가 동일 머티리얼 공유
콜리전 개별 설정 가능 콜리전 설정은 전체 공유
액터화 각각 액터로 존재 하나의 컴포넌트에 여러 인스턴스 포함

4. ISM과 상호작용의 문제점

 

  • 각 인스턴스는 독립된 액터가 아니기 때문에 상호작용 처리가 어렵다.
    • 예: 플레이어가 특정 나무만 "베는" 행동을 하려면 그 인스턴스 하나만 제거하거나 상태를 바꿔야 함.
  • 인스턴스별 상태를 추적하거나 커스텀 이벤트 처리 불가
    • 예: “이 나무는 체력이 0이 되면 쓰러진다” 같은 논리 구현이 복잡함.
  • 트리거, 인터랙션, 개별 이벤트 처리가 필요한 경우에는 적합하지 않음

5. 그럼 오똑행?

방법 1: 초기엔 ISM으로 전체 배치 + 상호작용 필요 시 개별 액터로 교체 또는 거리 기반 액터로 교체

  • 예:
    1. 숲 전체는 ISM으로 구성
    2. 플레이어 근처 일정 거리 안에 있는 나무만 추적
    3. 상호작용 요청 시, 해당 인스턴스를 제거하고, 같은 위치에 동일한 Static Mesh 액터를 생성해서 교체
    4. 이후 해당 액터를 대상으로 상호작용 처리 (체력, 애니메이션, 사운드 등)

방법 2: ISM 인스턴스 ID 기반으로 최소한의 상호작용 구현

  • GetInstanceTransform, RemoveInstance, UpdateInstanceTransform 같은 함수로 제한적인 조작 가능
  • 예를 들어 나무를 "한 번에 쓰러뜨리는" 단순한 시스템이라면, RemoveInstance() 만으로도 구현 가능

방법 3: 모든 나무를 액터로 배치

  • 상호작용이 많은 게임이더라도, 수천 개 액터는 퍼포먼스 문제
  • LOD 거리 안의 나무만 액터화하는 동적 스폰 시스템 필요

6. 참고 함수들

 

  • AddInstance(): 새로운 인스턴스를 추가
  • RemoveInstance(int32 InstanceIndex): 특정 인스턴스를 제거
  • GetInstanceTransform(int32 InstanceIndex): 해당 인스턴스의 위치/회전/스케일 정보 가져오기
  • UpdateInstanceTransform(...): 인스턴스 위치 갱신
  • GetInstancesOverlappingBox(...): 특정 범위 내 인스턴스 찾기

 

언리얼 엔진 공식 문서: Instanced Static Mesh Component

 

1. 개요

오픈 월드 생존 게임을 개발 중이며, 메인 메뉴에서 인게임으로 넘어갈 때 자연스럽고 끊김 없는 레벨 전환을 구현하고 싶어서 레벨 로딩 방식에 대해 정리하게 되었습니다. 이를 위해 언리얼 엔진에서 제공하는 다양한 레벨 로딩 방식을 조사하였습니다.


2. 언리얼 엔진의 레벨 전환 방식들

  • OpenLevel(): 전통적인 동기적 방식, 전체 레벨 교체
  • Level Streaming: 비동기 방식, 필요한 레벨을 불러오거나 제거
  • Seamless Travel:서버와 클라이언트의 상태를 유지한 채 전환하는 방식

3. OpenLevel - 동기적 레벨 전환

  • Level을 완전히 교체하는 방식
  • 호출 시 현재 레벨 언로드 후 새로운 레벨 로드
  • 새로운 레벨 로딩 완료까지 화면 렌더링 멈춤
장점 단점
구현이 가장 간단함
클린 상태에서 로딩되므로 디버깅 용이
로딩중 화면 멈춤(로딩 화면 구현 필요)
메모리 재로딩 비용 큼
네트워크 게임에서 클라이언트 재연결 필요

4. Level Streaming - 비동기적 레벨 전환

  • 기존 레벨을 유지하고 백그라운드에서 다른 레벨 불러오기 가능
    • LoadStreamLevel() / UnloadStreamLevel() 함수
  • 원하는 시점에 해당 레벨을 보이게 만들거나 숨기기 가능
    • LoadStreamLevel() / LevelStreamingDynamic의 파라미터 조절
    • SetShouldBeVisible() / SetShouldBeLoaded() 함수로 처리
// 파라미터 조절 방식
LoadStreamLevel(LevelName = "Cave_Level",  
                MakeVisibleAfterLoad = false,  // ← 핵심!
                ShouldBlockOnLoad = false)

// 함수 사용 방식
if (StreamingLevel)
{
    StreamingLevel->SetShouldBeVisible(false);  // 일단 숨긴 상태로 로딩
    StreamingLevel->SetShouldBeLoaded(true);    // 로딩만 함

    // 나중에 이 타이밍에서 보이게 만들기
    StreamingLevel->SetShouldBeVisible(true);
}
장점 단점
비동기적 방식으로 자연스러운 로딩 가능
메모리 관리 효율적
레벨 간 의존성 주의 필요
면밀한 월드 구조 설계 필요

5. Seamless Travel - 네트워크 상태 유지 전환 방식

  • 서버가 맵 이동 시 클라이언트와의 연결 유지해야 할 때 사용
    • 매커니즘: Actor Preservation ("보존 가능한 액터" 목록을 만들고, 이 액터들을 파괴하지 않고 유지)
    • APlayerController를 유지한 채로 레벨 전환.
    • ServerTravel() 또는 ClientTravel()에서 ?Seamless 옵션 사용
  • Transition Map: 전환 중 임시로 로딩되는 맵. Seamless Travel 중 발생하는 공백을 보완
    • 목적: 원활한 레벨전환, 플레이어 상태 유지(캐릭터, 인벤토리 등), 경량화된 임시 맵
장점 단점
멀티플레이에서 상태 유지 가능
클라이언트 재접속 없이 전환 가능
여러 리소스를 병렬로 로딩 가능
Transition Map 구성 필요
디버깅 난이도 어려움

 

언리얼 엔진 공식 문서: Travellling in Multiplayer


6. BONUS: 더 부드러운 레벨 전환 방법

방법  설명
Preloading Assets 게임 시작 시 미리 로딩할 리소스를 AssetManager로 관리
LevelStreamingDynamic "동적"으로 원하는 시점에 로딩 → 전환
Camera Fade 연출 PlayerController->ClientStartCameraFade() 사용해서 전환 부드럽게 보이기