티스토리 뷰
1. Unity ECS와 DOTS 개요
ECS(Entity Component System)
Unity가 도입한 ECS는 “데이터 지향적 설계”를 통해 기존 객체 지향(OOP)의 한계를 극복하려는 접근입니다.
- Entity: 단순히 ID 값. 그 자체로는 아무 기능도 없음.
- Component: Entity에 부착되는 순수 데이터. 예: Position, Velocity, Health.
- System: 특정 컴포넌트 조합을 가진 엔티티를 찾아 로직을 실행. 예: MovementSystem은 Position과 Velocity를 가진 엔티티에 대해 위치를 갱신.
Hot / Cold 데이터 분리
CPU 성능은 캐시 히트율에 크게 좌우됩니다. ECS는 자주 갱신되는 Hot 데이터와 가끔 접근되는 Cold 데이터를 분리해 캐시 효율을 높입니다.
- Hot: Position, Rotation, Velocity → 매 프레임 사용.
- Cold: WeaponType, AIConfig → 조건부로만 접근.
이렇게 하면 불필요한 데이터 로딩 없이 필요한 데이터만 연속적으로 메모리에 올릴 수 있어, SIMD 및 멀티스레딩 효율이 극대화됩니다.
DOTS (Data-Oriented Tech Stack)
Unity의 DOTS는 ECS에 Jobs System과 Burst Compiler를 결합한 기술 스택입니다.
- Jobs: 멀티스레딩 지원.
- Burst: CPU 아키텍처에 맞춰 SIMD 최적화된 네이티브 코드 생성.
- ECS: 데이터 지향적 구조로 확장성 확보.
정리하자면, DOTS는 대규모 데이터를 CPU에서 효율적으로 돌리기 위한 Unity만의 병렬화·최적화 풀 스택입니다.
2. Unreal Mass Entity Framework: 개념과 명칭
언리얼 엔진도 ECS와 같은 데이터 지향적 모델을 필요로 했습니다. 하지만 이미 Actor/Component 체계가 뿌리 깊게 자리잡고 있었기 때문에 혼란을 피하기 위해 Mass Entity Framework라는 독자적인 이름을 사용합니다.
핵심 개념
- Fragment: Unity ECS의 Component와 유사. 순수 데이터 단위.
- Tag: 특정 속성을 나타내는 경량 마커. 조건 분기에 사용.
- Processor: Fragment 조합에 로직을 실행하는 단위.
- Trait: Fragment, Tag, Processor를 패키징한 개념.
- 예: CharacterTrait을 붙이면 이동, 렌더링, 입력 등 관련 Fragment와 Processor가 자동 추가됨.
이러한 구조 덕분에, Mass는 프로그래머뿐 아니라 디자이너도 Trait 기반으로 쉽게 엔티티를 구성할 수 있습니다.
3. Movement 구현 예시
실제 Movement를 처리하는 과정을 예로 들어보겠습니다.
Fragment 정의
struct FTransformFragment : public FMassFragment
{
FTransform Transform;
};
struct FVelocityFragment : public FMassFragment
{
FVector Velocity;
};
Processor 구현
UCLASS()
class UMassMovementProcessor : public UMassProcessor
{
GENERATED_BODY()
public:
UMassMovementProcessor()
{
ExecutionOrder.ExecuteInGroup = UE::Mass::ProcessorGroupNames::Movement;
}
protected:
virtual void Execute(FMassEntityManager& EntityManager, FMassExecutionContext& Context) override
{
auto TransformList = Context.GetMutableFragmentView<FTransformFragment>();
auto VelocityList = Context.GetFragmentView<FVelocityFragment>();
for (int32 i = 0; i < Context.GetNumEntities(); i++)
{
TransformList[i].Transform.AddToTranslation(
VelocityList[i].Velocity * Context.GetDeltaTimeSeconds()
);
}
}
};
SIMD 적용 예시
#include <immintrin.h> // AVX Intrinsics
UCLASS()
class UMassSIMDGravityProcessor : public UMassProcessor
{
GENERATED_BODY()
public:
UMassSIMDGravityProcessor()
{
ExecutionOrder.ExecuteInGroup = UE::Mass::ProcessorGroupNames::Movement;
}
protected:
virtual void Execute(FMassEntityManager& EntityManager, FMassExecutionContext& Context) override
{
auto Velocities = Context.GetMutableFragmentView<FVelocityFragment>();
const int32 NumEntities = Context.GetNumEntities();
const float DeltaTime = Context.GetDeltaTimeSeconds();
__m128 Gravity = _mm_set_ps(0.f, 0.f, -980.f * DeltaTime, 0.f); // [X, Y, Z, Padding]
for (int32 i = 0; i < NumEntities; i += 4) // 4개 단위로 SIMD 처리
{
// Velocities[i] ~ Velocities[i+3]를 로드
__m128 vx = _mm_loadu_ps(&Velocities[i + 0].Velocity.X);
__m128 vy = _mm_loadu_ps(&Velocities[i + 1].Velocity.X);
__m128 vz = _mm_loadu_ps(&Velocities[i + 2].Velocity.X);
__m128 vw = _mm_loadu_ps(&Velocities[i + 3].Velocity.X);
// Z 축에만 중력 적용 (X, Y는 그대로)
vx = _mm_add_ps(vx, Gravity);
vy = _mm_add_ps(vy, Gravity);
vz = _mm_add_ps(vz, Gravity);
vw = _mm_add_ps(vw, Gravity);
// 다시 저장
_mm_storeu_ps(&Velocities[i + 0].Velocity.X, vx);
_mm_storeu_ps(&Velocities[i + 1].Velocity.X, vy);
_mm_storeu_ps(&Velocities[i + 2].Velocity.X, vz);
_mm_storeu_ps(&Velocities[i + 3].Velocity.X, vw);
}
}
};
ISPC 적용 예시 (더 간단한 SIMD 활용법)
// Gravity.ispc
export void ApplyGravity(uniform float3* velocities, uniform int N, uniform float dt)
{
foreach (i = 0 ... N)
{
velocities[i].z += -980.0f * dt; // SIMD 병렬화 자동 적용
}
}
C++ Processor에서는 ISPC 함수를 호출하기만 하면 됩니다:
extern void ApplyGravity_ispc(FVector* Velocities, int N, float dt);
virtual void Execute(FMassEntityManager& EntityManager, FMassExecutionContext& Context) override
{
auto Velocities = Context.GetMutableFragmentView<FVelocityFragment>();
ApplyGravity_ispc(&Velocities[0].Velocity, Context.GetNumEntities(), Context.GetDeltaTimeSeconds());
}
실무 팁
- GetMutableFragmentView를 사용하면 성능 오버헤드 없이 대규모 연속 데이터 접근 가능.
- Processor는 Chunk 단위 병렬 실행되므로, SIMD 최적화 효과가 큼.
- 필요시 ParallelForEachEntityChunk를 이용해 코어 활용 극대화 가능.
4. Fragment 접근 및 수정 방식
SharedPtr
- 장점: 데이터 소유권 관리에 유리.
- 단점: 참조 카운트 연산 비용 때문에 루프 내부에서는 비효율.
- 사용 예시: 외부 모듈과 데이터 공유, 긴 생명주기를 가진 대형 오브젝트 관리.
ArrayView / ConstArrayView
- 장점: 오버헤드가 거의 없는 뷰 타입.
- 단점: 소유권 없음.
- 사용 예시: Processor 내부에서 대규모 시뮬레이션 처리 시 권장.
결론: 성능 중심 로직 = ArrayView, 메모리 관리 필요 = SharedPtr
5. Mass Entity Framework 플러그인
Mass는 다양한 플러그인과 결합해 기능을 확장할 수 있습니다.
- MassGameplay: 캐릭터, NPC, 일반적인 게임 플레이 지원.
- MassCrowd: 수천 명 이상의 군중 시뮬레이션.
- MassTraffic: 차량, 교통 네트워크, 신호등 제어.
- MassLOD: 엔티티 단위 LOD 최적화.
- MassReplication: 네트워크 복제 최적화.
- MassSmartObjects: NPC와 상호작용 가능한 오브젝트 시스템.
- MassDebug/Visualizer: 시각화 및 디버깅 도구.
이 플러그인들을 조합하면 단순한 시뮬레이션을 넘어 도시 전체 생태계까지 구성할 수 있습니다.
6. Mass가 적합한 게임 장르
Mass는 특히 대규모 단순 객체 처리에서 강력합니다.
- RTS/전략 게임: 수백~수천 개 유닛 동시 제어.
- 군중/생태계 시뮬레이션: 도시 인구, 동물 무리, 생태계.
- 트래픽/레이싱 게임: 도로 네트워크, NPC 차량.
- 샌드박스/서바이벌: 월드 내 자원, 몬스터, 동물 개체.
실무에서는 Actor + Mass 하이브리드 구조를 쓰는 경우가 많습니다.
- Mass: 단순하고 대량의 NPC/오브젝트.
- Actor: 복잡한 개체(보스 몬스터, 플레이어 캐릭터).
7. Mass 내부 구조 이해하기
Mass의 성능 핵심은 Archetype-Chunk 메모리 구조입니다.
- Archetype: 특정 Fragment 조합을 가진 Entity 그룹.
- Chunk: 동일 Archetype 엔티티들을 묶어 저장하는 단위.
- Chunk는 Struct-of-Arrays(SoA) 형식으로 데이터를 저장해 캐시 효율이 뛰어남.
- Processor는 Query를 통해 대상 Chunk를 찾아내고, 이를 한 번에 처리.
즉, CPU는 연속 메모리에 정렬된 데이터만 읽으므로 캐시 미스가 최소화되고, SIMD 활용률이 높아집니다.
마무리
Unity DOTS와 Unreal Mass는 모두 데이터 지향적 패러다임을 기반으로 하지만, 구현 철학이 다릅니다.
- Unity: ECS를 “게임 개발의 기본 모델”로 삼고 독립적 기술 스택 구축.
- Unreal: 기존 Actor/Component 시스템과 공존 가능한 별도 프레임워크로 Mass 개발.
Mass의 강점은 다음과 같습니다.
- Trait 기반으로 손쉬운 구성.
- 대규모 시뮬레이션에 적합한 고성능 Chunk 구조.
- 플러그인 생태계를 통한 확장성.
앞으로 대규모 NPC, 군중, 트래픽, 생태계 시뮬레이션을 구현하고 싶다면, Mass Entity Framework는 반드시 고려해볼 가치가 있는 강력한 도구입니다.
참고
https://github.com/Megafunk/MassSample/blob/main/README.md
MassSample/README.md at main · Megafunk/MassSample
My understanding of Unreal Engine 5's experimental ECS plugin with a small sample project. - Megafunk/MassSample
github.com
https://contents.premium.naver.com/unrealstudy/unreal/contents/250524153826875lb
'개발 > UE5' 카테고리의 다른 글
| [UE5/Networking] IRIS 네트워크 모델 분석 (1) | 2025.09.05 |
|---|---|
| [Rendering] First Person Rendering (0) | 2025.09.03 |
| [UE5/Steam] 스팀 업적 시스템 관련 정리 (0) | 2025.03.06 |
| [UE5/Tip] 몽타주를 실행했으나, 애니메이션 인스턴스에서 한 프레임 늦게 수행되는 경우 (0) | 2025.02.13 |
| [UE5/Debugging] 언리얼 비주얼스튜디오 팁 (최적화 관련 매크로) (0) | 2025.01.17 |
- Total
- Today
- Yesterday
- unity
- 이미지이펙트
- normal
- Substance
- Noise
- 법선
- designer
- 컴네
- 컴퓨터구조론
- 네트워크
- 노영태
- 이미지 효과
- 인하대
- 정리
- ImageEffect
- 이종식
- 모션블러
- shader
- 블러효과
- 컴퓨터네트워크
- 유니티 셰이더
- #Shader #셰이더 #Tessellator #눈발자국 #발자국
- MotionBlur
- Substance Designer
- 유니티
- 소프트웨어공학
- HLSL
- Unreal
- 소공
- ue4
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |