성능 향상

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

이 페이지에서는 일반적인 성능 문제와 이를 완화하기 위한 모범 사례를 설명합니다.

스크립트 계산

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

일반적인 문제

  • 테이블 구조에 대한 집중적인 작업 - 직렬화, 역직렬화 및 깊은 복제와 같은 복잡한 작업은 특히 큰 테이블 구조에서 높은 성능 비용을 초래합니다. 이러한 작업이 재귀적이거나 매우 큰 데이터 구조를 반복하는 경우에는 더욱 그렇습니다.
  • 고주파 이벤트 - RunService의 프레임 기반 이벤트에 비용이 많이 드는 작업을 연결하면 빈도를 제한하지 않는 한 이러한 작업이 매 프레임마다 반복되어 불필요하게 계산 시간이 증가할 수 있습니다. 이러한 이벤트에는 다음이 포함됩니다:

완화 방법

  • RunService 이벤트에서 코드를 드물게 호출하고, 고주파 호출이 필수적인 경우(예: 카메라 업데이트)로 사용을 제한합니다. 대부분의 다른 코드는 다른 이벤트에서 실행하거나 루프에서 덜 자주 실행할 수 있습니다.
  • task.wait()를 사용하여 큰 작업이나 비용이 많이 드는 작업을 나누어 여러 프레임에 걸쳐 작업을 분산시킵니다.
  • 불필요하게 비용이 많이 드는 작업을 식별하고 최적화하며, 데이터 모델에 접근할 필요가 없는 계산 집약적인 작업에는 멀티스레딩을 사용합니다.
  • 특정 서버 측 스크립트는 스크립트를 바이트코드가 아닌 기계 코드로 컴파일하는 간단한 플래그인 네이티브 코드 생성로 이점을 얻을 수 있습니다.
  • 최적화 후에도 서버의 CPU가 여전히 부족하다면, Compute를 위한 확장 서비스를 통해 더 많은 CPU를 예약할 수 있습니다.

마이크로 프로파일러 범위

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

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

스크립트 메모리 사용

메모리 누수는 가비지 수집기가 더 이상 사용되지 않는 메모리를 제대로 해제하지 못하는 스크립트를 작성할 때 발생할 수 있습니다. 누수는 서버에서 특히 만연할 수 있는데, 서버는 며칠 동안 계속 온라인 상태일 수 있지만 클라이언트 세션은 훨씬 짧기 때문입니다.

개발자 콘솔의 다음 메모리 값은 추가 조사가 필요한 문제를 나타낼 수 있습니다:

  • 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 뷰포트 오른쪽 상단의 시각화 옵션 위젯에서 충돌 충실도를 켭니다.

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

    • 정밀도와 성능 요구 사항의 균형을 맞추는 충돌 충실도 옵션을 선택하는 방법에 대한 심층적인 안내는 물리 및 렌더링 매개변수 설정을 참조하십시오.

마이크로 프로파일러 범위

범위관련 계산
physicsStepped전체 물리 계산
worldStep각 프레임에서 수행되는 이산 물리 스텝

물리 메모리 사용

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

일반적인 문제

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

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

완화 방법

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

  • 충돌이 필요 없는 부품의 경우 BasePart.CanCollide, BasePart.CanTouch 및 BasePart.CanQuery를 false로 설정하여 충돌을 비활성화합니다.
  • CollisionFidelity 설정을 사용하여 충돌의 충실도를 줄입니다. Box는 메모리 오버헤드가 가장 낮고, Default 및 Precise는 일반적으로 더 비쌉니다.
    • 일반적으로 작은 고정 부품의 충돌 충실도를 Box로 설정하는 것이 안전합니다.
    • 매우 복잡한 큰 메시의 경우, 작은 객체로 충돌 메시를 구축하는 것이 좋습니다.

휴머노이드

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

일반적인 문제

  • NPC에서 모든 HumanoidStateTypes를 활성화 상태로 두기 - 특정 HumanoidStateTypes를 활성화 상태로 두는 것은 성능 비용이 발생합니다. NPC에 필요하지 않은 상태는 비활성화하십시오. 예를 들어, NPC가 사다리를 오를 계획이 없다면 Climbing 상태를 비활성화하는 것이 안전합니다.
  • Humanoids 또는 스킨된 MeshParts 모델을 자주 인스턴스화, 수정 및 재생성하기 - 이는 엔진이 처리하는 데 비용이 많이 들 수 있으며, 특히 이러한 모델이 레이어드 의류를 사용하는 경우 더욱 그렇습니다. 아바타가 자주 재생성되는 게임에서는 특히 문제가 될 수 있습니다.
    • 마이크로 프로파일러에서 긴 updateInvalidatedFastClusters 태그(4ms 이상)는 아바타 인스턴스화/수정이 과도한 무효화를 유발하고 있음을 나타내는 신호입니다.
  • 필요하지 않은 경우에 Humanoids 사용하기 - 움직이지 않는 정적 NPC는 일반적으로 Humanoid 클래스가 필요하지 않습니다.
  • 서버에서 많은 수의 NPC 애니메이션 재생하기 - 서버에서 실행되는 NPC 애니메이션은 서버에서 시뮬레이션되어 클라이언트에 복제되어야 합니다. 이는 불필요한 오버헤드를 초래할 수 있습니다.
  • 불필요한 크기 및 비율 변경 수행하기 - 크기/비율 변경은 FastCluster를 재구성하게 만듭니다. FastCluster 관련 성능 문제가 발생하는 경우 게임 플레이 중 이를 줄이도록 노력하십시오. 마찬가지로, 다른 속성 변경도 FastCluster를 재구성하게 만들 수 있으므로 일반적으로 이러한 변경을 가능한 한 줄이십시오.

완화 방법

  • NPC 애니메이션을 클라이언트에서 재생하기 - 많은 수의 NPC가 있는 게임에서는 클라이언트에서 Animator를 생성하고 애니메이션을 로컬에서 실행하는 것을 고려하십시오. 이는 서버의 부하를 줄이고 불필요한 복제의 필요성을 줄입니다. 또한 NPC가 캐릭터에 가까운 경우에만 애니메이션을 재생하는 등의 추가 최적화를 가능하게 합니다.
  • 휴머노이드에 대한 성능 친화적인 대안 사용하기 - NPC 모델은 반드시 휴머노이드 객체를 포함할 필요는 없습니다.
    • 정적 NPC의 경우, 단순한 AnimationController를 사용하십시오. 이들은 이동할 필요가 없지만 애니메이션을 재생해야 합니다.
    • 이동하는 NPC의 경우, NPC의 복잡성에 따라 자체 이동 컨트롤러를 구현하고 애니메이션에 AnimationController를 사용하는 것을 고려하십시오.
  • 사용하지 않는 휴머노이드 상태 비활성화 - Humanoid:SetStateEnabled()를 사용하여 각 휴머노이드에 필요한 상태만 활성화합니다.
  • 자주 재생성되는 NPC 모델 풀링 - NPC를 완전히 파괴하는 대신 비활성 NPC 풀로 NPC를 보내십시오. 이렇게 하면 새로운 NPC가 재생성될 때 풀에서 NPC 중 하나를 간단히 재활성화할 수 있습니다. 이 프로세스를 풀링이라고 하며, 캐릭터를 인스턴스화해야 하는 횟수를 최소화합니다.
  • 사용자가 가까이 있을 때만 NPC 생성하기 - 사용자가 범위에 없을 때 NPC를 생성하지 말고, 사용자가 범위를 벗어날 때 NPC를 제거하십시오.
  • 인스턴스화된 후 아바타 계층 구조에 대한 변경 피하기 - 아바타 계층 구조에 대한 특정 수정은 상당한 성능 영향을 미칩니다. 일부 최적화가 가능합니다:
    • 사용자 정의 절차적 애니메이션의 경우, JointInstance.C0 및 JointInstance.C1 속성을 업데이트하지 마십시오. 대신 Motor6D.Transform 속성을 업데이트하십시오.
    • 아바타에 BasePart 객체를 연결해야 하는 경우, 아바타 Model의 계층 구조 외부에서 수행하십시오.

마이크로 프로파일러 범위

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

렌더링

클라이언트가 각 프레임에서 소비하는 시간의 상당 부분은 현재 프레임에서 장면을 렌더링하는 데 사용됩니다. 서버는 렌더링을 수행하지 않으므로 이 섹션은 클라이언트에만 해당됩니다.

드로우 호출

드로우 호출은 엔진이 GPU에 무언가를 렌더링하라는 지시 세트입니다. 드로우 호출은 상당한 오버헤드를 가지고 있습니다. 일반적으로 프레임당 드로우 호출이 적을수록 프레임 렌더링에 소요되는 계산 시간이 줄어듭니다.

현재 발생하고 있는 드로우 호출 수는 스튜디오의 렌더 통계 ⟩ 타이밍 항목에서 확인할 수 있습니다. 클라이언트에서 렌더 통계를 보려면 ShiftF2를 누르십시오.

주어진 프레임에서 장면에 그려야 할 객체가 많을수록 GPU에 대한 드로우 호출이 더 많이 발생합니다. 그러나 Roblox 엔진은 _인스턴싱_이라는 프로세스를 사용하여 동일한 텍스처 특성을 가진 동일한 메시를 단일 드로우 호출로 통합합니다. 구체적으로, 동일한 MeshContent를 가진 여러 메시가 다음과 같은 경우 단일 드로우 호출로 처리됩니다:

기타 일반적인 문제

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

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

  • 인스턴싱 기회를 놓침 - 종종 장면에는 동일한 메시가 여러 번 복제되지만 각 복제본의 메시 또는 텍스처 자산 ID가 다릅니다. 이는 인스턴싱을 방지하고 불필요한 드로우 호출을 초래할 수 있습니다.

    이 문제의 일반적인 원인은 전체 장면이 한 번에 가져와지는 경우입니다. 개별 자산이 Roblox에 가져와진 다음 가져온 후 복제되어 장면을 조립하는 것이 아니라.

    다음과 같은 간단한 스크립트는 서로 다른 메시 ID를 사용하는 동일한 이름의 메시 파트를 식별하는 데 도움이 될 수 있습니다:

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

    출력( 스택 라인이 활성화된 경우)은 다음과 같을 수 있습니다. 반복된 라인은 동일한 메시의 재사용을 나타내며, 이는 좋습니다. 고유한 라인은 반드시 나쁜 것은 아니지만, 이름 지정 방식에 따라 게임에서 중복 메시를 나타낼 수 있습니다:

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

  • 과도한 그림자 캐스팅 - 그림자를 처리하는 것은 비용이 많이 드는 프로세스이며, 그림자를 캐스팅하는 조명 객체의 수와 밀도가 높거나 그림자에 영향을 받는 작은 부품의 수와 밀도가 높으면 성능 문제가 발생할 수 있습니다.

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

  • 불필요한 스킨 메시 파트 이동 - Humanoid가 없는 모델의 일부인 스킨 메시 파트는 공간적으로 구성된 FastClusters를 사용하여 그룹화됩니다. 이러한 메시 파트가 이동하면 지속적으로 이러한 공간 클러스터에 추가되고 제거되어 클러스터가 재구성되어 성능에 영향을 미칩니다.

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

완화 방법

  • 동일한 메시 인스턴싱 및 고유 메시 수 줄이기 - 모든 동일한 메시가 동일한 기본 자산 ID를 가지도록 하면 엔진이 이를 인식하고 단일 드로우 호출로 렌더링할 수 있습니다. 각 메시를 한 번만 업로드하고 스튜디오에서 재사용을 위해 복제하도록 하십시오. 대규모 맵을 전체적으로 가져오는 것은 동일한 메시가 별도의 콘텐츠 ID를 가지게 되어 엔진에 의해 고유 자산으로 인식될 수 있습니다. 패키지는 객체 재사용을 위한 유용한 메커니즘입니다.
  • 컬링 - 컬링은 최종 렌더링된 프레임에 영향을 미치지 않는 객체에 대한 드로우 호출을 제거하는 프로세스를 설명합니다. 기본적으로 엔진은 카메라의 시야(프러스텀 컬링) 밖에 있는 객체에 대한 드로우 호출을 건너뛰고, 다른 객체에 의해 가려진 부품, 메시 및 지형(폐색 컬링)에 대한 드로우 호출을 건너뜁니다. 실내 환경과 같은 특정 시나리오에서는 방 또는 포털 시스템을 구현하고 객체를 수동으로 컬링하여 드로우 호출 또는 전체 계산 부하를 추가로 줄일 수 있습니다.
  • 모델의 세부 수준 줄이기 - 인스턴스 스트리밍을 활성화하고 세계 모델의 LevelOfDetail 속성을 SLIM으로 설정하여 카메라에서 멀어질수록 최적화된 경량 SLIM 메시를 렌더링합니다.
  • 아바타의 세부 수준 줄이기 - 인스턴스 스트리밍을 활성화하고 Workspace.EnableSLIMAvatars를 설정하여 카메라에서 멀어질수록 플랫폼 아바타를 최적화된 경량 SLIM 표현으로 렌더링합니다.
  • 렌더링 충실도 줄이기 - MeshPart.RenderFidelity를 Automatic 또는 Performance로 설정합니다. 이렇게 하면 메시가 덜 복잡한 대안으로 대체될 수 있어 그려야 할 다각형 수를 줄일 수 있습니다.
  • 적절한 부품 및 조명 객체에서 그림자 캐스팅 비활성화 - Roblox 엔진은 클라이언트 그래픽 품질 수준이 낮아짐에 따라 그림자 품질을 자동으로 저하시키며, 품질 수준이 4 이하일 때 그림자를 완전히 비활성화합니다. 그러나 조명 객체와 부품에서 그림자 캐스팅 속성을 선택적으로 비활성화하여 그림자가 활성화된 상태에서 성능을 개선하고 그림자가 활성화된 상태를 유지할 가능성을 높일 수 있습니다. 편집 시간 또는 런타임 중에 수행할 수 있는 최적화의 몇 가지 예는 다음과 같습니다:
    • BasePart.CastShadow 속성을 사용하여 그림자가 보일 가능성이 낮은 작은 부품에서 그림자 캐스팅을 비활성화합니다. 이 전략은 사용자의 카메라에서 멀리 떨어진 부품에 적용할 때 특히 효과적입니다.

    • 가능할 경우 이동하는 객체에서 그림자를 비활성화합니다.

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

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

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

    • 실내 환경의 경우 특정 범위 외부의 조명을 비활성화하거나 방별로 비활성화하는 것을 고려합니다.

마이크로 프로파일러 범위

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

네트워킹 및 복제

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

일반적인 문제

  • 과도한 원격 트래픽 - RemoteEvent 또는 RemoteFunction 객체를 통해 많은 양의 데이터를 전송하거나 매우 자주 호출하면 매 프레임마다 수신 패킷을 처리하는 데 많은 CPU 시간이 소요될 수 있습니다. 일반적인 실수에는 다음이 포함됩니다:

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

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

  • 서버 측 TweenService - TweenService를 사용하여 서버 측에서 객체를 트윈하면 트윈된 속성이 매 프레임마다 각 클라이언트에 복제됩니다. 이는 클라이언트의 지연 시간이 변동함에 따라 트윈이 흔들리게 만들 뿐만 아니라 불필요한 네트워크 트래픽을 초래합니다.

완화 방법

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

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

마이크로 프로파일러 범위

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

자산 메모리 사용

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

인스턴스 스트리밍

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

메모리 문제에 직면하고 인스턴스 스트리밍이 비활성화된 경우, 특히 3D 세계가 큰 경우 게임을 업데이트하여 지원하는 것을 고려하십시오. 인스턴스 스트리밍은 3D 공간에서의 거리 기반이므로 더 큰 세계가 자연스럽게 더 많은 이점을 얻습니다.

인스턴스 스트리밍이 활성화된 경우, 그 공격성을 높일 수 있습니다. 예를 들어, 다음을 고려하십시오:

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

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

기타 일반적인 문제

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

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

  • 오디오 파일 - 오디오 파일은 메모리 사용량에 놀라운 기여를 할 수 있으며, 특히 게임의 일부에 필요한 것만 로드하는 대신 모든 파일을 클라이언트에 한 번에 로드하는 경우 더욱 그렇습니다. 전략에 대한 내용은 로드 시간을 참조하십시오.

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

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

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

완화 방법

  • 자산을 한 번만 업로드 - 객체 간에 동일한 자산 ID를 재사용하고 동일한 자산, 특히 메시 및 이미지를 여러 번 별도로 업로드하지 않도록 합니다.

  • 중복 자산 찾기 및 수정 - 서로 다른 ID로 여러 번 업로드된 동일한 메시 파트 및 텍스처를 찾습니다.

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

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

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

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

로드 시간

플레이어가 게임을 시작하기 전에 클라이언트는 ReplicatedFirst 및 ReplicatedStorage의 모든 내용, Workspace의 초기 내용 및 스크립트가 미리 로드하는 모든 자산을 수신해야 합니다. 클라이언트가 이 시간 동안 다운로드하고 처리해야 할 콘텐츠가 많을수록 플레이어가 기다리는 시간이 길어집니다. 조인 프로세스의 각 단계에 대한 세부 사항은 성능 문제 식별을 참조하십시오.

일반적인 문제

  • 인스턴스 스트리밍 비활성화 - 인스턴스 스트리밍이 비활성화된 경우, 플레이어가 들어오기 전에 전체 Workspace가 클라이언트에 복제되어야 하므로 부품, 모델 및 자산이 많은 게임은 비례적으로 더 긴 로드 시간을 가집니다.

  • ReplicatedStorage의 과도한 콘텐츠 - ReplicatedStorage의 모든 내용은 플레이어가 아무것도 보거나 상호작용하기 전에 모든 클라이언트에 복제되므로 클라이언트가 즉시 필요하지 않더라도 복제됩니다. 무료 모델 및 상속된 자산은 조용히 상당한 양을 추가할 수 있습니다.

  • 무거운 ReplicatedFirst 콘텐츠 - ReplicatedFirst는 다른 모든 것보다 먼저 복제되며 주로 사용자 정의 로딩 화면을 보유하는 데 존재합니다. 그 안에 추가 자산이나 스크립트가 많을수록 주요 복제 단계의 시작이 지연됩니다.

  • PreloadAsync()의 과도한 사용 - 많은 게임이 사용자 정의 로딩 화면을 구현하고 ContentProvider:PreloadAsync()를 사용하여 자산을 요청하여 이미지, 사운드 및 메시가 백그라운드에서 다운로드되도록 합니다. 이 접근 방식은 게임의 중요한 부분이 팝인 없이 완전히 로드되도록 보장하지만, 일반적인 실수는 실제로 필요한 것보다 더 많은 자산을 미리 로드하는 것입니다.

    나쁜 관행의 예는 전체 Workspace를 로드하는 것입니다. 이는 텍스처 팝인을 방지할 수 있지만 로드 시간을 크게 증가시킵니다.

    또 다른 유사한 관행은 ContentProvider.RequestQueueSize를 사용하여 요청된 모든 자산이 로드 완료되었는지 확인하는 것입니다. 그러나 이는 로드 시간을 크게 증가시키는 동일한 문제를 나타내며, 변동성이 큰 특성으로 인해 신뢰할 수 없는 방법입니다.

  • 비용이 많이 드는 시작 스크립트 - 조인 프로세스 중에 실행되는 LocalScripts에서의 무거운 스크립트 실행은 대규모 데이터 구조를 동기적으로 로드하는 것과 같은 방식으로 복제에서 플레이 가능한 상태로의 전환을 지연시킵니다.

완화 방법

  • 인스턴스 스트리밍 활성화 - 스트리밍은 플레이어의 스폰 지점 근처에 있는 세계의 일부만 초기 조인 시 전송한 다음, 플레이어가 게임을 진행하면서 나머지를 점진적으로 스트리밍합니다. 이미 사용하지 않는 게임의 경우, 스트리밍은 로드 시간을 줄이는 가장 효과적인 방법 중 하나입니다. 새로운 장소에 대해 기본적으로 스트리밍이 활성화됩니다. 설정 단계, 속성 참조 및 마이그레이션 안내는 인스턴스 스트리밍을 참조하십시오.

  • ReplicatedStorage를 간소화 - 클라이언트가 조인할 때 필요한 것만 저장합니다. 예를 들어, LocalScripts에서 필요한 공유 ModuleScripts를 저장합니다. 즉시 필요하지 않은 대형 모델, 오디오 및 기타 자산은 ServerStorage로 이동하고, 플레이어가 필요할 때 클라이언트로 이동합니다. 더 이상 사용하지 않는 콘텐츠에 대해 ReplicatedStorage를 정기적으로 감사합니다.

  • ReplicatedFirst를 간소화 - ReplicatedFirst를 로딩 화면에 필요한 것만으로 제한합니다. 예를 들어, 대형 텍스처나 3D 모델이 아닌 기본 UI 요소로 제한하고, 그 안에서 실행되는 스크립트에서 무거운 계산을 피합니다. 사용자 정의 로딩 화면을 구축하는 방법에 대한 안내는 로딩 화면에서 확인할 수 있습니다.

  • 필요한 자산만 미리 로드 - ContentProvider:PreloadAsync()는 필요한 상황에서만 사용하십시오. 여기에는 다음이 포함됩니다:

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

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

  • 비필수 시작 작업 연기 - 플레이어가 아직 도달하지 않은 콘텐츠(예: 열지 않은 UI 패널)는 처음 필요할 때만 로드합니다. 초기화 작업이 조기에 발생해야 하는 경우, 한 번에 모두 실행하는 대신 여러 프레임에 걸쳐 분산시킵니다. 기술에 대한 내용은 스크립트 계산을 참조하십시오.

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