Unity 6.5 · 2D Profiler
```
Sprite Atlas 최적화 전후를 2D Profiler로 직접 비교하기
Sprite Atlas를 만들었다고 최적화가 끝난 것은 아닙니다. 실제 프레임에서 Atlas를 사용하는지 확인해야 합니다.
목적: Sprite Atlas의 실제 사용 여부와 내부 사용 영역을 2D Profiler로 확인합니다.
```
2D Profiler에서 볼 항목
```
Sprite Count / Sprites Rendered
로드된 Sprite 수와 현재 프레임에서 사용한 Sprite 수를 확인합니다.
SpriteAtlas Count / SpriteAtlases Rendered
로드된 Atlas 수와 현재 프레임에서 사용한 Atlas 수를 확인합니다.
Usage
Atlas 영역 중 현재 프레임에서 실제 사용한 Sprite 영역 비율입니다.
Usage를 FPS 수치처럼 해석하면 안 됩니다.
Usage는 Atlas 내부 사용 영역을 보여줍니다. 패킹 효율이나 FPS 자체를 의미하지 않습니다.
```
씬별 목표와 차이
```
목표 Sprite Atlas 확인점
A NoAtlas Atlas 미적용 기준 25 / 25 0 / 0 개별 Texture 사용
B EfficientAtlas 24개 Sprite를 2개 Atlas로 구성 25 / 25 2 / 2 Gameplay 16개 + UI 8개
C WastefulAtlas 미사용 Sprite를 대량 포함 25 / 25 1 / 1 같은 화면에서도 Usage 감소
D FourSprites 4종 Sprite만 반복 배치 13 / 5 1 / 1 배치 수와 고유 Sprite 수 구분
```
비교에서 확인할 핵심
```
A → B
화면은 같습니다. Atlas Rendered는 0에서 2로 바뀝니다.
B → C
Rendered 수는 같습니다. 미사용 Sprite 때문에 Usage는 낮아질 수 있습니다.
D
오브젝트 수와 고유 Sprite 수는 다를 수 있습니다.
```
C와 D가 의미하는 것
```
C WastefulAtlas — Atlas 낭비 확인
같은 24개 Sprite를 표시합니다. 그러나 사용하지 않는 Sprite 80개도 Atlas에 넣습니다. 화면은 같아도 실제 사용 영역은 작습니다.
D FourSprites — 반복 배치 해석
SpriteRenderer가 많아도 같은 Sprite를 반복할 수 있습니다. Profiler 수치는 배치된 오브젝트 수와 다를 수 있습니다.
Usage의 의미
Usage는 선택한 프레임에서 실제 렌더링에 사용한 영역의 비율입니다.
낮은 Usage는 큰 Atlas 일부만 사용한다는 신호가 될 수 있습니다.
```
측정 방법
```
Window > Analysis > Profiler를 엽니다. 이후 2D 모듈을 선택합니다.
각 씬을 실행합니다. 상태가 안정된 프레임을 하나 선택합니다.
Count, Rendered, Atlas 상세 목록과 Usage를 함께 비교합니다.
Physics 2D와 혼동하지 않습니다. 이번 글에서 사용하는 것은 Unity 6.5의 2D Profiler 모듈입니다.
```
씬 A — NoAtlas
```
A
Sprite Atlas를 사용하지 않는 기준 상태
모든 Sprite를 개별 Texture로 사용합니다.
Sprite 25 / Rendered 25
Atlas는 사용하지 않습니다.
게임뷰
2D Profiler
```
씬 B — EfficientAtlas
```
B
Gameplay과 UI를 두 개 Atlas로 분리
화면에 함께 표시되는 Sprite를 목적별 Atlas로 묶습니다.
Gameplay Atlas와 UI Atlas를 각각 사용합니다.
Atlas Count / Rendered = 2 / 2
사용한 Sprite Atlas
게임뷰
2D Profiler
```
씬 C — WastefulAtlas
```
C
하나의 큰 Atlas에 많은 Sprite를 포함
화면은 B와 비슷하게 유지합니다. 사용하지 않는 Sprite도 같은 Atlas에 넣습니다.
Atlas Count / Rendered = 1 / 1
Rendered 수만 보면 문제를 발견하기 어렵습니다.
사용한 Atlas
게임뷰
2D Profiler
```
낮은 Usage 사례
```
Usage
Atlas를 사용해도 항상 효율적인 것은 아니다
이 사례에서는 Usage가 약 3.1%로 나타납니다.
Usage 3.1%
큰 Atlas를 로드했지만 실제로 사용하는 Sprite 영역은 매우 작습니다.
사용한 Atlas와 실행 화면
2D Profiler
```
Profiler 수치를 해석할 때 주의할 점
```
Usage가 낮다고 무조건 잘못된 Atlas는 아닙니다. 로딩 방식과 메모리 사용량도 함께 확인해야 합니다.
SpriteRenderer 개수와 Sprites Rendered는 같은 의미가 아닙니다. 같은 Sprite를 여러 번 반복해서 사용할 수 있습니다.
Atlas Count가 적다고 항상 더 좋은 것도 아닙니다. 함께 사용하는 Sprite를 어떤 기준으로 묶었는지가 더 중요합니다.
```
정리
```
2D Profiler는 Sprite Atlas가 실제 프레임에서 사용되는지 보여줍니다.
Count와 Rendered만 보면 Atlas 낭비를 놓칠 수 있습니다.
Usage까지 함께 보면 Atlas 구성의 효율을 더 쉽게 판단할 수 있습니다.
```

Unity에서 Play 버튼을 누르면 게임이 바로 실행되는 것처럼 보입니다.

하지만 내부에서는 여러 초기화 작업이 실행됩니다.

그중 하나가 Domain Reload입니다.

Domain Reload는 static 변수와 이벤트 상태를 초기화하는 역할을 합니다.

프로젝트가 커지면 이 과정도 시간이 걸릴 수 있습니다.

그래서 Unity에서는 Domain Reload를 끌 수 있습니다.

문제는 끈 순간부터 static 상태를 직접 관리해야 한다는 점입니다.


핵심 요약

  • Domain Reload는 static 상태를 초기화합니다.
  • 끄면 Play Mode 진입 시간이 줄어들 수 있습니다.
  • 대신 static 값이 이전 Play 상태를 유지할 수 있습니다.
  • SubsystemRegistration으로 static 값을 직접 초기화할 수 있습니다.
  • static 이벤트도 중복 등록을 주의해야 합니다.

목표

Domain Reload를 껐을 때 static 값이 왜 남는지 확인합니다.

그리고 안전하게 초기화하는 방법을 알아봅니다.


결과물

최종적으로 다음 패턴을 사용합니다.

[RuntimeInitializeOnLoadMethod(
    RuntimeInitializeLoadType.SubsystemRegistration)]
static void ResetStaticData()
{
    playCount = 0;
}

Domain Reload를 꺼도 Play Mode마다 static 값을 초기화할 수 있습니다.


결과물로 얻을 수 있는 기대

Play Mode 진입 시간을 줄이면서 static 상태를 안정적으로 관리할 수 있습니다.

Domain Reload를 껐을 때 발생하는 이상한 Editor 버그도 줄일 수 있습니다.


1. 어떻게 끄고 켜지?

Enter Play Mode Settings 옵션

Unity 6에서는 Project Settings > Editor > Enter Play Mode Settings에서 동작을 선택할 수 있습니다.

이미지처럼 4가지 옵션이 있습니다.

옵션의미
Reload Domain and Scene Domain과 Scene을 모두 다시 로드합니다. 가장 일반적인 초기화 방식입니다.
Reload Scene only Scene만 다시 로드합니다. static 상태는 유지될 수 있습니다.
Reload Domain only Domain만 다시 로드합니다. Scene 상태는 유지합니다.
Do not reload Domain or Scene 둘 다 다시 로드하지 않습니다. Play Mode 진입은 빠르지만 직접 상태를 관리해야 합니다.

2. Domain Reload란?

Domain Reload는 Unity가 스크립팅 상태를 다시 초기화하는 과정입니다.

기본적인 Play Mode에서는 static 변수도 초기화합니다.

예를 들어 다음 코드가 있다고 가정하겠습니다.

public static int PlayCount = 0;

게임에서 값을 변경합니다.

PlayCount = 10;

Play Mode를 종료하고 다시 실행합니다.

Domain Reload가 동작하면 다시 초기값으로 돌아갑니다.

첫 번째 Play
PlayCount = 0

값 변경
PlayCount = 10

Stop

두 번째 Play
PlayCount = 0

개발자가 별도 초기화 코드를 작성하지 않아도 됩니다.


3. Domain Reload를 끄면 어떻게 될까?

Domain Reload를 끄면 Play Mode 진입 시간을 줄일 수 있습니다.

하지만 static 값이 자동으로 초기화되지 않습니다.

간단한 테스트 코드를 만들어 보겠습니다.

using UnityEngine;

public class DomainReloadTest : MonoBehaviour
{
    private static int playCount;

    private void Start()
    {
        playCount++;

        Debug.Log($"Play Count : {playCount}");
    }
}

Domain Reload가 동작하면 매번 다음과 같이 출력됩니다.

Play Count : 1

하지만 Domain Reload를 끄면 값이 누적될 수 있습니다.

첫 번째 Play
Play Count : 1

두 번째 Play
Play Count : 2

세 번째 Play
Play Count : 3

playCount가 이전 값을 유지하기 때문입니다.

버그가 아니라 정상적인 동작입니다.


4. static 값을 직접 초기화하기

이 문제는 RuntimeInitializeOnLoadMethod로 해결할 수 있습니다.

using UnityEngine;

public class DomainReloadTest : MonoBehaviour
{
    private static int playCount;

    [RuntimeInitializeOnLoadMethod(
        RuntimeInitializeLoadType.SubsystemRegistration)]
    private static void ResetStaticData()
    {
        playCount = 0;
    }

    private void Start()
    {
        playCount++;

        Debug.Log($"Play Count : {playCount}");
    }
}

이제 Domain Reload를 꺼도 결과는 같습니다.

첫 번째 Play
Play Count : 1

두 번째 Play
Play Count : 1

세 번째 Play
Play Count : 1

매번 Runtime 시작 시 직접 초기화하기 때문입니다.


5. 왜 SubsystemRegistration을 사용할까?

RuntimeInitializeOnLoadMethod에는 실행 시점을 지정할 수 있습니다.

이번에는 다음 값을 사용했습니다.

RuntimeInitializeLoadType.SubsystemRegistration

이 시점은 Runtime 초기 과정에서 실행됩니다.

Scene의 Awake()보다 먼저 static 상태를 초기화하기에 적합합니다.

따라서 Domain Reload를 끈 프로젝트에서 자주 사용할 수 있습니다.


6. static 이벤트도 주의해야 한다

Domain Reload를 끄면 변수만 확인하면 안 됩니다.

static 이벤트 등록 상태도 남을 수 있습니다.

예를 들어 다음 코드가 있습니다.

Application.quitting += OnQuit;

Play Mode에 들어갈 때마다 등록하면 중복될 수 있습니다.

그러면 하나의 이벤트가 여러 번 호출됩니다.

안전하게 처리하려면 기존 등록을 먼저 제거합니다.

[RuntimeInitializeOnLoadMethod(
    RuntimeInitializeLoadType.SubsystemRegistration)]
static void Initialize()
{
    Application.quitting -= OnQuit;
    Application.quitting += OnQuit;
}

핵심은 이 패턴입니다.

이벤트 -= 함수;
이벤트 += 함수;

기존 등록을 제거한 뒤 다시 등록합니다.


7. Domain Reload를 꺼도 될까?

꺼도 됩니다.

다만 다음 두 항목을 직접 관리해야 합니다.

확인 항목이유

static 변수 이전 Play 값이 남을 수 있음
static 이벤트 이벤트가 중복 등록될 수 있음

프로젝트가 작다면 Domain Reload를 그대로 사용해도 됩니다.

Play Mode 진입 시간이 부담스러워지면 비활성화를 검토하면 됩니다.


마무리

Domain Reload를 끄는 목적은 간단합니다.

Play Mode 진입 시간을 줄이는 것입니다.

하지만 Unity가 해주던 static 초기화도 함께 사라집니다.

따라서 static 상태를 직접 관리해야 합니다.

가장 먼저 기억할 코드는 다음입니다.

[RuntimeInitializeOnLoadMethod(
    RuntimeInitializeLoadType.SubsystemRegistration)]
static void ResetStaticData()
{
    // static 데이터 초기화
}

static 이벤트도 함께 확인해야 합니다.

Domain Reload를 끄기 전에 이 두 가지만 점검해도 많은 문제를 예방할 수 있습니다.

유니티 : https://github.com/Unity-Technologies/2d-renderer-samples?utm_source=chatgpt.com

최신화가 안되어서. 최신화 할 겸 만들었습니다.

수정한 샘플 : https://github.com/MinByungGil/2d-renderer-samples

 

UNITY 2D RENDERER

씬으로 배우는 Unity 2D 조명과 화면 표현

Unity의 2D 조명은 빛 하나만 배치해서 완성하지 않습니다. 빛의 종류, 대상 레이어, 표면 방향, 재질, 후처리를 함께 설계합니다.

Unity 6 URP 2D Renderer 10개 샘플 씬 주니어 개발자용
이 글은 기술 용어를 화면 변화와 연결해 설명합니다. 먼저 결과를 보고, 다음에 Inspector 설정을 확인하세요.

핵심 요약

01

한 장면, 한 가지 질문

각 씬은 하나의 렌더링 개념을 집중해서 보여 줍니다.

02

빛을 기능으로 확장

기본 조명을 Normal Map, Mask, Shadow, 후처리와 연결합니다.

03

한 번에 하나씩 비교

설정 하나만 바꾸고 화면 변화의 원인을 확인합니다.

이 글의 목표

배우는 내용

2D Light가 화면에 영향을 주는 과정을 이해합니다. 각 씬의 목적과 관찰 포인트도 함께 확인합니다.

얻는 결과

필요한 샘플 씬을 직접 고릅니다. 조명, 그림자, 재질, 카메라 설정을 비교하며 문제 원인을 찾습니다.

목차

프로젝트 결과물

이 프로젝트는 실행 가능한 씬 10개와 공유 기반 폴더 1개를 제공합니다. 각 씬은 독립적으로 열어 확인합니다.

 

Lights

빛의 종류, 표면 반응, 적용 범위를 비교합니다. 3개 씬을 제공합니다.

Shadows

하나의 그림자와 여러 그림자의 합성을 비교합니다. 2개 씬을 제공합니다.

Post Processing

Volume과 Bloom으로 최종 화면 분위기를 바꿉니다. 1개 씬을 제공합니다.

Shader Graph

재질 노드로 스프라이트 색상 흐름을 바꿉니다. 1개 씬을 제공합니다.

Pixel Perfect Camera

스프라이트, Tilemap, UI의 픽셀 표현을 비교합니다. 3개 씬을 제공합니다.

Common

URP Asset, 2D Renderer Data, 스프라이트와 Volume 설정을 공유합니다.

추천 학습 순서

  1. Common에서 공유 설정과 에셋 위치를 확인합니다.
  2. Lights에서 빛의 종류와 표면 반응을 살펴봅니다.
  3. Shadows에서 빛과 그림자의 관계를 비교합니다.
  4. Post Processing에서 화면 효과를 추가합니다.
  5. Shader Graph에서 재질 표현을 바꿉니다.
  6. Pixel Perfect Camera에서 최종 화면과 UI를 확인합니다.
2D 조명을 읽는 다섯 가지 질문

Inspector에서 아래 순서로 질문하면 빛 문제를 빠르게 좁힐 수 있습니다.

어떤 Light인가?
어디에 비추는가?
표면 방향이 있는가?
어떻게 합치는가?
최종 효과를 더하는가?

폴더별 씬 상세 설명

Common/

Common/은 실행 씬을 모아 둔 폴더가 아닙니다. 다른 씬이 함께 사용하는 렌더링 설정과 시각 에셋을 보관합니다.

Render Pipeline

URP Asset과 2D Renderer Data를 보관합니다.

Sprites

스프라이트, Normal Map, 외곽선, 쿠키를 보관합니다.

Volume Profile

여러 씬이 참고하는 화면 효과 설정을 보관합니다.

공유 원칙

공유 설정이 여러 씬의 기본 렌더링 흐름을 결정합니다.

Lights/

이 폴더는 2D Light를 세 단계로 설명합니다. 빛의 모양, 표면 반응, 조명 대상을 차례로 살펴봅니다.

01

1 Light Types

Assets/Samples/2D Renderer/Lights/1 Light Types.unity

요약

다섯 종류의 2D Light가 빛의 모양과 범위를 바꾸는 방식을 비교합니다.

목적

Global, Parametric, Freeform, Point, Sprite Light 2D를 한 화면에서 살펴봅니다.

결과물

배경과 스프라이트에 다섯 종류의 조명을 배치한 비교 씬입니다.

사용하면 할 수 있는 것

장면 목적에 맞는 Light 종류를 고르고 위치, 색, 강도를 조절합니다.

상세 설명

Global Light 2D는 넓은 영역에 기본 밝기를 만듭니다. Parametric Light 2D는 정해진 도형으로 빛을 만듭니다.

Freeform Light 2D는 점을 움직여 빛의 모양을 직접 만듭니다. Point Light 2D는 중심에서 사방으로 빛을 퍼뜨립니다.

Sprite Light 2D는 이미지 형태를 빛의 무늬로 사용합니다. 같은 스프라이트도 빛의 위치와 범위에 따라 다르게 보입니다.

추천 실험
  1. 각 Light를 하나씩 끄며 역할을 확인합니다.
  2. Light 위치를 바꾸며 조명 영역을 비교합니다.
  3. 색과 강도를 차례로 바꾸며 화면 변화를 기록합니다.
02

2 Normal Map

Assets/Samples/2D Renderer/Lights/2 Normal Map.unity

요약

Normal Map이 표면 방향을 알려 주고 조명이 명암을 계산합니다.

목적

Normal Map을 연결한 스프라이트와 일반 스프라이트의 차이를 비교합니다.

결과물

GlowRocks B와 배경에 Freeform Light를 비추는 비교 씬입니다.

사용하면 할 수 있는 것

2D 그림에 표면 명암과 입체감을 추가하는 방법을 이해합니다.

상세 설명

Normal Map은 표면이 어느 방향을 향하는지 색으로 저장합니다. 2D Light는 그 방향 정보를 사용해 밝기와 명암을 계산합니다.

빛을 움직이면 표면의 밝은 부분과 어두운 부분도 움직입니다. Normal Map을 끄면 표면 반응이 단순해집니다.

관련 텍스처는 Common/Sprites/GlowRocks B_n.png입니다. 같은 Light를 Normal Map 사용 전후에 비춰 결과를 비교하세요.

03

3 Masks

Assets/Samples/2D Renderer/Lights/3 Masks.unity

요약

Light Mask는 빛의 대상을 고릅니다. Blend Style은 빛의 합성 방식을 고릅니다.

목적

배경과 오브젝트에 서로 다른 조명을 적용하는 방법을 비교합니다.

결과물

Global Light 2D, Key Light, Rim Light를 한 씬에 배치합니다.

사용하면 할 수 있는 것

Sorting Layer별 조명과 주 조명, 외곽 조명을 설계합니다.

상세 설명

Global Light 2DBackground 레이어를 비춥니다. Key LightRim LightSpace Junk 레이어를 비춥니다.

Light Inspector의 Apply To Sorting Layers가 조명 대상을 고릅니다. 오브젝트가 빛 영역 안에 있어도 레이어가 다르면 빛을 받지 않습니다.

Key Light는 기본 합성을 사용합니다. Rim Light는 두 번째 Rim Blend Style을 사용해 오브젝트 외곽을 강조합니다.

Rim Light는 외곽선을 자동으로 그리지 않습니다. 대상 레이어, 조명 영역, 보조 텍스처를 모두 확인해야 합니다.

추천 실험
  1. Global, Key, Rim Light를 차례로 끕니다.
  2. Key Light의 대상 Sorting Layer를 해제합니다.
  3. Rim Light의 Blend Style을 바꾸고 결과를 비교합니다.

Shadows/

이 폴더는 빛이 만드는 어두운 영역을 설명합니다. 하나의 그림자부터 여러 그림자의 합성까지 비교합니다.

04

1 Shadows

Assets/Samples/2D Renderer/Shadows/1 Shadows.unity

요약

하나의 2D Light와 Shadow Caster가 만드는 기본 그림자를 살펴봅니다.

목적

빛과 오브젝트의 위치가 그림자 방향과 길이에 미치는 영향을 확인합니다.

결과물

Rock과 Parametric Light가 만드는 단일 그림자 씬입니다.

사용하면 할 수 있는 것

Shadow Caster를 연결하고 그림자 경계를 비교합니다.

상세 설명

Shadow Caster는 스프라이트가 그림자를 만드는 경계를 정의합니다. 빛과 Rock의 상대 위치가 그림자 방향을 결정합니다.

빛과 Rock 사이의 거리는 그림자 길이에 영향을 줍니다. Light와 Rock을 옮기며 그림자 변화를 관찰하세요.

05

2 Shadow Composite

Assets/Samples/2D Renderer/Shadows/2 Shadow Composite.unity

요약

여러 Rock 오브젝트가 만든 그림자를 하나의 빛 아래에서 합칩니다.

목적

그림자 영역이 겹칠 때 최종 화면이 달라지는 과정을 확인합니다.

결과물

여러 Rock과 Shadow Caster를 배치한 합성 그림자 씬입니다.

사용하면 할 수 있는 것

오브젝트 배치로 그림자 모양을 바꾸고 각 기여도를 확인합니다.

상세 설명

여러 Rock의 그림자 영역이 겹치면 최종 그림자 모양도 달라집니다. Rock과 Light의 위치를 한 번에 하나씩 바꾸세요.

개별 Shadow Caster를 끄면 해당 Rock의 기여를 확인할 수 있습니다. 이 씬은 그림자를 조명 결과의 조각으로 이해하도록 돕습니다.

Post Processing/

이 폴더는 조명 결과에 화면 효과를 추가하는 방법을 설명합니다.

06

1 Post Processing

Assets/Samples/2D Renderer/Post Processing/1 Post Processing.unity

요약

Volume이 2D 조명 장면에 후처리 효과를 추가합니다.

목적

Bloom이 밝은 영역과 전체 화면 분위기를 바꾸는 과정을 확인합니다.

결과물

GlowRocks B와 조명 영역에 Bloom Profile을 적용한 씬입니다.

사용하면 할 수 있는 것

조명과 후처리의 역할을 나누고 화면 분위기를 설계합니다.

상세 설명

2D Light는 장면 안의 빛과 색을 만듭니다. Post Processing은 완성된 화면에 추가 효과를 계산합니다.

Profiles/Bloom Profile.asset은 Bloom 설정을 담습니다. Bloom은 밝은 영역 주변에 부드러운 빛 번짐을 만듭니다.

먼저 Volume 효과를 끕니다. 다음에 Bloom을 켜고 같은 장면을 비교합니다. 마지막으로 효과 강도를 조절합니다.

Shader Graph/

이 폴더는 재질이 화면 색을 바꾸는 과정을 설명합니다.

07

1 Shader Graph

Assets/Samples/2D Renderer/Shader Graph/1 Shader Graph.unity

요약

Shader Graph 재질이 2D Lit Sprite의 색상 표현을 바꿉니다.

목적

기본 재질과 색상 반전 재질의 차이를 비교합니다.

결과물

RockInvert Color 재질을 연결한 비교 씬입니다.

사용하면 할 수 있는 것

노드를 연결해 재질 색상 흐름을 만들고 조명과 함께 비교합니다.

상세 설명

Shader Graph는 코드를 직접 작성하지 않고 재질 흐름을 구성합니다. Invert Color 그래프는 입력 색상의 반대 색상을 출력합니다.

2D Lit Sprite는 재질과 조명 결과를 함께 화면에 그립니다. 따라서 같은 조명도 재질에 따라 다른 색을 보여 줍니다.

Materials/Invert Color.matShader Graphs/Invert Color.shadergraph를 차례로 확인하세요.

Pixel Perfect Camera/

이 폴더는 화면을 픽셀 격자에 맞추는 방법을 비교합니다. 조명보다 카메라, 스프라이트, 타일, UI 표현을 다룹니다.

08

1 SpriteRenderer Examples

Assets/Samples/2D Renderer/Pixel Perfect Camera/1 SpriteRenderer/1 SpriteRenderer Examples.unity

요약

SpriteRenderer 이동 사례를 두 카메라로 비교합니다.

목적

오브젝트 이동 방식이 픽셀 정합과 화면 가장자리에 미치는 영향을 확인합니다.

결과물

정지, 부모 이동, 애니메이션, 물리, 스크립트 이동 사례를 배치합니다.

사용하면 할 수 있는 것

일반 카메라와 Pixel Perfect Camera의 차이를 설명합니다.

상세 설명

장면은 Stationary, Animated, Physically Controlled 같은 사례를 제공합니다. 각 사례는 서로 다른 이동 방식을 보여 줍니다.

화면의 Pixel Perfect 토글로 두 카메라를 전환합니다. 정지 오브젝트와 이동 오브젝트의 가장자리를 차례로 비교하세요.

09

2 Tilemap Examples

Assets/Samples/2D Renderer/Pixel Perfect Camera/2 Tilemap/2 Tilemap Examples.unity

요약

Dungeon과 애니메이션 Water Tilemap을 두 카메라로 비교합니다.

목적

타일 경계와 카메라 이동이 픽셀 정합에 미치는 영향을 확인합니다.

결과물

던전, 물 타일, 로봇 캐릭터를 배치한 이동형 비교 씬입니다.

사용하면 할 수 있는 것

타일 경계, 카메라 추적, Rule Tile 결과를 함께 관찰합니다.

상세 설명

Dungeon은 Rule Tile로 만든 던전 타일을 보여 줍니다. Water는 애니메이션 타일로 만든 물 표현을 보여 줍니다.

실행 중 방향키로 로봇 캐릭터를 움직입니다. Pixel Perfect 토글로 카메라를 바꾸고 타일 경계를 비교하세요.

10

3 UI Scaling Example

Assets/Samples/2D Renderer/Pixel Perfect Camera/3 UI Scaling Example/3 UI Scaling Example.unity

요약

기준 해상도에 맞춰 카메라와 UI 크기를 비교합니다.

목적

화면 크기와 종횡비가 UI 배치에 미치는 영향을 확인합니다.

결과물

기준 해상도 320 x 180과 Screen Space - Camera UI를 사용하는 씬입니다.

사용하면 할 수 있는 것

UI 크기, Crop Frame, Custom Canvas Scaler의 역할을 비교합니다.

상세 설명

Game 뷰의 해상도와 종횡비를 바꾸며 UI를 비교합니다. Canvas의 기존 Canvas Scaler를 끄고 Custom Canvas Scaler를 확인합니다.

Pixel Perfect Camera의 Crop Frame X/Y 설정도 함께 살펴봅니다. Screen Space - Camera 조건에서 UI 배치와 잘림을 비교하세요.

장면을 확인하는 실험 규칙

기본 화면을 먼저 확인합니다.
관찰할 오브젝트와 Light를 고릅니다.
설정 하나만 바꿉니다.
변경 전후 화면을 비교합니다.
바꾼 값과 결과를 기록합니다.
같은 해상도와 종횡비를 유지합니다.

빛이 보이지 않을 때 확인할 순서

  1. 스프라이트의 Sorting Layer를 확인합니다.
  2. Light의 Apply To Sorting Layers를 확인합니다.
  3. Light가 오브젝트를 실제로 덮는지 확인합니다.
  4. Light의 Intensity가 0에 가깝지 않은지 확인합니다.
  5. 스프라이트가 2D Renderer용 재질과 셰이더를 사용하는지 확인합니다.
검증 안내: 이 글은 저장소 문서와 현재 에셋 구조를 기준으로 작성했습니다. Unity 컴파일과 10개 씬의 최종 Play Mode 검증은 별도로 확인해야 합니다.

용어 사전

2D Light2D 그림과 배경을 밝히는 조명 요소입니다.
Normal Map표면 방향을 저장해 명암 계산을 돕는 이미지입니다.
Light Mask빛이 영향을 줄 Sorting Layer를 고르는 필터입니다.
Sorting Layer스프라이트를 화면에 그리는 그룹 순서입니다.
Blend Style여러 빛을 최종 색에 섞는 방식입니다.
Shadow Caster오브젝트가 그림자를 만들 영역을 정의합니다.
Volume화면 효과 설정을 모아 관리하는 영역입니다.
Bloom밝은 부분 주변에 빛 번짐을 추가하는 효과입니다.
Shader Graph노드를 연결해 재질 표현을 만드는 도구입니다.
Pixel Perfect Camera픽셀 단위 표현을 유지하도록 화면을 계산하는 카메라입니다.
Unity 6.6 Dictionary 직렬화 쉽게 이해하기
💡 한 줄 요약
Unity 6.6은 Dictionary<TKey, TValue>를 직접 저장합니다. Inspector에서도 Key와 Value를 바로 편집할 수 있습니다.
핵심 요약
[SerializeField]를 붙이면 Dictionary를 Unity가 직렬화합니다.
Inspector에서 Key와 Value를 직접 수정할 수 있습니다.
List 두 개로 Dictionary를 흉내 낼 필요가 줄었습니다.
필드 타입은 Dictionary<TKey, TValue>를 사용합니다.
public Dictionary도 [SerializeField]를 붙입니다.
목표
Unity 6.6에서 Dictionary를 Inspector 데이터로 사용하는 방법을 알아봅니다.
지원 범위와 주의사항도 함께 확인합니다.
결과물
using System.Collections.Generic;
using UnityEngine;
 
public class Inventory : MonoBehaviour
{
[SerializeField]
private Dictionary<string, int> itemCounts = new();
}
Inspector에서는 Key와 Value를 표 형태로 편집합니다.
Key
Value
Potion
5
Sword
1
결과물로 얻을 수 있는 기대
Key 기반 데이터를 Inspector에서 바로 관리할 수 있습니다.
ScriptableObject 데이터 작성도 더 단순해집니다.
별도 SerializableDictionary와 변환 코드도 줄일 수 있습니다.
상세 설명
1. 무엇이 달라졌나
기존 Unity는 일반적인 Dictionary를 기본 직렬화하지 못했습니다.
개발자는 List 두 개나 커스텀 직렬화 코드를 자주 사용했습니다.
Unity 6.6은 Dictionary<TKey, TValue>를 직접 직렬화합니다.
Inspector도 Dictionary 전용 편집 기능을 제공합니다.
2. Dictionary에는 [SerializeField]를 붙인다
[SerializeField]
private Dictionary<string, int> itemCounts = new();
Unity가 Dictionary를 저장하도록 필드에 속성을 지정합니다.
3. Inspector 표시 순서와 실제 순서는 다르다
Inspector는 Key 기준으로 정렬해서 보여줄 수 있습니다.
하지만 화면 순서와 실제 직렬화 순서는 다를 수 있습니다.
실제 데이터는 항목을 추가한 순서를 유지합니다.
4. 사용할 수 있는 Key와 Value
int, float, bool, string
enum
Vector2, Vector3, Color
[Serializable] 클래스와 struct
UnityEngine.Object 파생 타입
기본형부터 Unity 타입과 직렬화 가능한 커스텀 타입까지 사용할 수 있습니다.
5. Value에는 List와 배열도 사용할 수 있다
[SerializeField]
private Dictionary<int, List<int>> stageRewards = new();
반대로 Key에는 List나 배열을 사용할 수 없습니다.
// 지원하지 않음
Dictionary<List<int>, int>
6. List나 배열 안에 Dictionary를 직접 넣지 않는다
List<Dictionary<string, int>>
Dictionary<string, int>[]
필요하면 Dictionary를 직렬화 가능한 타입으로 감쌉니다.
[System.Serializable]
public class ItemTable
{
[SerializeField]
public Dictionary<string, int> items = new();
}
 
[SerializeField]
private List<ItemTable> tables = new();
7. 커스텀 타입을 Key로 쓸 때 주의한다
커스텀 class는 기본적으로 객체 참조를 비교합니다.
필드 값이 같아도 다른 객체라면 다른 Key로 판단할 수 있습니다.
EqualsGetHashCode 구현을 권장합니다.
값 타입은 IEquatable<T> 구현도 유용합니다.
8. 중복 Key는 Inspector에서 확인한다
Inspector에는 중복 Key가 잠시 표시될 수 있습니다.
런타임 Dictionary는 동일한 Key를 두 개 저장하지 못합니다.
9. null Key도 주의한다
null Key는 런타임 Dictionary에 정상 항목으로 들어가지 않습니다.
Inspector에서 비어 있는 Key를 발견하면 먼저 수정하세요.
10. Inspector 표시를 꾸밀 수 있다
[SerializeField]
[DictionaryDisplay(
keyLabel = "Item",
valueLabel = "Drop Chance",
keyColumnFraction = 0.6f)]
private Dictionary<string, float> drops = new();
열 이름과 Key 영역 비율을 직접 지정할 수 있습니다.
11. Prefab에서는 순서 변경을 조심한다
Prefab Instance는 Dictionary 항목별 Override를 지원합니다.
Unity는 내부 직렬화 위치를 기준으로 Override를 기록합니다.
항목을 추가하거나 삭제하면 Override 위치가 달라질 수 있습니다.
Prefab Dictionary를 수정한 뒤 Instance Override를 다시 확인하세요.
12. 실전에서 어디에 쓰면 좋은가
아이템 ID → 보유 개수
몬스터 ID → 능력치
스테이지 번호 → 보상 목록
사운드 이름 → AudioClip
캐릭터 타입 → Prefab
설정 이름 → ScriptableObject
[SerializeField]
private Dictionary<string, AudioClip> sounds = new();
 
public AudioClip GetSound(string id)
{
return sounds.TryGetValue(id, out var clip) ? clip : null;
}
DictionaryDisplay로 Inspector 표시 꾸미기
같은 Dictionary 타입에 공통 표시 규칙도 지정할 수 있습니다.
using System.Collections.Generic;
using UnityEngine;
 
[assembly: DictionaryDisplayForType(
typeof(Dictionary<string, SkillLevel>),
layout = DictionaryLayout.OneColumnWithValueVisible,
keyLabel = "Skill",
valueLabel = "Level")]
 
[System.Serializable]
public struct SkillLevel
{
public int rank;
public float bonus;
}
 
public class Character : MonoBehaviour
{
[SerializeField]
private Dictionary<int, Dictionary<string, SkillLevel>> skillsPerTier = new();
}
 
public class AbilityBook : MonoBehaviour
{
[SerializeField]
private Dictionary<string, SkillLevel> learnedSkills = new();
}
주의사항
Dictionary마다 [SerializeField]가 필요합니다.
[SerializeReference]를 Dictionary 필드에 직접 붙일 수 없습니다.
Inspector 다중 객체 편집은 지원하지 않습니다.
Inspector 검색과 필터 기능은 없습니다.
Prefab Override와 순서 변경을 함께 사용할 때 주의합니다.
결론
Unity 6.6의 Dictionary 직렬화는 데이터 작성 방식을 크게 단순화합니다.
단순한 Key-Value 데이터라면 별도 SerializableDictionary부터 만들 필요가 없습니다.
먼저 [SerializeField] Dictionary<TKey, TValue>를 검토하면 됩니다.

Unity MCP 기능 요약

Unity 개발 작업에서의 일반적인 사용 빈도를 기준으로 활용도를 평가했습니다.

Assets

MCP 이름 기능 설명 활용도
AssetGeneration_GenerateAsset 이미지·텍스처 등의 에셋 생성 ★★★★☆
AssetGeneration_GetModels 에셋 생성에 사용할 AI 모델 조회 ★★☆☆☆
AssetGeneration_ConvertSprite 이미지를 Unity Sprite로 변환 ★★★★☆
AssetGeneration_ConvertToMaterial 생성 결과물을 Material로 변환 ★★★☆☆
AssetGeneration_ConvertToTexture 생성 결과물을 Texture로 변환 ★★★★☆
AssetGeneration_CreateAnimation 이미지나 에셋을 이용해 애니메이션 생성 ★★★☆☆
AssetGeneration_EditAnimation 생성된 애니메이션 수정 ★★★☆☆
AssetGeneration_GetComposition 에셋 생성 작업의 구성·결과 정보 조회 ★★☆☆☆
AssetGeneration_ManageInterrupted 중단된 에셋 생성 작업 확인·재개 ★★☆☆☆
AudioClip_Edit AudioClip 자르기 등 오디오 편집 ★★★☆☆
FindProjectAssets 프로젝트에서 조건에 맞는 에셋 검색 ★★★★★
ManageShader Shader 생성·수정·조회 ★★★★☆

Core

MCP 이름 기능 설명 활용도
RunCommand Unity Editor 명령이나 등록된 작업 실행 ★★★★★
ApplyTextEdits 코드·텍스트 파일에 부분 수정 적용 ★★★★★
CreateScript 새로운 C# 스크립트 생성 ★★★★★
DeleteScript 기존 스크립트 삭제 ★★☆☆☆
FindInFile 프로젝트 파일 내부 문자열 검색 ★★★★★
GetSha 파일 또는 프로젝트 상태 비교용 해시 조회 ★☆☆☆☆
ImportExternalModel 외부 3D 모델을 Unity 프로젝트로 가져오기 ★★★☆☆
ListResources 접근할 수 있는 Unity 리소스 목록 조회 ★★★★☆
ManageAsset 에셋 생성·이동·이름 변경·삭제·속성 관리 ★★★★★
ManageEditor 플레이·정지·일시정지 등 Editor 제어 ★★★★★
ManageGameObject GameObject와 컴포넌트 생성·수정·삭제 ★★★★★
ManageMenuItem Unity 메뉴 항목 조회·실행 ★★★☆☆
ManageScene Scene 생성·열기·저장 및 구조 관리 ★★★★★
ManageScript 스크립트 생성·조회·수정 통합 관리 ★★★★★
ManageScript_capabilities 지원되는 스크립트 작업 기능 조회 ★★☆☆☆
ReadResource Unity 프로젝트 리소스 내용 읽기 ★★★★☆
ScriptApplyEdits C# 스크립트에 구조화된 코드 수정 적용 ★★★★★
ValidateScript C# 스크립트 문법·컴파일 상태 검사 ★★★★★

Debug & Diagnostics

MCP 이름 기능 설명 활용도
GetConsoleLogs Unity Console의 로그·경고·오류 조회 ★★★★★
Profiler_GetBottomUpSampleTree 하위 호출부터 병목 원인을 역추적 ★★★☆☆
Profiler_GetCounterSummary CPU·메모리 등 선택한 카운터 요약 ★★★★☆
Profiler_GetCounterTable 프레임별 프로파일러 카운터 조회 ★★★☆☆
Profiler_GetFrameGcAllocations 특정 프레임의 GC 할당 원인 분석 ★★★★★
Profiler_GetFrameRangeGcAllocations 여러 프레임의 GC 할당 변화 분석 ★★★★★
Profiler_GetFrameRangeTopTimeSummary 지정 구간에서 시간이 많이 든 작업 요약 ★★★★★
Profiler_GetFrameSelfTimes 특정 프레임의 함수별 Self Time 조회 ★★★★☆
Profiler_GetFrameTopTimeSamples 특정 프레임의 주요 병목 샘플 조회 ★★★★★
Profiler_GetOverallGcAllocations 전체 프로파일링 구간의 GC 할당 요약 ★★★★☆
Profiler_GetRelatedSamplesTree 선택한 샘플과 연결된 호출 관계 조회 ★★★☆☆
Profiler_GetSampleGcAllocations 특정 샘플의 GC 할당량 조회 ★★★★☆
Profiler_GetSampleGcAllocationSummary 특정 샘플의 GC 할당 결과 요약 ★★★★☆
Profiler_GetSampleTimeSummary 선택한 함수·샘플의 실행시간 요약 ★★★★★
Profiler_GetSampleTimeSummary (variant) 조건 또는 범위를 지정한 실행시간 요약 ★★★★☆
ReadConsole Unity Console의 현재 출력 내용 읽기 ★★★★★

Editor

MCP 이름 기능 설명 활용도
Camera_Capture 게임 카메라의 현재 화면 캡처 ★★★★☆
SceneView_Capture2DScene Scene View를 2D 화면으로 캡처 ★★★★☆
SceneView_CaptureMultiAngleSceneView Scene을 여러 각도에서 캡처 ★★★☆☆
GetProjectData Unity 버전·렌더 파이프라인 등 프로젝트 정보 조회 ★★★★★
GetUserGuidelines 프로젝트에 설정된 사용자 작업 지침 조회 ★★★★☆
PackageManager_ExecuteAction Unity 패키지 설치·제거·업데이트 실행 ★★★★☆
PackageManager_GetData 설치된 패키지와 사용 가능한 버전 조회 ★★★★☆
활용도: 일반적인 Unity 프로젝트에서 AI 에이전트가 실제 개발 작업에 사용하는 빈도 기준

'Unity > AI' 카테고리의 다른 글

github copilot과 Unity MCP 연결(Claude Code 도 비슷)  (0) 2026.05.25
Unity MCP Tools 설명  (0) 2026.05.24

Unity uGUI Safe Area로 안전한 모바일 UI 만들기

한 줄 소개: 모바일 기기의 노치, 둥근 모서리, 시스템 제스처 영역을 피해 UI를 배치하는 uGUI Safe Area 적용법입니다.

목표

노치나 화면 모서리 때문에 버튼, HUD, 상단 바가 가려지지 않도록 필수 UI를 안전 영역 안에 배치합니다.

해상도와 화면 방향이 바뀌어도 같은 UI 구조가 자동으로 대응하도록 구성합니다.

 

유니티 : 6.6
패키지 버전: uGUI 2.6.0 (패키지 매니저에서 설치)
날짜: 2026년 3월 30일

(오랜기간 직접 개발해서 썼는데 드디어 지원. 울고 싶다. )

결과

UI가 다양한 모바일 화면에서 잘리지 않고 안정적으로 표시됩니다.

핵심 요약

  • Safe Area 컴포넌트는 기기가 알려 주는 Screen.safeArea를 기준으로 RectTransform의 앵커를 조정합니다.
  • 해상도나 화면 방향이 바뀌면 안전 영역도 런타임에 다시 반영됩니다.
  • 배경처럼 화면 끝까지 보여야 하는 요소는 Safe Area 밖에 두고, 버튼과 HUD처럼 반드시 보여야 하는 요소만 안에 둡니다.
  • 한쪽 노치 때문에 UI가 치우치면 Center Horizontally 또는 Center Vertically를 사용합니다.

적용 방법

1. 전용 루트 오브젝트 만들기

Canvas 아래에 화면 전체를 채우는 SafeAreaRoot를 만들고 Safe Area 컴포넌트를 추가합니다.

 

빨간색은 SafeArea

파란색은 TopBar

중앙.

초록색은 BottomBar

빨간색은 SafeArea

SafeAreaTestCanvas.zip
0.10MB

2. 필수 UI를 자식으로 이동하기

화면에서 반드시 보여야 하는 버튼, 텍스트, HUD를 SafeAreaRoot 아래에 둡니다.

전체 화면을 채울 배경과 장식 이미지는 Canvas 바로 아래에 유지합니다.

3. Inspector 설정하기

  1. Reference Orientation을 UI를 제작한 기준 방향으로 설정합니다.
  2. Edges에서 안전 영역을 적용할 위·오른쪽·아래·왼쪽 가장자리를 선택합니다.
  3. 가로 화면에서 한쪽 노치 때문에 UI가 치우치면 Center Horizontally를 켭니다.
  4. 위아래 여백이 비대칭이라면 Center Vertically를 켭니다.

참고 자료

 

----

 

 

'Unity > UI' 카테고리의 다른 글

RectTransformUtility  (0) 2023.03.14
CalculateRelativeRectTransformBounds  (1) 2021.08.04
텍스트 배경크기조절. ContentSizeFitter  (0) 2021.08.04
글자수에 따라 배경의 크기가 자동으로 커지는 효과  (0) 2020.08.11
팁 정리  (0) 2019.10.30

기본 / 생성

옵션뜻입력 예시

Enabled 자동 생성 여부 true
Rate 초당 생성 개수 20
Lifetime 파티클 생존 시간 1 ~ 2
TimeScale 파티클 시간 배율 1

외형

옵션뜻입력 예시

Texture 파티클 이미지 rbxassetid://123
Size 수명에 따른 크기 변화 0 → 2 → 0
Squash 가로/세로 눌림·늘림 0 → 1
Color 수명에 따른 색 변화 흰색 → 파랑
Transparency 수명에 따른 투명도 1 → 0 → 1
Brightness 파티클 밝기 1
LightEmission 자체 발광 느낌 0.8
LightInfluence 주변 조명의 영향 0
ZOffset 화면상 앞뒤 렌더 위치 0.1

이동

옵션뜻입력 예시

Speed 처음 이동 속도 5 ~ 10
Acceleration 생성 후 계속 받는 가속도 0, 10, 0
Drag 이동 감속 정도 3
VelocityInheritance 부모 이동속도 상속 비율 0.5
LockedToPart 생성 후에도 부모를 따라갈지 false
WindAffectsDrag 월드 바람 영향 여부 true

방출 방향

옵션뜻입력 예시

EmissionDirection 기본 방출 방향 Top
SpreadAngle 방출되는 퍼짐 각도 30, 30

EmissionDirection

Top
Bottom
Front
Back
Left
Right

회전

옵션뜻입력 예시

Rotation 생성될 때 시작 회전각 0 ~ 360
RotSpeed 초당 회전 속도 -90 ~ 90

파티클 방향

옵션뜻입력 예시

Orientation 파티클 이미지가 바라보는 방향 FacingCamera

Orientation

FacingCamera              카메라를 바라봄
FacingCameraWorldUp       세로 방향을 유지하며 카메라를 봄
VelocityParallel          이동 방향과 평행
VelocityPerpendicular     이동 방향과 수직

생성 영역 Shape

옵션뜻입력 예시

Shape 파티클 생성 영역 모양 Sphere
ShapeStyle 영역 내부/표면 생성 Volume
ShapeInOut 안쪽/바깥쪽 방출 Outward
ShapePartial Shape 일부만 사용 0.5

Shape

Box
Sphere
Cylinder
Disc

ShapeStyle

Volume     내부 전체에서 생성
Surface    표면에서만 생성

ShapeInOut

Outward     바깥쪽으로
Inward      안쪽으로
InAndOut    양쪽 랜덤

Flipbook 애니메이션

옵션뜻입력 예시

FlipbookLayout 스프라이트 프레임 배열 Grid4x4
FlipbookSizeX Custom 가로 프레임 수 4
FlipbookSizeY Custom 세로 프레임 수 4
FlipbookFramerate 애니메이션 FPS 12
FlipbookMode 애니메이션 재생 방식 Loop
FlipbookStartRandom 랜덤 프레임부터 시작 true
FlipbookBlendFrames 프레임 사이 부드럽게 보간 true

FlipbookLayout

None
Grid2x2
Grid4x4
Grid8x8
Custom

FlipbookMode

Loop       반복
OneShot    한 번 재생
PingPong   정방향 → 역방향 반복
Random     랜덤

거의 안 쓰는 옵션

옵션뜻입력 예시

LocalTransparencyModifier 로컬 투명도 추가 보정 0
VelocitySpread 구형 퍼짐 옵션, Deprecated 사용 안 함
FlipbookIncompatible Flipbook 사용 불가 상태 확인 읽기용

가장 자주 쓰는 핵심만 뽑으면

Rate                초당 생성량              20
Lifetime            생존시간                 1 ~ 2
Size                크기 변화                0 → 2 → 0
Transparency        투명도 변화              1 → 0 → 1
Speed               초기 속도                5 ~ 10
Acceleration        가속도                   0, 10, 0
Drag                감속                     3
EmissionDirection   방출 방향                Top
SpreadAngle         퍼짐 각도                30, 30
Rotation            시작 회전                0 ~ 360
RotSpeed            회전 속도                -90 ~ 90
Color               색 변화                  흰색 → 파랑
Texture             이미지                   rbxassetid://123

 

트레일 텍스쳐 옵션정리

'로블록스' 카테고리의 다른 글

데이터 저장소 4.(캐싱)  (0) 2026.07.30
데이터 저장소. 3 (버전)  (0) 2026.07.30
데이터 저장소. 2 (웹페이지에서 접근)  (0) 2026.07.30
데이터 저장소. 1 ( 기본 )  (0) 2026.07.30
카메라 변경  (0) 2026.07.26

캐싱

성능을 개선하고 서버에 대한 요청 수를 줄이기 위해 데이터 저장소의 데이터를 임시로 저장하는 데 캐싱을 사용하세요. 예를 들어, 게임은 데이터의 복사본을 캐시하여 데이터 저장소에 다른 호출을 할 필요 없이 빠르게 접근할 수 있습니다.

 

실제 구현은?

Roblox 데이터 저장소(DataStore)에서 GetAsync(), SetAsync(), UpdateAsync(), IncrementAsync(), RemoveAsync() 등의 작업 명령어를 호출하면, 기본적으로 엔진 차원에서 4초간의 로컬 캐싱이 자동으로 작동합니다.

따라서 개발자가 별도의 캐시 시스템을 구현하지 않아도 데이터 저장소를 사용하는 것만으로 캐싱이 자연스럽게 적용됩니다.

캐싱 비활성화 

local ok = pcall(function()
    store:UpdateAsync(key, transform)
end)

if not ok then
    local options = Instance.new("DataStoreGetOptions")
    options.UseCache = false

    local success, value = pcall(function()
        return store:GetAsync(key, options)
    end)

    if success then
        -- 캐시된 상태가 아닌 백엔드 상태에 따라 결정
    end
end

'로블록스' 카테고리의 다른 글

파티클  (0) 2026.08.22
데이터 저장소. 3 (버전)  (0) 2026.07.30
데이터 저장소. 2 (웹페이지에서 접근)  (0) 2026.07.30
데이터 저장소. 1 ( 기본 )  (0) 2026.07.30
카메라 변경  (0) 2026.07.26

1. 버전이 생성되는 기준 (하루 1회)

  • UTC 기준 하루 첫 쓰기 시점: 특정 데이터(키)를 SetAsync(), UpdateAsync(), IncrementAsync()로 처음 변경할 때, 그 시점의 이전 데이터가 백업 버전으로 저장됩니다.
  • 같은 날 추가 변경 시: 같은 UTC 날짜(24시간) 안에서 랭킹이나 점수가 10번, 100번 변경되어 계속 업데이트되더라도, 이전 데이터를 덮어쓸 뿐 매번 새 백업 버전이 생기지는 않습니다.

2. 이전 데이터 복구 (백업)

  • 만약 플레이어의 데이터가 손상되거나 버그가 발생하면, ListVersionsAsync()로 해당 날짜에 저장된 백업 목록을 조회하고, GetVersionAsync()로 해당 시점의 데이터를 가져와 복구할 수 있습니다.
  • 이 백업 버전 데이터는 30일 동안 보관된 후 자동으로 삭제됩니다.

특정 유저 데이터가 깨졌다면

개발자가 직접 구현해야 하는 부분

  1. 복구 스크립트 
    • 복구할 플레이어의 Key(예: User_12345)를 지정합니다.
    • ListVersionsAsync()를 호출하여 과거 저장된 버전 목록(시간대별 백업 목록)을 가져옵니다.
    • 복구하고자 하는 특정 시간대의 버전을 선택합니다.
    • GetVersionAsync()로 해당 버전의 데이터를 읽어옵니다.
    • 읽어온 옛날 데이터를 SetAsync()로 현재 최신 데이터 위치에 덮어씌웁니다.
  2. 예시)
local DataStoreService = game:GetService("DataStoreService")
local playerDataStore = DataStoreService:GetDataStore("PlayerData")

local function rollbackPlayerData(userId, targetDateTime)
    local key = "User_" .. userId
    
    -- 1. 지정한 시간(targetDateTime) 이전의 가장 가까운 버전 목록 조회
    local success, pages = pcall(function()
        return playerDataStore:ListVersionsAsync(
            key, 
            Enum.SortDirection.Descending, 
            nil, 
            targetDateTime.UnixTimestampMillis
        )
    end)
    
    if success then
        local items = pages:GetCurrentPage()
        if #items > 0 then
            local targetVersion = items[1].Version -- 가장 가까운 과거 버전 번호
            
            -- 2. 해당 버전의 데이터값 읽어오기
            local getSuccess, oldData = pcall(function()
                return playerDataStore:GetVersionAsync(key, targetVersion)
            end)
            
            -- 3. 읽어온 옛날 데이터로 현재 데이터 덮어쓰기 (복구 실행)
            if getSuccess and oldData then
                playerDataStore:SetAsync(key, oldData)
                print(userId .. "번 유저 데이터 복구 완료!")
            end
        end
    end
end

'로블록스' 카테고리의 다른 글

파티클  (0) 2026.08.22
데이터 저장소 4.(캐싱)  (0) 2026.07.30
데이터 저장소. 2 (웹페이지에서 접근)  (0) 2026.07.30
데이터 저장소. 1 ( 기본 )  (0) 2026.07.30
카메라 변경  (0) 2026.07.26

+ Recent posts