개발블로그
[유니티 C#] 유니티의 시간 마법사 Time.timeScale, 그리고 Time.unscaledTime 본문
💪
게임에서 최종 보스를 물리칠 때 슬로우 모션으로 보여지는 효과를 많이 보았다.
혹은 왼쪽 하단에 게임의 진행 속도를 조절할 수 있는 배속 버튼도 많이 사용한다.
유니티에서 시간을 빠르게, 혹은 느리게 움직이는 방법을 알아보고, TimeScale에 의존 적인 unscaledTime 도 알아보도록 하자.
🔶 Time.timeScale
시간이 경과하는 크기를 나타낸다.
timeScale이 1.0인 경우에 실제 시간과 같은 속도로 경과한다.
timeScale이 만약 0.5라면 실제 시간과 비교해서 2배 느리게 경과한다.
timeScale이 0으로 설정되는 경우에는 일반적으로 프레임 비율(framerate)과 독립적으로, 모든 기능을 일시 정지한다.
예를 들어 타이머가 가고있는 인게임에서 일시 정지 버튼을 누르면 게임의 진행상태가 멈춘다.
일시 정지 버튼을 눌렀을 때 timeScale을 0으로 맞춰주고, 일시정지를 나갔을 때 1로 바꿔준다면 타이머가 가지않도록 시간을 멈출 수 있다.
예제 코드를 보자.
using UnityEngine;
public class StudyTime_timeScale : MonoBehaviour
{
[SerializeField] GameObject _gbjSquare = null;
[SerializeField] GameObject _gbjPause = null;
float _curPos = 0f; // 현재 위치 x 값
float _minPos = -4.5f; // 시작 위치
float _maxPos = 4.5f; // 도착 위치
float _dir = 5f; // 이동 방향, 스피드
void Start()
{
_curPos = _gbjSquare.transform.position.x;
}
void Update()
{
Moving();
}
void Moving()
{
_curPos += _dir * Time.deltaTime;
if (_curPos >= _maxPos)
{
_dir *= -1;
_curPos = _maxPos;
}
else if(_curPos <= _minPos)
{
_dir *= -1;
_curPos = _minPos;
}
_gbjSquare.transform.position = new Vector2(_curPos, transform.position.y);
}
public void OnBtnPause()
{
_gbjPause.SetActive(true);
Time.timeScale = 0f;
}
public void OnBtnReplay()
{
_gbjPause.SetActive(false);
Time.timeScale = 1f;
}
}
GIF 1
보라색 사각형 게임오브젝트가 양쪽으로 왔다 갔다 이동하고 PAUSE 버튼을 누르면 timeScale 이 0이 되어 동작을 멈추고,
REPLAY 버튼을 눌러서 timeScale이 1이 되면 다시 동작을 수행한다.
여기서 2배속, 0.5배속, 원래 속도 버튼을 추가해보자.
public void OnBtn2XSpeed()
{
Time.timeScale = 2f;
}
public void OnBtn05XSpeed()
{
Time.timeScale = 0.5f;
}
public void OnBtnOriginalSpeed()
{
Time.timeScale = 1f;
}
함수를 세개 추가해서 각각의 버튼에 넣어주었다.
실행 결과는 위와 같다. GIF 2
버튼을 눌렀을 때 각각의 TimeScale을 지정해주어 오브젝트의 움직임 배속을 설정해주었다.
방치형 게임에서 많이 사용하는 방식인데, 이를 통해 게임의 진행 배속을 결정할 수 있게 할 수 있다.
✔ 버튼을 누를 때 마다 진행 배속 속도를 다르게 해보자
using UnityEngine.UI;
[SerializeField] Text _textSpeed = null;
public void OnBtnChangeSpeed()
{
float speed = Time.timeScale;
switch(speed)
{
case 1.0f : {
_textSpeed.text = "1.5x";
Time.timeScale = 1.5f;
break;
}
case 1.5f: {
_textSpeed.text = "2.0x";
Time.timeScale = 2f;
break;
}
case 2.0f: {
_textSpeed.text = "1.0x";
Time.timeScale = 1f;
break;
}
}
}
버튼을 누르면 1.0x -> 1.5x -> 2.0x 의 속도로 변화하도록 함수를 추가했다.
2D 오브젝트의 움직임 코드는 동일하기 때문에 생략했다.
+) 2021.09.03 내용 추가
float은 값이 정확하지 않으므로 case 문에서 조건 값으로 넣어주기가 상당히 애매하다.그러므로 float 을 바로 case문에서 사용하기보다 enum 문으로 받아서 사용해주는 것이 더 나은 방법이라고 할 수 있다.
수정한 코드는 아래와 같다.
// 코드가 같은 부분은 생략.
enum Speed
{
Original,
Fast,
MaxSpeed
}
public class StudyTime_timeScale : MonoBehaviour
{
Speed _ESpeed = Speed.Original;
public void OnBtnChangeSpeed()
{
switch(_ESpeed)
{
case Speed.Original: {
_textSpeed.text = "1.5x";
_ESpeed = Speed.Fast;
Time.timeScale = 1.5f;
break;
}
case Speed.Fast: {
_textSpeed.text = "2.0x";
_ESpeed = Speed.MaxSpeed;
Time.timeScale = 2f;
break;
}
case Speed.MaxSpeed: {
_textSpeed.text = "1.0x";
_ESpeed = Speed.Original;
Time.timeScale = 1f;
break;
}
}
}
}
case문에 float값을 바로 넣어주지 않고 enum 을 넣어서 수정했다.
이렇게 하면 float값의 변화로 인한 값 오차로 버그가 날 수 있는 코드를 수정한 것이다.
실제로 오류가 나지는 않았지만 float값은 오차가 생길 수 있기 때문에 이런식으로 사용하는 것이 좋다.
버튼을 누르면 진행 속도를 변경할 수 있다.
이 방법은 자동 진행되는 방치형 게임에서 게임의 진행 속도를 변경할 때 많이 사용하는 방법이다.
🔶 Time.unscaledTime
프레임이 시작된이후의 시간. 즉, 게임이 시작된 시간으로부터 경과된 시간을 나타내며 TimeScale의 영향을 받지 않는다.
유니티 도큐먼트 한글 버전에는 TimeScale에 의존적이다 라고 나와있는데 번역에 문제가 있는 것 같다.
영문 도큐먼트는 타임스케일에 영향을 받지 않는다고 쓰여있다.
그러므로 게임의 TimeScale이 0(Pause 상태) 일 때에도 unscaledTime 은 그 영향을 받지않는다.
확인을 위해서 화면에 Time.Time(Cur Time)과 Time.unscaledTime(Cur RealTime)을 출력하고,
timeScale이 0.5, 0, 2 일때의 unscaledTime 을 비교해보는 코드를 작성했다.
using UnityEngine;
using UnityEngine.UI;
public class StudyTime : MonoBehaviour
{
[SerializeField] Text _textCurTime;
[SerializeField] Text _textCurRealTime;
void Update()
{
CountTime();
}
void CountTime()
{
_textCurTime.text = string.Format("Cur Time : {0:N2}", Time.time);
_textCurRealTime.text = string.Format("Cur RealTime : {0:N2}", Time.unscaledTime);
}
public void OnBtnPause()
{
// toggle
if(Time.timeScale <= 0f)
{
Time.timeScale = 1f;
}
else
Time.timeScale = 0f;
}
public void OnBtnSpeedSlow()
{
Time.timeScale = 0.5f;
}
public void OnBtnSpeedFast()
{
Time.timeScale = 2f;
}
}
PAUSE 버튼을 Toggle로 사용하기 위해 타임스케일이 0이면 버튼을 눌렀을 때 1로,
1이면 버튼을 눌렀을 때 0으로 변경되도록 한 후 실행했다.
실행 화면을 확인해보면 Cur Time(Time.time)은 Pause 상태일 때 멈추고,
Cur RealTime(Time.unscaledTime)은 TimeScale에 영향을 받지 않으므로 계속해서 카운트되는 것을 확인할 수 있다.
2배속 버튼과 0.5배속 버튼도 마찬가지로,
버튼을 누르면 Cur Time(Time.time)은 배속에 영향을 받는 것을 확인할 수 있고,
반면 Cur RealTime(Time.unscaledTime)은 배속에 영향을 받지 않아서 속도에 변화가 없다.
이를 통해서 unscaledTime을 활용해서 모바일게임에서 게임이 멈췄을 때나 홈으로 나갔을 때
보상을 지급하기 위해 시간을 카운팅 해야할 때는 unscaledTime 을 사용해서 시간 카운팅을 할 수 있다.
2021.10.29.금요일 추가할 내용
Update, Coroutine에서 작동시키기
* 공부하는 단계입니다. 잘못된 부분이 있다면 피드백 부탁드립니다😊
* e-mail : heehee970@naver.com
'Unity > 스크립팅' 카테고리의 다른 글
[유니티 C#] GetAxis, GetAxisRaw. 오브젝트가 바라보는 방향으로 이동하기 (0) | 2021.09.29 |
---|---|
[유니티 C#] Action 과 Func (0) | 2021.09.04 |
[유니티 C#] Time.deltaTime, Application.targetFrameRate을 통한 프레임 제어 (0) | 2021.08.04 |
객체 지향 프로그래밍(OOP)에서의 클래스 개요. 변수와 프로퍼티의 차이 (0) | 2021.08.02 |
[유니티 C#] 함수 대리자. 델리게이트 (Delegate), 이벤트(event) (0) | 2021.08.02 |