클라이언트의 TimeScale 조정하기

[UtilLogic](/apiReference/Logics/UtilLogic)의 `SetClientTimeScale()`를 사용해 클라이언트의 애니메이션 재생 속도, 이동 속도, 물리의 속도를 제어할 수 있습니다. 이 메소드를 활용해 카메라 효과를 조절하거나, 애니메이션 재생 속도를 제어해 더욱 몰입감 있는 월드를 제작할 수 있습니다.

##### 참고 가이드

  * [UtilLogic](/apiReference/Logics/UtilLogic)



## SetClientTimeScale

`SetClientTimeScale()`를 사용해 클라이언트에서 `Update(deltaTime)`로 전달되는 값을 변경하여, 월드 전체의 시간을 조절할 수 있습니다. 기본값은 1이며, 0 ~ 100 사이의 값을 사용할 수 있습니다. 음수값은 사용할 수 없습니다. `SetClientTimeScale()`로 deltaTime 값이 변경되어도 Update의 호출 빈도는 그대로 유지되는 것이 특징입니다. 예를 들어 초당 60프레임일 때 TimeScale 값을 3으로 설정한다면, 프레임이 초당 180으로 늘어나는 것이 아니라 각 프레임에서 전달되는 `deltaTime` 값이 3배로 증가하게 됩니다. 따라서 서버는 변경된 값에 영향을 받지 않고, 클라이언트의 엔티티만 영향을 받아 초가 3배 빨리 증가합니다.

[gif](1774245408714fb25081cef3b4c9eac5b7ef7c52a4194)

`SetClientTimeScale()` 메소드는 월드 전체의 시간에 영향을 끼치므로 시간, 속도와 관련된 프로퍼티, 함수가 있는 API는 영향을 받게 됩니다. 예를 들어 아래와 같이 작성할 경우 `OnBeginPlay()`가 서버와 클라이언트에서 모두 실행되므로 `Wait()`이 양쪽에서 모두 동작하고, `SetClientTimeScale()`은 클라이언트에서만 동작하게 됩니다.
    
    
    @Component
    script TimeScaleSetter extends Component
        method void OnBeginPlay()
        log("Current Time")
        wait(10)
        _UtilLogic:SetClientTimeScale(2)
        log("Current Time")
        end
    end
    

서버에서 발생하는 로그 값은 정확히 10초 간격으로 발생해 20:24:33 처음 로그가 출력된 후 다음번 로그는 20:24:43에 출력됩니다. 그러나 클라이언트의 TimeScale이 2배로 적용되었기 때문에 클라이언트에서의 로그 값은 20:24:33 처음 출력된 후 다음번 로그는 20:24:38에 출력됩니다.

[png](17740064823740459c88eb7b44ffda3289c1801959301)

### 영향을 받는 API

`UtilLogic:SetClientTimeScale()`에 영향을 받는 목록입니다.

[{"이름":"AIComponent","설명":"OnBehave()의 Delta이 TimeScale 값을 곱한 값만큼 변합니다. AIComponent 컴포넌트의 UpdateAuthority가 Client일 때 영향을 받습니다."},{"이름":"BackgroundComponent","설명":"타입이 Template인 경우 배경의 애니메이션이 영향을 받습니다."},{"이름":"CameraComponent","설명":"ZoomTo()가 영향을 받습니다."},{"이름":"ChatBalloonComponent","설명":"시간과 관련된 프로퍼티만 영향을 받습니다."},{"이름":"DamageSkinComponent","설명":"모든 프로퍼티, 메소드가 영향을 받습니다."},{"이름":"PlayerControllerComponent","설명":"이동과 관련된 모든 메소드가 영향을 받습니다."},{"이름":"SpriteRendererComponent","설명":"SpriteRUID가 AnimationClip인 경우 영향을 받습니다."},{"이름":"TimerService","설명":"클라이언트 환경의 스크립트에서 실행된 경우 영향 받습니다."},{"이름":"TweenLogic","설명":"클라이언트 환경의 스크립트에서 실행된 경우 영향 받습니다."},{"이름":"머티리얼","설명":"시간과 관련된 TimeScale 프로퍼티가 있는 머티리얼인 경우 영향을 받습니다. 예를 들어 머티리얼의 TimeScale 값이 2, SetClientTimeScale 값이 2인 경우 4배로 적용됩니다."},{"이름":"물리","설명":"PhysicsRigidbodyComponent, PhysicsColliderComponent, RevoluteJointComponent, PrismaticJointComponent, PulleyJointComponent, WeldJointComponent, WheelJointComponent가 영향을 받습니다. 모두 Client Only일 때 영향을 받습니다."},{"이름":"이동","설명":"RigidbodyComponent, KinematicBodyComponent, SideViewBodyComponent가 영향을 받습니다."},{"이름":"파티클","설명":"AreaParticleComponent, BasicParticleComponent, BaseParticleComponent, SpriteParticleComponent가 영향을 받습니다."}]

### 영향을 받지 않는 API

`UtilLogic:SetClientTimeScale()`에 영향 받지 않는 목록입니다.

[{"이름":"ScreenMessageLogic","설명":"알림 메시지를 띄우는 것이 TimeScale에 영향을 받지 않습니다."},{"이름":"ScreenTransitionService","설명":"화면 전환 효과의 지속 시간이 TimeScale에 영향을 받지 않습니다."},{"이름":"SoundComponent","설명":"효과음 재생 속도가 TimeScale에 영향을 받지 않습니다. Pitch 프로퍼티를 활용해 속도를 조절해야 합니다."},{"이름":"UtilLogic","설명":"ElapsedSeconds는 TimeScale에 영향을 받지 않고, 일정하게 시간이 흐릅니다."},{"이름":"WebViewComponent","설명":"웹뷰의 화면이 TimeScale에 영향을 받지 않습니다."},{"이름":"YoutubePlayerWorldComponent, YoutubePlayerGUI","설명":"영상 재생 속도가 TimeScale에 영향을 받지 않습니다. PlaySpeed를 활용해 속도를 조절해야 합니다."}]

## 클라이언트들의 TimeScale 값이 다를 경우

한 월드에 접속한 여러 클라이언트가 서로 다른 TimeScale 값을 가질 때 클라이언트는 각각의 월드 속도를 따릅니다. 예를 들어 플레이어A는 TimeScale 값이 빨라지는 영역에 있고, 플레이어B는 TimeScale 값이 느린 영역에 있는 상황이라 생각해봅시다. 플레이어A의 TimeScale이 빠름에도 플레이어B가 뛰는 모습이 매우 느리게 보입니다. 플레이어B의 위치 정보는 클라이언트B의 TimeScale에 따라 변화 속도가 결정되기 때문입니다. 이 때문에 클라이언트B의 속도가 느리다면, 클라이언트A에서도 플레이어B의 이동 및 위치 변화가 매우 느리게 반영됩니다. 다만 점프, 공격, AnimationClip 등의 재생 속도는 각 클라이언트가 자체적으로 처리하기 때문에, 클라이언트A에서 플레이어B의 애니메이션은 정상 속도로 보이지만 실제 위치 변화는 느리게 진행되는 모습이 나타납니다.

[gif](17742417136167a4ceb01859b4b059c1936b1fe7218f8)

### 예제

주황색 광선 영역에 닿으면 TimeScale 값이 증가하고, 초록색 광선 영역에서는 TimeScale 값이 감소하게 만들어 봅시다. TimeScale 값 변화에 따라 월드 전체의 시간의 흐름이 달라지는 것을 확인할 수 있습니다. TimeScale의 변화를 확인하기 위해 맵에 엔티티를 추가하고, 머티리얼을 지정해봅시다. 머티리얼 지정 방법은 [머티리얼 응용하기](/docs?postId=1323)를 참고하세요.

[gif](17740068053871043ed1e8999496aaf514e14390eff88)

  1. 맵에 새로운 SpeedUpLight 엔티티를 생성하고, TriggerComponent를 추가합니다.
  2. TriggerComponent - Collider의 [Edit] 버튼을 눌러 설정한 스프라이트와 비슷한 크기의 트리거 영역을 생성합니다.
  3. Workspace - MyDesk에 새로운 CurrentTime 로직을 생성하고, 아래와 같이 작성합니다. 
         
         @Component
         script ActiveTimeScale extends Component
         property number NowTimeScale = 1
         end
         



    
    
    4. **Workspace - MyDesk**에 새로운 **ChangeSpeed** 컴포넌트를 생성합니다.
    5.  **SpeedUpLight** 엔티티에 **ChangeSpeed** 컴포넌트를 추가합니다.
    6.  **ChangeSpeed** 컴포넌트에 아래와 같이 작성합니다.
    
        ```lua
    @Component
    script PlayerTimeScaleAdjuster extends Component
        @Sync
        property number ChangeRate = 0
    
        @ExecSpace("ClientOnly")
        method void ChangeScale()
        _CurrentTime.NowTimeScale = _CurrentTime.NowTimeScale + self.ChangeRate
        _CurrentTime.NowTimeScale = math.clamp(_CurrentTime.NowTimeScale,0.36, 5)
        _UtilLogic:SetClientTimeScale(_CurrentTime.NowTimeScale)
        end
    
        @EventSender("Self")
        handler HandleTriggerStayEvent(TriggerStayEvent event)
        --------------- Native Event Sender Info ----------------
        -- Sender: TriggerComponent
        -- Space: Server, Client
        ---------------------------------------------------------
    
        -- Parameters
        -- local TriggerBodyEntity = event.TriggerBodyEntity
        ---------------------------------------------------------
    
        if self:IsClient()  then
            if  _UserService.LocalPlayer.Name == event.TriggerBodyEntity.Name then
                self:ChangeScale()
            end
        end
        end
    end
    

  7. 프로퍼티 창에서 ChangeRate에 0.05 값을 입력합니다.
  8. SpeedUpLight 엔티티를 복제하고, SpeedDownLight로 이름을 변경합니다.
  9. SpeedDownLight 엔티티의 ChangeRate 값을 -0.03으로 변경합니다.



## 유의 사항

클라이언트가 임의로 시간 배율을 조작하여 이동 속도를 변경해 월드에 악영향을 줄 수 있습니다. 그러므로 시간 배율이 월드의 주요 기능이나 금전 영역에 영향을 미치지 않도록 제작해야 합니다. 또한, 비정상적인 입력을 감지하기 위한 검증 로직을 추가하는 것이 좋습니다.