초기화 -> 물리연산 -> 게임 로직 -> 해체
게임 오브젝트 생성할 때, 최초 1번만 실행되는 함수이다.
Update() 함수 시작 전에 최초 1번 실행되는 함수이다.
* Awake()와 Start()는 초기화의 영역
유니티 엔진이 물리연산을 하기 전에 실행되는 업데이트 함수이다.
컴퓨터의 사양과 관계 없이 고정된 실행주기로 호출되며 CPU 부하가 올 수 있다.
*FixedUpdate()는 물리 연산의 영역
주기적으로 변하는 게임 로직을 적용하는 함수이다.
환경(=성능)에 따라 실행주기가 달라질 수 있다.
*Update()는 게임로직의 영역
모든 업데이트가 끝난 뒤 마지막으로 호출되는 함수이다.
오브젝트가 삭제될 때 무언가를 남기고 삭제되는 함수이다.
*OnDestory는 해체의 영역
(활성화 Awake()와 Start()사이에 위치)
최초 1회 실행이 아닌 크고 켜고 할 때마다 실행된다.
(Lateupdate()와 OnDesytoy 사이에 위치)
모든 업데이트가 끝나고 오브젝트가 비활성화 되거나 삭제될 때 실행된다.
Update(): 매 프레임마다 함수 안의 내용을 동일하게 계속 호출한다.
Update()는 기본적으로 1초에 프레임을 호출하는 횟수(=성능 차이)가 다르게 될 경우 문제가 발생하며, 이 문제를 해결하기 위해서 Time.deltaTime을 사용한다.
Update() 함수가 호출되는 간격을 알려준다.
▷ 1초에 10프레임인 경우
Time.deltaTime = 0.1f (1초 / 10 프레임이라는 뜻)
=> Update()함수가 10번 호출 된다. 0.1f * 10(프레임) = 1f
▷ 1초에 20프레임인 경우
Time.deltaTime = 0.05f (1초 / 20 프레임이라는 뜻)
=> Update()함수가 20번 호출 된다. 0.05f * 20(프레임) = 1f
결론적으로 모두 1f 이다.
=> Time.deltaTime을 통해 각 컴퓨터 속도 및 성능에 맞추어 모든 사용자의 PC에서 동일한 퍼포먼스를 보여주도록 한다.
유니티에서 무언가 이동하거나 자연스럽게 화면 전환하거나 기타 등등에 이용한다.
끝점의 값이 주어졌을 때 그 사이에 위치한 값을 추정하기 위해서 직선 거리에 따라 선형적으로 계산하는 방법이다.
Lerp(float a, float b, float t)
a: 시작 점
b: 끝 점
t: a와 b 사이의 비율 지점으로 0에서 1 사이의 값을 가진다.
ex) t가 0이면 시작점인 a, t가 1이면 끝점인 b, 0.5이면 그 중간 점이다.
public class Lerp : MonoBehaviour
{
Vector3 startPos; // 시작 값
Vector3 targetPod = new Vector3(0, 5, 0); // 끝 값
float currentTime = 0; //a와 b를 선형보간하는 t
public float moveTime = 5.0f; //움직이는 시간
private void Start()
{
startPos = transform.position;
}
void Update()
{
currentTime += Time.deltaTime; // 매 프레임마다 업데이트
if(
transform.position = Vector3.Lerp(startPos, targetPod, currentTime / moveTime);
}
}
=> 특정 목표 위치까지의 이동에 사용한다.
public class DotweenTest2 : MonoBehaviour
{
private Vector3 targetPos = new Vector3(0,5,0);
private Vector3 targetScale = new Vector3(2,2,2);
Tweener tr;
Sequence sequence; // 하나가 아닌 다른 Tweener들을 제어
void Start()
{
// 기본 설정 값임
DOTween.Init(false, true, LogBehaviour.Verbose).SetCapacity(200, 50);
// 첫번째 요소 autoKillMode: Dotween 재사용 여부 결정
// useSafeMode: 약간 느리지만 실행되는 동안, 실행 대상이 파괴 되는 경우와 같은 예외 상황을 자동으로 처리
// LogBehavior: 오류 메세지 기록을 설정
// SetCapacity(Tweener 개수, Sequence 개수)
transform.DOMove(targetPos, 1.0f); // endValue, duration
transform.DOMoveX(10f, 1.0f);
transform.DOShakePosition(10f, 1, 10, 90, true);
transform.DOScale(targetPos, 2.0f);
transform.DOScale(1.5f, 1.0f).SetLoops(3, LoopType.Restart); // set~ 변화에 대한 설정
transform.DOScale(1.5f, 1.0f).SetLoops(3, LoopType.Yoyo);
transform.DOScale(1.5f, 1.0f).SetDelay(1.0f);
Tweener tr = transform
.DOScale(1.5f, 2.0f)
.SetEase(Ease.InBounce)
.OnComplete(()=> transform.DOMove(targetPos, 2.0f));
tr.Play(); //이걸로 재사용 가능
//tr.Kill(); // 재사용하던 tween 제거
// Do 대상의 변화를 직접 지시
// Set 추가 설정
// on 람다를 이용한 콜백 함수 실행
//Dotween의 반환 타입은 Tweener -> 값을 제어하고 애니메이션을 적용 -> 재사용 가능(첫번째 인자 false로 바꾸기 -> 메모리에 저장)
// Sequence: 값을 제어하지 않고 다른 Tweener 를 제어, 그룹으로 애니메이션을 적용
}
}
다양한 옵션들을 통해 오브젝트의 동작, 애니메이션을 줄 수 있다.
Tweener tr = transform
.DOScale(1.5f, 2.0f)
.SetEase(Ease.InBounce)
.OnComplete(()=> transform.DOMove(targetPos, 2.0f));
오브젝트에게 DO를 통해 크기 변화를 지시했는데, set을 통해 동시에 Bounce 효과를 준다. 그리고 ON을 통해 complete, 즉 동작이 종료되면 이동하도록 지시했다.
DOTween의 반환 타입이다.
값을 제어하고 애니메이션을 적용한다.
DOTween.Init(false, true, LogBehaviour.Verbose).SetCapacity(200, 50);
다음과 같이 DOTween의 초기 설정에서 첫번째 인자 값인 autoKillMode를 false로 하면 DOTween의 동작을 재사용 가능하도록 사용할 수 있다.
값을 제어하지 않고 다른 Tweener들을 제어한다.
그룹으로 애니메이션을 적용한다.
public class DotweenTest6 : MonoBehaviour
{
Sequence sequence;
void Start()
{
sequence = DOTween.Sequence();
sequence.Append(transform.DOMove(new Vector3(0f, 5f, 0f), 2.0f));
sequence.Join(transform.DORotate(new Vector3(0f, -18f, 0f), 2.0f));
sequence.Append(transform.DOMove(new Vector3(0f, 36f, 0f), 2.0f));
sequence.Insert(4.0f, transform.DOScale(new Vector3(1.5f, 1.5f, 1.5f), 1.0f));
sequence.Prepend(transform.DOScale(new Vector3(0.5f, 0.5f, 0.5f), 2.0f));
}
}
sequence 메소드
위의 코드에서의 동작 순서
Prepend => Append/Join => Append => Insert
일반적인 결과는 하나의 프레임 안에서 결과 값을 보여준다.
코루틴은 Enumerator을 사용해서 여러 프레임에 걸쳐서 실행된다.
코루틴의 정의: Update() 메서드와 다르게 메서드 제어권을 유니티에 반환하고, 특정 조건이 되면 다시 진행한다.
public class Coroutine1 : MonoBehaviour
{
public Image image;
private float alpha;
private float fadeTime = 3.0f;
void Start()
{
}
void FadeIn()
{
while(alpha < 1.0f)
{
alpha += Time.deltaTime / fadeTime;
image.color = new Color(1, 1, 1, alpha);
}
}
void Update()
{
if (alpha < 1.0f)
{
alpha += Time.deltaTime / fadeTime;
image.color = new Color(1, 1, 1, alpha);
}
}
}
일반 메서드 FadeIn()의 경우 최종 결과 값만 반영되어, 서서히 뚜렷해지는게 아니라 그냥 뚜렷한 이미지를 출력한다.
update() => 매프레임 마다 결과값이 업데이트 되면서 서서히 이미지가 노출된다.
하지만, Update()에다만 모든 동작을 적용하면 코드가 너무 비대해진다.`
public class Coroutune3 : MonoBehaviour
{
public Image image;
private float alpha = 0f;
private float fadeTime = 3.0f;
void Start()
{
StartCoroutine(FadeIn());
}
IEnumerator FadeIn()
{
while (alpha < 1.0f)
{
alpha += Time.deltaTime / fadeTime;
image.color = new Color(1, 1, 1, alpha);
yield return null;
if (alpha > 0.5f)
yield break;
}
}
}
다음과 같이 코루틴을 적용하여 같은 효과를 보여줄 수 있다.
▷ 코루틴 사용 조건
IEnumerator Count()
{
Debug("1");
yield return new WaitForSeconds(1, 0f);
Debug("2");
yield return new WaitForSeconds(1, 0f);
Debug("3");
}
▷ 코루틴 사용방법
Coroutine co = StartCoroutine(FadeIn());
Coroutine co2 = StartCoroutine("FadeIn");
StopCoroutine(co);
StopCoroutine(FadeIn());
stopAllCoroutines();
▷ 코루틴 작동 방식
C# Enumerator를 활용해서 만들어진다.
=> 차이점은 열거할 때마다 특정 프레임에서 실행되도록 한다.
YieldInstruction
=> 코루틴은 일정 시간 대기하고 실행하는 방식으로 작동한다. 해당 방식을 YieldInstruction 클래스가 정의하고 있다.
IEnumerator CoroutineTest()
{
// 다음 update() 호출시까지 대기 후 실행
yield return null;
// 다음 FixedUpdate() 호출시까지 대기 후 실행
yield return new WaitForFixedUpdate();
// float time만큼 시간이 지난 후 첫프레임까지 대기 후 실행
yield return new WaitForSeconds(time);
// float time만큼 시간이 지난 후 첫프레임까지 대기 후 실행
// timeScale의 영향을 받지 않음 (임의로 조정된 시간 반영x)
yield return new WaitForSecondsRealtime(time);
// 모든 렌더링 작업이 완료되는 프레임이 끝날 때까지 대기 후 실행
yield return new WaitForEndOfFrame();
// 해당 조건이 참이 될 때까지 대기 후 실행
yield return new WaitUntil(() => Time > 10);
}
* StartCoroutine() 하면 garbage가 생기기 때문에 상황에 따라 적절하게 필요한 곳에 사용해야 한다.
내용이 반복적이고 꾸준하게 실행되는 경우에는 Update()를 사용하는 것도 좋다. (update()는 garbage가 발생하지 않는다.)
WaitForFixedUpdate wf = new WaitForFixedUpdate();
// 다음 FixedUpdate() 호출시까지 대기 후 실행
yield return new WaitForFixedUpdate();
yeid return wf;
=> 하나의 인스턴스로 돌려쓰는게 가능한 캐싱의 경우는 메모리를 아낄 수 있다.
public static readonly WaitForFixedUpdate m_WaitForFixedUpdate = new WaitForFixedUpdate();
public static readonly WaitForEndOfFrame m_WaitForEndOfFrame = new WaitForEndOfFrame();
private static readonly Dictionary<float, WaitForSeconds> m_WaitForSecondsdict = new Dictionary<float, WaitForSeconds>();
public static WaitForSeconds WaitForSeconds(float waitTime)
{
WaitForSeconds wfs;
if (m_WaitForSecondsdict.TryGetValue(waitTime, out wfs))
return wfs;
else
{
wfs = new WaitForSeconds(waitTime);
m_WaitForSecondsdict.Add(waitTime, wfs);
return wfs;
}
}
=> 다음과 같이 딕셔너리를 사용해서 garbage 없이 매개변수를 원하는 초로 입력 받아 사용하는 것도 가능하다.
해당 포스트는 게이머TV, [4. 유니티가 어려운 초보자를 위한 유니티 기초 강의], (2024, 9월 22일), 섹션 1. 유니티 기초 강의를 참고하여 작성하였습니다.
Corner Unity
Editor : 니나노
[유니티 소소모임] 1주차. 유니티 기초 (Editor: Luna) (4) | 2024.10.04 |
---|---|
[유니티 소소모임] 1주차. 유니티 기초 (Editor: Hetbahn) (0) | 2024.10.04 |
[유니티 소소모임] 1주차. 유니티 기초 (Editor: Borybop) (0) | 2024.10.04 |
[유니티 소소모임] 1주차. 유니티 기초 (Editor: 동동) (0) | 2024.10.04 |
[유니티 소소모임] 1주차. 유니티 기초 (Editor: Ijin) (1) | 2024.10.04 |