성능 향상

*이 콘텐츠는 AI(베타)를 사용해 번역되었으며, 오류가 있을 수 있습니다. 이 페이지를 영어로 보려면 여기를 클릭하세요.

이 페이지는 일반적인 성능 문제와 이를 완화하기 위한 최선의 방법을 설명합니다.

스크립트 계산

Luau 코드에서 비싼 작업은 처리하는 데 더 오랜 시간이 걸리며, 따라서 프레임 속도에 영향을 미칠 수 있습니다. 병렬로 실행되지 않는 한, Luau 코드는 동기적으로 실행되며, 스레드가 양보하는 함수를 만날 때까지 기본 스레드를 차단합니다.

일반적인 문제

  • 테이블 구조에 대한 집중적인 작업 - 직렬화, 역직렬화 및 깊은 복제와 같은 복잡한 작업은 특히 큰 테이블 구조에 대해 높은 성능 비용을 초래합니다. 이러한 작업이 재귀적이거나 매우 큰 데이터 구조를 반복적으로 포함하는 경우 특히 그렇습니다.

  • 높은 빈도의 이벤트 - RunService의 프레임 기반 이벤트에 비싼 작업을 연결하되 빈도를 제한하지 않으면, 이러한 작업이 매 프레임마다 반복되어 계산 시간의 불필요한 증가를 초래할 수 있습니다. 이러한 이벤트에는 다음이 포함됩니다:

완화

  • RunService 이벤트에서 코드를 드물게 호출하고, 높은 빈도의 호출이 필수적인 경우(예: 카메라 업데이트)로 제한하십시오. 대부분의 다른 코드는 다른 이벤트에서 실행하거나 덜 자주 루프 내에서 실행할 수 있습니다.
  • task.wait()를 사용하여 큰 또는 비싼 작업을 분할하여 여러 프레임에 걸쳐 작업을 분산시키십시오.
  • 불필요하게 비싼 작업을 식별하고 최적화하며, 데이터 모델에 액세스할 필요가 없는 계산 집약적 작업에는 멀티 스레딩을 사용하십시오.
  • 특정 서버 측 스크립트는 네이티브 코드 생성의 이점을 볼 수 있으며, 이는 스크립트를 바이트 코드가 아닌 기계 코드로 컴파일하는 간단한 플래그입니다.

MicroProfiler 범위

범위관련 계산
RunService.PreRenderPreRender 이벤트에서 실행되는 코드
RunService.PreSimulationStepped 이벤트에서 실행되는 코드
RunService.PostSimulationHeartbeat 이벤트에서 실행되는 코드
RunService.HeartbeatHeartbeat 이벤트에서 실행되는 코드

MicroProfiler를 사용하여 스크립트를 디버그하는 방법에 대한 자세한 내용은 특정 코드를 태그하고 구체성을 더욱 높이는 기능을 포함하는 debug 라이브러리를 참조하십시오. 스크립트에서 호출하는 많은 Roblox API 메서드에도 유용한 신호를 제공할 수 있는 자체 관련 MicroProfiler 태그가 있습니다.

스크립트 메모리 사용

메모리 누수는 스크립트를 작성할 때 발생할 수 있으며, 가비지 수집기가 더 이상 사용되지 않을 때 제대로 해제하지 못하는 메모리를 소비하게 됩니다. 누수는 주로 서버 측에서 발생하는데, 이는 서버가 수많은 일수 동안 온라인 상태를 유지할 수 있지만 클라이언트 세션은 훨씬 짧기 때문입니다.

Developer Console의 다음 메모리 값은 추가 조사가 필요한 문제를 나타낼 수 있습니다:

  • LuaHeap - 높은 소비 또는 성장하는 소비는 메모리 누수를 암시합니다.
  • InstanceCount - 일관되게 증가하는 인스턴스 수는 코드에서 일부 인스턴스에 대한 참조가 가비지 수집되지 않음을 나타냅니다.
  • PlaceScriptMemory - 스크립트별 메모리 사용 내역을 제공합니다.

일반적인 문제

  • 연결 상태로 남아 있는 연결 - 엔진은 인스턴스에 연결된 이벤트와 연결된 콜백 내의 값을 절대 가비지 수집하지 않습니다. 따라서 연결된 인스턴스 및 참조된 값 내의 코드의 활성 연결은 이벤트가 발생한 후에도 메모리 가비지 수집의 범위를 벗어납니다.

    인스턴스가 파괴될 때 이벤트는 연결 해제되지만, 일반적인 실수는 이것이 Player 객체에 적용된다고 가정하는 것입니다. 사용자가 게임을 떠나면 엔진은 자동으로 해당 사용자에 대한 Player 객체와 캐릭터 모델을 파괴하지 않으므로, 캐릭터 모델 아래의 Player 객체 및 인스턴스에 연결된 것은 스크립트에서 연결 해제하지 않으면 메모리를 계속 소비합니다. 이로 인해 수백 명의 사용자가 게임에 가입하고 떠나는 동안 서버에 매우 큰 메모리 누수가 발생할 수 있습니다.

  • 테이블 - 테이블에 객체를 삽입하지만 더 이상 필요하지 않을 때 제거하지 않으면 메모리 소비가 불필요하게 증가합니다. 특히 사용자가 접속할 때 사용자 데이터를 추적하는 테이블에 대해 그렇습니다. 예를 들어, 아래의 코드 샘플은 사용자가 접속할 때마다 사용자 정보를 추가하는 테이블을 생성합니다:

    예제
    local playerInfo = {}
    Players.PlayerAdded:Connect(function(player)
    playerInfo[player] = {} -- 일부 정보
    end)

    이러한 항목을 더 이상 필요하지 않을 때 제거하지 않으면, 테이블의 크기가 계속해서 증가하고 세션에 더 많은 사용자가 추가될수록 더 많은 메모리를 소비하게 됩니다. 이 테이블을 반복하는 모든 코드는 테이블의 크기가 커짐에 따라 계산 비용이 더 높아집니다.

완화

메모리 누수를 방지하기 위해 사용된 모든 값을 정리하려면:

  • 모든 연결 해제 - 코드베이스를 검토하고 각 연결이 다음 경로 중 하나를 통해 정리되도록 하십시오:

    • Disconnect() 함수를 사용하여 수동으로 연결 해제.
    • Destroy() 함수를 사용하여 이벤트가 속한 인스턴스를 파괴하고.
    • 연결이 추적되는 스크립트 객체를 파괴합니다.
  • 사용자가 떠난 후 플레이어 객체 및 캐릭터 제거 - Workspace.PlayerCharacterDestroyBehavior를 활성화하여 사용자가 떠난 후 플레이어 객체 및 캐릭터 모델을 자동으로 파괴하도록 설정합니다. 원하시면 대신 수동으로 정리할 수 있습니다:

    예제 플레이어 및 캐릭터 정리
    local Players = game:GetService("Players")
    Players.PlayerAdded:Connect(function(player)
    player.CharacterRemoving:Connect(function(character)
    task.defer(character.Destroy, character)
    end)
    end)
    Players.PlayerRemoving:Connect(function(player)
    task.defer(player.Destroy, player)
    end)

물리 계산

과도한 물리 시뮬레이션은 서버와 클라이언트 모두에서 프레임당 계산 시간이 증가하는 주요 원인일 수 있습니다.

일반적인 문제

  • 과도한 물리적 시간 단계 빈도 - 기본적으로, 스텝 동작은 적응 모드이며, 물리적 스텝은 60Hz, 120Hz 또는 240Hz로 수행됩니다. 물리 메커니즘의 복잡성에 따라 달라집니다.

    개선된 물리적 정확도를 제공하는 고정 모드도 있으며, 이는 모든 물리 집합체가 240Hz(프레임당 4회)로 스텝을 수행하도록 강제합니다. 이로 인해 매 프레임마다 계산량이 상당히 증가합니다.

  • 시뮬레이션된 객체의 과도한 수 또는 복잡성 - 시뮬레이션되는 3D 집합체가 많을수록 각 프레임에서 물리 계산에 시간이 더 걸립니다. 종종 게임에서는 시뮬레이션할 필요가 없는 객체가 포함되거나 필요 이상으로 많은 제약 조건과 관절을 가진 메커니즘이 있을 수 있습니다.

  • 과도하게 정밀한 충돌 감지 - 메쉬 파트에는 충돌을 감지하기 위한 CollisionFidelity 속성이 있으며, 이는 다양한 성능 영향을 미치는 모드를 제공합니다. 메쉬 파트에 대한 정밀한 충돌 감지 모드는 가장 비싼 성능 비용을 가지며 엔진의 계산 시간이 더 길어집니다.

완화

  • 시뮬레이션이 필요하지 않은 파트 고정 - 물리적 구동이 필요하지 않은 모든 파트를 고정하십시오. 예를 들어, 정적 NPC의 경우 그렇습니다.

  • 적응 물리 스텝 사용 - 적응적 스텝은 물리 메커니즘에 대한 물리 계산 속도를 동적으로 조정하여 경우에 따라 물리 업데이트를 덜 자주 수행할 수 있도록 합니다.

  • 메커니즘 복잡성 줄이기

    • 가능한 한 메커니즘 내의 물리적 제약 조건이나 관절 수를 최소화합니다.
    • 메커니즘 내의 자기 충돌을 줄입니다. 예를 들어 래그돌 팔다리가 서로 충돌하지 않도록 제한 또는 비충돌 제약 조건을 적용합니다.
  • 메쉬에 대한 정밀 충돌 충실도 사용 줄이기

    • 사용자가 그 차이를 거의 인식하지 않을 작은 물체나 상호작용이 없는 물체의 경우 상자 정밀도를 사용합니다.

    • 작은 중간 크기의 물체에 대해서는 물체 모양에 따라 상자 또는 외형 정밀도를 사용합니다.

    • 크고 매우 복잡한 객체의 경우 가능한 경우 보이지 않는 파트를 사용하여 사용자 정의 충돌을 구축합니다.

    • 충돌이 필요 없는 객체의 경우 충돌을 비활성화하고 상자 또는 외형 충실도를 사용하십시오. 충돌 기하학은 여전히 메모리에 저장됩니다.

    • 스튜디오에서 디버그 목적으로 충돌 기하학을 렌더링하려면 3D 뷰포트 오른쪽 상단의 Visualization Options 위젯에서 충돌 충실도를 켭니다.

      또는 Explorer에서 CollisionFidelity=PreciseConvexDecomposition 필터를 적용하여 정밀 충실도를 가진 모든 메쉬 파트 개수를 표시하고 쉽게 선택할 수 있습니다.

    • 정밀도와 성능 요구사항을 조화롭게 하는 충돌 충실도 옵션을 선택하는 방법에 대한 심층적인 과정은 물리 및 렌더링 매개변수 설정을 참조하십시오.

MicroProfiler 범위

범위관련 계산
physicsStepped전체 물리 계산
worldStep각 프레임에서 수행된 이산 물리 단계

물리 메모리 사용

물리적 이동과 충돌 감지는 메모리를 소비합니다. 메쉬 파트에는 메쉬의 충돌 경계를 평가하는 데 사용되는 접근 방식을 결정하는 CollisionFidelity 속성이 있습니다.

일반적인 문제

기본 및 정밀 충돌 감지 모드는 하위 충실도 충돌 모드보다 상당히 더 많은 메모리를 소비합니다.

PhysicsParts 아래에서 높은 메모리 소비가 나타나면, 게임의 개체들의 충돌 충실도를 줄이는 방법을 탐색해야 할 수도 있습니다.

완화 방법

충돌 충실도로 인해 사용되는 메모리를 줄이려면:

  • 충돌이 필요 없는 파트의 경우 BasePart.CanCollide, BasePart.CanTouchBasePart.CanQueryfalse로 설정하여 충돌을 비활성화합니다.
  • CollisionFidelity 설정을 사용해 충돌의 충실도를 줄입니다. Box는 메모리 오버헤드가 가장 낮고, DefaultPrecise는 일반적으로 더 비쌉니다.
    • 일반적으로 작은 고정된 파트의 충돌 충실도를 Box로 설정하는 것이 안전합니다.
    • 매우 복잡한 큰 메쉬의 경우, 작은 객체로 충돌 메쉬를 만드는 것을 고려할 수 있습니다.

휴머노이드

Humanoid는 플레이어 및 비 플레이어 캐릭터(NPC)에 대해 광범위한 기능을 제공하는 클래스입니다. 강력하지만 Humanoid는 상당한 계산 비용이 발생합니다.

일반적인 문제

  • 모든 HumanoidStateTypes를 NPC에 알리기 - 특정 HumanoidStateTypes를 활성 상태로 두는 데는 성능 비용이 있습니다. NPC에 필요하지 않은 주 상태는 비활성화하십시오. 예를 들어, NPC가 사다리를 오를 일이 없다면 Climbing 상태를 비활성화하는 것이 안전합니다.
  • Humanoids 또는 스키닝MeshParts의 빈번한 인스턴스화, 수정 및 리스폰 - 이는 엔진이 처리하는 데 많은 자원을 소모할 수 있으며, 특히 이러한 모델이 레이어드 의류를 사용하는 경우 더욱 그러합니다. 이는 아바타가 자주 리스폰되는 게임에서 특히 문제가 됩니다.
    • MicroProfiler에서 긴 updateInvalidatedFastClusters 태그(4ms 이상)는 아바타 인스턴스화/수정이 과도한 무효처리를 유발하고 있다는 신호일 수 있습니다.
  • 필요하지 않은 경우에 휴머노이드 사용 - 정적 NPC는 일반적으로 Humanoid 클래스가 필요 없습니다.
  • 서버에서 다수의 NPC 애니메이션 재생 - 서버에서 실행되는 NPC 애니메이션은 서버에서 시뮬레이션되어 클라이언트에 복제되어야 하므로 불필요한 부하가 초래될 수 있습니다.
  • 불필요한 크기 및 비율 변경 수행 - 크기/비율 변경은 FastCluster를 재구축해야 합니다. FastCluster 관련 성능 문제를 경험한다면 이 작업을 줄이십시오. 유사하게, 다른 속성 변경도 FastCluster를 재구축하게 만들 수 있으므로, 일반적으로 이러한 변경을 가능한 한 줄이십시오.

완화

  • 클라이언트에서 NPC 애니메이션 재생 - 많은 NPC가 있는 게임에서는 클라이언트에서 Animator를 생성하고 애니메이션을 로컬에서 실행하도록 고려하십시오. 이렇게 하면 서버의 부하가 줄어들고 불필요한 복제를 방지할 수 있습니다. 또한 NPC와 캐릭터에 가까운 NPC에 대해서만 애니메이션을 재생하는 등의 추가 최적화가 가능해집니다.
  • 휴머노이드의 성능 친화적인 대안 사용 - NPC 모델은 반드시 휴머노이드 객체를 포함할 필요는 없습니다.
    • 정적 NPC의 경우 이동할 필요가 없기 때문에 단순한 AnimationController를 사용하십시오.
    • 이동하는 NPC의 경우, NPC의 복잡성에 따라 독자적인 이동 제어기를 구현하고 애니메이션용으로 AnimationController를 사용할 수 있습니다.
  • 사용하지 않는 휴머노이드 상태 비활성화 - Humanoid:SetStateEnabled()를 사용하여 각 휴머노이드에 대해 필요 상태만 활성화하십시오.
  • 빈번히 리스폰되는 NPC 모델 풀링 - NPC를 완전히 파괴하는 대신 비활성 상태의 NPC 풀로 보냅니다. 이렇게 하면 새로운 NPC를 리스폰할 때 기존 NPC 중 하나를 다시 활성화하면 됩니다. 이 프로세스는 풀링이라고 하며 캐릭터 인스턴스화 횟수를 최소화합니다.
  • 사용자가 근처에 있을 때만 NPC 생성 - 사용자가 범위에 없을 때 NPC를 생성하지 않으며, 사용자가 범위를 벗어날 때 제거합니다.
  • 인스턴스화된 후 아바타 계층 구조에 변경 금지 - 아바타 계층 구조에 대한 특정 수정은 상당한 성능 영향을 미칠 수 있습니다. 다음과 같은 최적화 방법이 있습니다:

MicroProfiler 범위

범위관련 계산
stepHumanoid휴머노이드 제어 및 물리
stepAnimation휴머노이드 및 애니메이터 애니메이션
updateInvalidatedFastClusters아바타를 인스턴스화하거나 수정하는 것과 관련됨

렌더링

클라이언트가 매 프레임에서 보내는 시간의 상당 부분은 현재 프레임의 장면 렌더링에 소모됩니다. 서버는 렌더링을 하지 않으므로 이 섹션은 클라이언트 전용입니다.

드로우 호출

드로우 호출은 엔진이 GPU에게 렌더링 지시를 내리는 설정입니다. 드로우 호출은 상당한 오버헤드를 가집니다. 일반적으로 프레임당 드로우 호출 수가 적을수록 렌더링에 소모되는 계산량이 줄어듭니다.

스타디오의 Render StatsTiming 항목에서 현재 몇 개의 드로우 호출이 발생하고 있는지 확인할 수 있습니다. 클라이언트에서 Render Stats를 보려면 ShiftF2를 누르십시오.

주어진 프레임에서 장면에 그려야 하는 객체가 많을수록 GPU에 대한 드로우 호출이 많아집니다. 그러나 Roblox 엔진은 _인스턴싱_이라는 프로세스를 활용하여 동일한 텍스처 특성을 가진 동일한 메쉬를 단일 드로우 호출로 결합합니다. 구체적으로, 동일한 MeshContent를 가진 여러 메쉬는:

기타 일반적인 문제

  • 과도한 객체 밀도 - 많은 수의 객체가 높은 밀도로 집중되어 있는 경우, 장면의 해당 영역을 렌더링하는 데 더 많은 드로우 호출이 필요합니다. 특정 맵의 일부를 살펴보았을 때 프레임 속도가 떨어지는 경우, 해당 영역의 객체 밀도가 너무 높을 수 있다는 좋은 신호입니다.

    데칼, 텍스처 및 입자와 같은 객체는 배치가 잘되지 않으며 추가 드로우 호출을 유발합니다. 이 객체 유형에 대해 장면에서 특히 주의하십시오. 특히, ParticleEmitters의 속성 변경은 성능에 극적인 영향을 미칠 수 있습니다.

  • 인스턴싱 기회 놓치기 - 종종 장면에는 동일한 메쉬가 여러 번 복제되어 포함되지만, 각 복제본은 서로 다른 메쉬 또는 텍스처 자산 ID를 가지고 있습니다. 이로 인해 인스턴싱이 방해받고 불필요한 드로우 호출이 발생합니다.

    이 문제의 일반적인 원인은 전체 장면이 한 번에 가져와지는 것입니다. 각 자산이 Roblox에 개별적으로 가져와지고, 가져온 후에 복제되어 장면을 조립하는 것이 아닙니다.

    다음과 같이 같은 이름을 가진 메쉬 파트를 찾아내는 간단한 스크립트도 있습니다:

    for _,descendant in workspace:GetDescendants() do
    if descendant:IsA("MeshPart") then
    print(descendant.Name .. ", " .. descendant.MeshId)
    end
    end

    출력( Stack Lines가 활성화된 경우)은 다음과 같을 수 있습니다. 반복되는 줄은 동일한 메쉬 재사용을 나타내므로 바람직합니다. 고유한 줄은 반드시 나쁜 것은 아니지만, 명명 규칙에 따라 게임 내 중복 메쉬를 나타낼 수 있습니다:

    LargeRock, rbxassetid://106420009602747 (x144) -- 좋음
    LargeRock, rbxassetid://120109824668127
    LargeRock, rbxassetid://134460273008628
    LargeRock, rbxassetid://139288987285823
    LargeRock, rbxassetid://71302144984955
    LargeRock, rbxassetid://90621205713698
    LargeRock, rbxassetid://113160939160788
    LargeRock, rbxassetid://135944592365226 -- 가능한 모든 중복
  • 과도한 객체 복잡성 - 드로우 호출 수만큼 중요하지는 않지만, 장면의 삼각형 수는 프레임 렌더링에 소요되는 시간에 영향을 미칩니다. 매우 많은 수의 매우 복잡한 메쉬를 가진 장면은 일반적인 문제이며, 너무 많은 메쉬에 대해 MeshPart.RenderFidelity 속성이 Precise로 설정된 장면도 마찬가지입니다.

  • 과도한 그림자 casting - 그림자 처리는 비용이 높은 프로세스이며, 그림자를 casting하는 많은 수의 조명 객체(또는 그림자의 영향을 받는 많은 수의 작은 부분)가 있는 맵에서는 성능 문제가 발생할 수 있습니다.

  • 높은 투명성 중복 - 부분적으로 투명한 객체를 서로 가까이 배치할 경우 엔진은 겹치는 픽셀을 여러 번 렌더링해야 하므로 성능이 저하될 수 있습니다. 이 문제를 식별하고 해결하는 방법에 대한 자세한 내용은 레이어드 투명도 삭제를 참조하십시오.

  • 불필요한 스킨 메쉬 파트 이동 - 휴머노이드가 없는 모델의 일부인 스킨드 메쉬 파트는 공간적으로 조직된 FastClusters를 이용하여 그룹화됩니다. 이러한 메쉬 파트가 이동할 때마다 이러한 공간 클러스터에 추가되거나 제거되어야 하므로 클러스터가 재구축되고 성능에 영향을 줍니다.

    • 효과적인 해결 방법은 모델 내에 휴머노이드를 삽입하는 것입니다. 휴머노이드의 존재는 기본 공간 클러스터링 동작을 무시하고 모델 전체에 대해 단일 통합 FastCluster의 사용을 강제합니다. 결과적으로 위치 업데이트는 더 이상 클러스터 재구성을 필요로 하지 않으므로 성능 병목 현상이 완화됩니다. 이 기술은 이동이 예상되는 메쉬 파트에 대해서만 사용해야 하며, 메모리 오버헤드를 초래하고 공간 최적화의 이점을 무효화할 수 있습니다. 이와 같은 변경을 한 후에는 항상 게임을 프로파일링할 것을 권장합니다. 추가 정보는 휴머노이드 성능 팁를 참조하십시오.
  • Class.Model에 너무 많은 부품 - 모델에 너무 많은 부품이 포함되는 경우 부품의 속성이 변경되어 전체 재구성이 필요할 가능성으로 인해 더 자주 재구성이 발생할 수 있습니다. FastCluster를 사용하고 있을 때 모델의 부품 수의 적절한 균형을 찾으십시오.

완화

  • 동일한 메쉬 인스턴싱 및 고유 메쉬 수 줄이기 - 모든 동일한 메쉬가 동일한 기본 자산 ID를 가질 수 있도록 보장하면 엔진이 이를 인식하고 단일 드로우 호출로 렌더링할 수 있습니다. 각 메쉬를 맵에 한 번만 업로드하고 Studio의 재사용을 위해 복제하는 것이 좋습니다. 대규모 맵을 한 번에 가져오면 동일한 메쉬가 서로 다른 콘텐츠 ID를 가질 수 있으며 엔진에 의해 고유한 자산으로 인식될 수 있습니다. 패키지는 객체 재사용을 위한 유용한 메커니즘입니다.

  • 컬링 - 컬링은 최종 렌더링 프레임에 포함되지 않은 객체의 드로우 호출을 제거하는 프로세스를 설명합니다. 기본적으로 엔진은 카메라의 시야(프러스텀 컬링) 외부에 있는 객체 및 다른 객체에 의해 가려진 부분과 메쉬를 위해 드로우 호출을 건너뜁니다(차단 컬링). 실내 환경과 같은 특정 시나리오에서는 방 또는 포탈 시스템을 구현하고 수동으로 객체를 컬링하여 드로우 호출이나 전체 계산 부하를 더욱 줄일 수 있습니다.

  • 모델의 세부 정보 수준 줄이기 - 인스턴스 스트리밍을 활성화하고 월드 모델의 LevelOfDetail 속성을 SLIM으로 설정하여 카메라에서 거리가 멀어질수록 최적화된 경량 SLIM 메쉬를 렌더링합니다.

  • 아바타의 세부 수준 줄이기 - 인스턴스 스트리밍을 활성화하고 Workspace.EnableSLIMAvatars를 설정하여 카메라에서 거리가 멀어질수록 최적화된 경량 SLIM 표현을 렌더링합니다.

  • 렌더링 충실도 줄이기 - MeshPart.RenderFidelityAutomatic 또는 Performance로 설정합니다. 이렇게 하면 메쉬가 덜 복잡한 대안으로 대체되므로 드로우해야 할 다각형 수가 줄어들 수 있습니다.

  • 적절한 파트 및 조명 객체에서 그림자 casting 비활성화 - Roblox 엔진은 클라이언트 그래픽 품질 수준이 감소함에 따라 그림자 품질을 자동으로 저하시키고, 품질 수준이 4 이하로 떨어지면 그림자를 전혀 비활성화합니다. 그러나 특정 조명 객체 및 부품에서 그림자 casting 속성을 선택적으로 비활성화하여 그림자 활성화 중 성능을 향상시키고 그림자가 활성화된 상태를 유지할 가능성을 높일 수 있습니다. 편집 시간 또는 런타임 중에 아래와 같은 최적화 적용의 예시:

    • 그림자가 보일 가능성이 없는 작은 부분에서 그림자 casting을 비활성화하는 BasePart.CastShadow 속성을 사용하십시오. 이 전략은 사용자의 카메라에서 멀리 떨어진 부분에 적용할 경우 특히 효과적입니다.

    • 가능한 경우 이동 물체의 그림자를 비활성화합니다.

    • 그림자가 필요 없는 조명 인스턴스에서 Light.Shadows를 비활성화합니다.

    • 조명 인스턴스의 범위 및 각도를 제한합니다.

    • 조명 인스턴스 수를 줄입니다.

    • 실내 환경에서는 특정 범위 외부 또는 방별로 조명을 비활성화하는 것도 고려하십시오.

MicroProfiler 범위

범위관련 계산
Prepare and Perform전체 렌더링
Perform/Scene/computeLightingPerform조명 그리드 및 그림자 업데이트
LightGridCPU복셀 조명 그리드 업데이트
ShadowMapSystem그림자 매핑
Perform/Scene/UpdateView렌더링 준비 및 입자 업데이트
Perform/Scene/RenderView렌더링 및 후처리

네트워킹 및 복제

네트워킹 및 복제는 서버와 연결된 클라이언트 간에 데이터가 전송되는 과정을 설명합니다. 정보는 매 프레임마다 클라이언트와 서버 간에 전송되지만, 더 많은 양의 정보는 더 많은 계산 시간을 요구합니다.

일반적인 문제

  • 과도한 원격 트래픽 - RemoteEvent 또는 RemoteFunction 객체를 통해 방대한 양의 데이터를 보내거나 매우 자주 호출하는 것은 각 프레임마다 수신 패킷 처리에 많은 CPU 시간을 소모하게 됩니다. 일반적인 실수는 다음과 같습니다:

    • 복제할 필요가 없는 데이터를 매 프레임마다 복제합니다.
    • 입력을 기반으로 데이터를 복제하되 이를 조절하는 메커니즘이 없습니다.
    • 필요 이상의 데이터를 전달합니다. 예를 들어, 물건을 구입할 때 플레이어의 전체 인벤토리가 아닌 구매한 물건의 세부 정보만 전송합니다.
  • 복잡한 인스턴스 트리 생성 또는 제거 - 서버에서 데이터 모델에 변경이 이루어지면, 이 변경 내용을 연결된 클라이언트에 복제합니다. 따라서 런타임에 맵과 같은 대규모 인스턴스 계층 구조를 생성하거나 파괴하는 것은 매우 네트워크 집중적일 수 있습니다.

    여기에 일반적인 원인은 애니메이션 편집기 플러그인으로 저장한 복잡한 애니메이션 데이터입니다. 게임이 게시되기 전에 이를 제거하지 않으면, 애니메이션 모델이 정기적으로 복제될 때 많은 양의 데이터가 불필요하게 복제됩니다.

  • 서버 측 TweenService - TweenService가 서버에서 개체를 트윈하는 데 사용되는 경우, 트윈 속성은 매 프레임마다 각 클라이언트에 복제됩니다. 이것은 클라이언트의 대기 시간이 변동함에 따라 트윈이 불안정하게 될 뿐만 아니라 불필요한 네트워크 트래픽을 유발합니다.

완화

불필요한 복제를 줄이기 위해 다음 전술을 사용할 수 있습니다:

  • 원격 이벤트를 통해 한 번에 많은 양의 데이터 전송 피하기. 대신 필요 데이터만 적은 빈도로 전송하십시오. 예를 들어, 캐릭터의 상태에 대해서는 변경될 때만 복제하십시오.
  • 맵과 같은 복잡한 인스턴스 트리 청크로 나누기 및 조각으로 로드하여 여러 프레임에 걸쳐 이들의 복제 작업을 분산하십시오.
  • 애니메이션 메타데이터 정리 - 특히 리그의 애니메이션 디렉토리를 가져온 후.
  • 불필요한 인스턴스 복제 제한 - 서버가 생성되는 인스턴스를 알 필요가 없는 경우에 특히 그렇습니다. 여기에는 다음이 포함됩니다:
    • 폭발 또는 마법 주문과 같은 시각적 효과. 서버는 결과를 결정하기 위해 위치만 알면 되며, 클라이언트는 시각적 효과를 로컬에서 생성할 수 있습니다.
    • 1인칭 아이템 뷰 모델.
    • 서버가 아닌 클라이언트에서 트윈 개체를 수행합니다.

MicroProfiler 범위

범위관련 계산
ProcessPackets이벤트 호출 및 속성 변경과 같은 수신 네트워크 패킷 처리
Allocate Bandwidth and Run Senders서버와 관련된 아웃고잉 이벤트

자산 메모리 사용

클라이언트 메모리 사용을 개선하기 위해 제작자가 사용할 수 있는 가장 영향력 있는 메커니즘은 인스턴스 스트리밍을 활성화하는 것입니다.

인스턴스 스트리밍

인스턴스 스트리밍은 필요하지 않은 데이터 모델의 일부를 선택적으로 로드 차단하여 로드 시간을 상당히 줄이고 클라이언트가 메모리 압박을 받을 때 충돌을 방지할 수 있는 능력을 높이는 것입니다.

메모리 문제가 발생하고 인스턴스 스트리밍이 비활성화된 경우, 게임을 그에 맞게 업데이트하는 것을 고려하십시오. 특히 3D 월드가 크다면 더욱 그렇습니다. 인스턴스 스트리밍은 3D 공간에서의 거리 기반이므로, 더 큰 월드는 자연스럽게 이점이 더 큽니다.

인스턴스 스트리밍이 활성화되면 더 공격적으로 이를 증가시키는 방법도 있습니다. 예를 들어 다음을 고려하십시오:

  • 가능한 경우 Enum.ModelStreamingMode.Persistent의 사용을 줄입니다. 이를 호환성 조치로 사용하는 경우 스크립트를 업데이트해야 할 수도 있습니다.
  • Workspace.StreamingMinRadiusWorkspace.StreamingTargetRadius를 줄입니다.

스트리밍 옵션 및 해당 이점에 대한 자세한 내용은 스트리밍 속성을 참조하십시오.

기타 일반적인 문제

  • 자산 중복 - 동일한 자산을 여러 번 업로드하여 발생하는 일반적인 실수입니다. 이로 인해 동일한 콘텐츠가 메모리에 여러 번 로드될 수 있습니다.

  • 과도한 자산 양 - 자산이 동일하지 않은 경우에도 동일한 자산을 재사용하고 메모리를 절약할 기회를 놓치는 경우가 있습니다.

  • 오디오 파일 - 오디오 파일은 특히 게임의 일부를 위한 필요 없었던 모든 것을 가져오지 않고 필요한 것만 클라이언트에 로드하는 경우 메모리 사용에 의외로 기여할 수 있습니다. 전략에 대해서는 로드 시간을 참조하십시오.

  • 고해상도 텍스처 - 텍스처의 그래픽 메모리 소비는 디스크 크기와는 관계가 없습니다. 텍스처의 픽셀 수에 따라 메모리 사용이 결정됩니다. 예를 들어, 1024x1024 픽셀 텍스처는 512x512 텍스처의 4배의 그래픽 메모리를 소비합니다.

    Roblox에 업로드된 이미지는 고정된 형식으로 변환되므로 픽셀당 더 적은 바이트와 관련된 색상 모델로 이미지를 업로드하는 것에 대한 메모리 이점이 없습니다. 마찬가지로, 업로드 전에 이미지를 압축하거나 필요없는 경우 알파 채널을 제거하면 디스크에서 이미지 크기를 줄일 수 있지만 메모리 사용량을 개선하지는 않습니다.

    게임이 로드될 때 엔진은 낮은 품질의 텍스처로 시작한 후, 장치 메모리의 가용성, 카메라와의 거리, 텍스처가 차지하는 화면 공간의 양 및 기타 요인에 따라 품질을 높입니다. 그럼에도 불구하고, 전략적으로 텍스처 크기를 조정하면 게임 내 메모리 사용량을 개선할 수 있습니다.

완화

  • 자산은 한 번만 업로드 - 객체 전체에서 동일한 자산 ID를 재사용하고 동일한 자산(특히 메쉬 및 이미지)이 반복해서 업로드되지 않도록 하십시오.

  • 중복 자산 찾고 수정 - 서로 다른 ID로 여러 번 업로드된 동일한 메쉬 파트 및 텍스처를 찾아보십시오.

    • 자산의 유사성을 자동으로 감지하는 API가 없기 때문에, 모든 이미지 자산 ID를 수집하여(수동으로 또는 스크립트로), 다운로드한 후 외부 비교 도구를 사용하여 비교할 수 있습니다.
    • 메쉬 파트의 경우, 고유한 메쉬 ID를 가져와 크기별로 정리하여 중복을 수동으로 식별하는 것이 최선의 전략입니다.
    • 서로 다른 색상에 대해 별도의 텍스처를 사용하는 대신, 단일 텍스처를 업로드하고 SurfaceAppearance.Color 속성을 사용하여 다양한 색조를 적용합니다.
  • 맵에서 개체를 개별적으로 가져옵니다 - 전체 맵을 한 번에 가져오는 대신, 맵의 자산을 개별적으로 가져오고 다시 구성합니다. 가져오기 도구는 메쉬의 중복 제거를 하지 않으므로, 바닥 타일과 같이 많은 개별 타일이 포함된 대규모 맵을 가져오면 이 타일이 각각 별도의 자산으로 업로드되어 메모리 및 드로우 호출 발생으로 이어질 수 있습니다.

  • 이미지의 픽셀 수를 필요한 양보다 적게 제한 - 이미지가 화면에서 차지하는 물리적 공간이 크지 않으면, 일반적으로 최대 512x512픽셀의 크기가 필요합니다. 대부분의 작은 이미지는 256x256픽셀보다 작아야 합니다.

  • 트림 시트를 사용하여 3D 맵에서 최대 텍스처 재사용 보장 - 트림 시트를 만들기 위한 단계 및 예제는 트림 시트 만들기를 참조하십시오.

    또한 스프라이트 시트를 사용하여 여러 개의 작은 UI 이미지를 한 이미지로 로드하는 것을 고려할 수 있습니다. 그러면 ImageLabel.ImageRectOffsetImageLabel.ImageRectSize를 사용하여 시트의 일부분을 표시할 수 있습니다.

로드 시간

많은 게임이 사용자 정의 로딩 화면을 구현하고 ContentProvider:PreloadAsync() 메서드를 사용하여 자산을 요청하여 이미지, 사운드 및 메쉬가 백그라운드에서 다운로드되도록 합니다.

이 접근법의 장점은 중요한 게임 부분이 완전히 로드되도록 하여 팝인이 발생하지 않도록 할 수 있다는 것입니다. 그러나 일반적인 실수는 실제로 필요한 것보다 너무 많은 자산을 미리 로드하기 위해 이 메서드를 과도하게 사용하는 것입니다.

잘못된 관행의 예는 전체 Workspace를 로드하는 것입니다. 이렇게 하면 텍스처 팝인 방지를 할 수 있지만, 로드 시간이 상당히 증가합니다.

유사한 관행은 ContentProvider.RequestQueueSize를 사용하여 모든 요청된 자산이 로딩 완료되도록 보장하는 것입니다. 그러나 이 또한 상당히 증가한 로드 시간을 초래할 수 있으며, 변동성이 큰 특성으로 인해 신뢰할 수 없는 방법입니다.

대신 필요한 경우에만 ContentProvider:PreloadAsync()를 사용하십시오. 이는 다음과 같은 상황을 포함합니다:

  • 로딩 화면의 이미지.
  • 버튼 배경 및 아이콘과 같은 게임 메뉴의 중요한 이미지.
  • 시작 또는 스폰 지역의 중요한 자산.

많은 자산을 로드해야 하는 경우, 로드 건너뛰기 버튼을 제공하는 것이 좋습니다.

©2026 Roblox Corporation. Roblox 및 Roblox 로고, 'Powering Imagination'은 미국 및 기타 국가 내 당사의 등록 및 미등록 상표입니다.