Performance 10

Unity 6 Awaitable 실전: 코루틴 없이 프레임 대기와 백그라운드 연산 처리하기

Unity에서 시간차 연출이나 비동기 로딩을 구현할 때 가장 먼저 떠오르는 도구는 코루틴이다. 하지만 작업이 여러 단계로 이어지면 yield return이 중첩되고, 반환값과 예외를 다루기도 불편해진다.Unity의 Awaitable은 이런 흐름을 C#의 async와 await 문법으로 표현한다. 다음 프레임, 물리 프레임, 지정 시간뿐 아니라 메인 스레드와 백그라운드 스레드 전환도 명시할 수 있다. 특히 로딩 화면, 절차적 데이터 생성, 복잡한 UI 연출처럼 순서가 중요한 코드에서 효과가 크다.코루틴을 프레임 단위 Awaitable로 바꾸기다음 코드는 오브젝트가 파괴되거나 플레이 모드가 종료될 때 자동으로 중단되는 페이드 처리다.using System.Threading;using UnityEngine;p..

C++23 std::move_only_function으로 소유권 있는 게임 콜백 안전하게 넘기기

게임 코드에서 콜백은 피하기 어렵다. 스킬 캐스팅 완료, 비동기 로딩 완료, 서버 응답 처리, 타이머 만료처럼 "지금은 아니고 나중에 실행할 코드"가 계속 생긴다. 전통적으로는 함수 포인터, std::function, 엔진별 delegate를 많이 쓴다.문제는 콜백이 단순 함수 호출을 넘어 소유권을 함께 가져야 할 때다. 예를 들어 로딩 작업이 끝날 때 임시 버퍼를 넘기거나, 서버 요청 객체를 한 번만 소비하거나, 취소 가능한 작업 핸들을 콜백 안에 묶고 싶을 수 있다. 이때 std::function은 복사 가능해야 하므로 std::unique_ptr 같은 move-only 객체를 캡처한 람다를 담을 수 없다.C++23의 std::move_only_function은 이 빈틈을 메운다. 이름 그대로 복사할..

C#·C++ 모던 2026.07.05

Unity DOTS 없이 멀티스레드로 수천 오브젝트 움직이기: TransformAccessArray 실전

총알 수천 발, 물고기 떼, 몰려오는 좀비, 부서진 파편. 화면 위에서 동시에 움직이는 오브젝트가 늘어나면 프레임이 무너진다. 원인은 단순하다. 모든 Update()가 메인 스레드 한 줄에서 순차 실행되기 때문이다. CPU 코어는 8개인데 한 개만 일하고 나머지는 놀고 있는 셈이다.교과서적인 해답은 DOTS와 ECS다. 하지만 이미 GameObject와 프리팹으로 굴러가는 프로젝트를 ECS로 갈아엎는 비용은 현실적으로 감당하기 어렵다. 콜라이더, 애니메이터, 기존 컴포넌트 구조를 전부 다시 설계해야 하기 때문이다.다행히 중간 지점이 있다. Unity의 C# Job System과 Burst 컴파일러는 GameObject를 그대로 둔 채로 이동 로직만 멀티스레드로 돌릴 수 있다. 핵심 도구가 Transfor..

게임 코드에 AI 리뷰어 붙이기: Update 루프의 GC 할당을 매 커밋 자동으로 잡는 워크플로우

AI 코딩 도구를 새 코드 짜는 데만 쓰고 있다면 절반만 쓰는 셈이다. Claude Code, Copilot, Cursor는 이미 작성된 코드를 읽고 문제를 짚어내는 데도 강하다. 특히 게임 코드처럼 "컴파일은 멀쩡히 되는데 프레임을 갉아먹는" 함정이 많은 영역에서 AI 리뷰는 사람이 눈으로 놓치기 쉬운 부분을 잘 잡는다.문제는 기본 상태의 AI 리뷰가 너무 일반적이라는 점이다. 변수명, 널 체크, 예외 처리 같은 웹 백엔드식 지적만 잔뜩 내놓고, 정작 게임에서 치명적인 매 프레임 힙 할당이나 박싱은 그냥 지나친다. 게임 코드 리뷰는 게임의 룰로 한 번 길들여야 비로소 쓸모가 생긴다.이 글에서는 Claude Code를 예로, 게임 전용 리뷰 기준을 파일로 박아두고 매 커밋 diff에 AI 리뷰어를 자동으..

C++ 23 std::flat_map으로 게임 룩업 테이블의 캐시 미스 잡기

연관 컨테이너가 필요할 때 C++ 개발자가 가장 먼저 떠올리는 건 std::map이다. 키로 빠르게 찾고, 자동으로 정렬되고, 삽입과 삭제가 O(log n)이다. 교과서적으로는 흠잡을 데가 없다. 그런데 게임 코드에서 매 프레임 수백 번 순회하는 자료구조로 std::map을 쓰는 순간, 프로파일러는 캐시 미스로 새빨갛게 물든다.이유는 알고리즘 복잡도가 아니라 메모리 레이아웃에 있다. std::map은 내부적으로 균형 이진 트리(보통 red-black tree)이고, 노드 하나하나가 힙에 따로 할당된다. 노드들이 메모리 여기저기 흩어져 있으니 순회할 때마다 포인터를 따라가야 하고, CPU는 매번 새 캐시 라인을 읽어와야 한다. 요즘 하드웨어에서 캐시 미스 한 번은 산술 연산 수백 개와 맞먹는 비용이다.C+..

C#·C++ 모던 2026.06.23

C++ 23 std::generator로 게임 웨이브와 절차적 콘텐츠를 lazy 시퀀스로 뽑아내기

웨이브 스포너를 구현해 본 사람이라면 누구나 한 번쯤 비슷한 코드를 짜 봤을 것이다. 현재 웨이브 번호를 멤버 변수로 들고, 매 웨이브가 끝날 때마다 ++currentWave 하고, 다음에 스폰할 적의 종류와 수를 그때그때 switch 나 if 덩어리로 계산한다. 절차적 던전도 마찬가지다. 방 목록을 미리 std::vector 에 전부 채워 넣거나, 인덱스를 손으로 굴리면서 "다음 방"을 꺼내는 상태 머신을 직접 만든다.문제는 이 두 가지가 본질적으로 같은 패턴인데도 매번 손으로 상태 관리 코드를 다시 쓴다는 점이다. "다음 값을 요청하면 그때 계산해서 돌려준다"는 lazy 시퀀스 개념이 게임 로직 곳곳에 있는데, C++ 에는 오랫동안 이걸 깔끔하게 표현할 표준 도구가 없었다.C++ 23 의 std::g..

C#·C++ 모던 2026.06.16

C# 12 [InlineArray]로 Unity 핫패스에서 힙 없이 고정 크기 버퍼 구현하기

왜 고정 크기 버퍼가 필요한가?게임 로직을 짜다 보면 "최대 8개 스킬", "최대 4명 파티원", "최대 16개 투사체" 같이 상한이 명확한 소규모 컬렉션을 수시로 만나게 된다. 가장 손쉬운 선택은 List나 T[]지만, 둘 다 힙 객체를 만들어 GC 추적 대상이 된다. 캐릭터가 1000명이고 스킬이 8개라면 별것 아닌 float[]가 1000개의 소형 힙 오브젝트로 쌓인다.그렇다고 unsafe fixed float[8]로 가자니 unsafe 블록이 코드 전반에 퍼지고, Unity DOTS의 IComponentData처럼 unmanaged 제약이 강한 컨텍스트에서는 fixed 버퍼조차 쓸 수 없는 경우가 생긴다.C# 12에서 도입된 [InlineArray] 어트리뷰트는 이 딜레마를 깔끔하게 해결한다. 단..

C#·C++ 모던 2026.06.14

Unity DOTS 실전: IJobEntity + Burst 로 수만 개 엔티티 이동을 메인 스레드에서 떼어내기

탄막 슈터, RTS, 군중 시뮬레이션처럼 화면에 수천에서 수만 개의 오브젝트가 동시에 움직이는 게임을 만들다 보면 결국 같은 벽에 부딪힌다. MonoBehaviour.Update() 안에서 transform 을 하나하나 갱신하는 순간 프로파일러가 빨갛게 물든다. GameObject 기반 접근은 객체마다 흩어진 메모리, 가상 호출 오버헤드, 단일 스레드 실행이라는 삼중고를 안고 있다.DOTS(Data-Oriented Technology Stack)의 ECS 는 이 문제를 데이터 레이아웃과 병렬화로 정면 돌파한다. 같은 컴포넌트를 가진 엔티티들을 연속된 메모리(chunk)에 모아 두고, 그 위를 SIMD 친화적으로 훑는다. 여기에 Burst 컴파일러를 얹으면 C# 코드가 LLVM 을 거쳐 네이티브에 가까운 ..

Span<T>와 stackalloc로 Unity 핫패스에서 GC를 없애는 법

게임에서 프레임이 튀는 가장 흔한 원인 중 하나는 GC(가비지 컬렉션) 스파이크다. 평소엔 멀쩡하다가 갑자기 한 프레임이 16ms를 넘기면서 화면이 미세하게 끊기는 그 현상. 프로파일러를 열어보면 GC.Collect가 범인인 경우가 정말 많다. 매 프레임 돌아가는 Update 루프 안에서 무심코 만든 임시 배열, 문자열 결합, LINQ 한 줄이 힙에 쓰레기를 쌓고 있는 것이다.문제는 이걸 "최적화 나중에 하자"고 미루기 쉽다는 점이다. 핫패스에서 발생하는 작은 할당들은 개별적으로는 무해해 보이지만, 60fps 기준으로 초당 60번씩 쌓이면 결국 컬렉션을 강제로 트리거한다. 그리고 Unity의 기본 Boehm GC는 stop-the-world 방식이라, 컬렉션이 도는 순간 메인 스레드가 멈춘다.이 글에서는..

C#·C++ 모던 2026.05.31

Unity에서 큰 struct 복사 비용 잡기: in, ref readonly, 그리고 defensive copy 함정

게임 코드에서 struct는 GC 압박을 피하려는 우리의 최선의 친구다. Vector3, Matrix4x4, 커스텀 Transform 데이터 묶음처럼 자주 만들고 자주 버리는 값들은 class 대신 struct로 두는 게 정석이다. 그런데 struct가 커지면 다른 문제가 슬그머니 들어온다. 바로 복사 비용이다.class는 참조 하나(8바이트 포인터)만 넘어가지만 struct는 메서드에 넘길 때마다 통째로 복사된다. 16바이트 Vector4 정도면 무시할 만하지만, 64바이트짜리 Matrix4x4나 필드가 잔뜩 박힌 게임 상태 struct를 매 프레임 수천 번 함수에 넘긴다면 이 복사가 누적되어 측정 가능한 비용이 된다. 더 나쁜 건, 우리가 알아채지 못하는 사이에 컴파일러가 몰래 추가로 복사하는 경우다..

C#·C++ 모던 2026.05.24