본문 바로가기

캔버스 루프 애니메이션 3편 『프레임 계산』

by Recstasy 2020. 2. 5.

물리적으로 '움직임'이란 '시간동안 움직인 거리'다. 우리는 특정 시간동안 움직인 거리가 많을수록 속도가 빠르다고 말한다. 그래서 움직인 거리를 구할 때는 '속도 x 시간'으로 계산한다. '시간'과 '거리'는 물리에서 필수적인 요소이며, 현실을 모방하는 가상 공간(컴퓨터)역시 시간과 거리는 필수다. 단, 현실과 물리적 단위가 다를 뿐이다.


[현실]시간 => [컴퓨터]프레임률

[현실]거리 => [컴퓨터]픽셀


컴퓨터 세계에서는 시간을 프레임률로 계산하며, 거리는 픽셀이다. 따라서 움직임을 만들어야하는 프로그램의 핵심은 프레임률, 픽셀에 따른 가속도에 있다.


1 컴퓨터의 시간

컴퓨터의 시간을 알아보기 위해서 브라우저(크롬 or Edge)에서 F12키를 클릭해보자. 콘솔 항목에서 console.log(Date.now()); 명령어를 입력하면 아래와 같은 숫자가 나올 것이다. 


  

오늘의 날짜와 시간이 아닌 14자리의 숫자가 찍혀나온다. 이와 관련한 자바스크립트 |MDN의 설명은 아래와 같다. 

즉, "1580777086164"란 숫자는 1970년 1월 1일 0시 0분 0초로부터 현재까지의 지나간 시간을 의미하며, 단위는 '밀리초'다.(밀리초:1000/1초) 


자바스크립트로 컴퓨터의 시간은 밀리초 단위로 돌아간다고 일단 이해하고, 이제 개발자는 해당 시간을 프로그램 내의 프레임이란 단위로 변환해야한다. 


1) Delta : 시간 차이

2) Frame : 이미지 프레임의 전환속도

3) Duration : 이미지 프레임의 움직이는 속도

4) currentTime : delta값 저장


'밀리초'를 이용해서 움직임을 만들기 위해서는 위의 4가지 값(Delta, Frame, Duration, currentTime)이 필요하다. 그리고 전체적인 작동원리는 다음과 같다. 


* init() : 초기 시간값 저장

* refresh() : 시간 변화량(delta) 저장, 초기 시간값 갱신

* update(delta) : 이미지 객체의 update()메서드에 시간변화량 전달


requestAnimationFrame()에서 refresh()메서드가 반복(재귀)될 때마다 시간변화량(delta)이 발생한다. 해당 시간변화량을 update()메서드를 통해 이미지 객체에 전달하는 것으로 Game클래스의 책임은 끝난다. 


2 프레임 계산

프레임은 1초당 실행하는 이미지의 개수다. 가령, 영화에서 24프레임이라고 하면 초당 24장의 이미지가 실행되는 셈이다. 게임은 어떨까? 


다음과 같은 상황을 상상해보자.

"당신은 의자에 앉아있고, 플립 애니메이션이 그려진 책 한권과 옆에는 물통 A, B가 있다. 그리고 잠시 뒤 쪽지가 전달된다. 물통에는 다음과 같은 단어가 새겨져 있고, 감독관이 전달한 쪽지에는 숫자(정수)와 규칙이 적혀있다.


*A물통 : Delta

*B물통 : currenTime

*쪽지 : Duration(정수값) 



규칙1: B물통의 물이 Duration과 같아지면, 

        B물통의 물을 A물통에 채워진 양만큼 비워라


규칙2: B물통에 채워진 물의 양을 index공식에 넣어서, 

        정수값이 되면 책장을 한장 넘겨라.


*index공식: (B물통의 물의 양 / Duration) * 책장에 그려진 그림의 개수



이제 당신은 A(Delta)물통의 물이 채워질 때마다 B(currentTime)물통에 쏟아부어야한다. B물통(currentTime)에는 "index"란 눈금이 그려져있다. 해당 눈금은 정수단위이며, 물의 높이가 눈금에 다다를 때마다 당신은 빨리 책장을 한장씩 넘겨야 한다. 

*참고: 플립 애니메이션이란 책장마다 그려진 특정 동작의 이미지를 넘기는 애니메이팅 방식이다.



위의 예시를 게임에 비유하자면 빠진 것은 프레임이다. 하지만 프레임은 준비한 이미지의 개수마다 달라질 수 있고, 개발자가 임의로 정해야하기 때문에 일부로 표시를 하지 않았다. 


* Duration : 1초 동안 나타나는 이미지의 개수  x (1.0 / 개발자가 정한 frame수)


만일 10장의 이미지를 준비했고, frame을 '10'으로 설정했다면 Duration은 '1'이다. Duration이 1이라 가정했을 때 '물통 예시'를 생각해보자. 참고로 delta값은 0.001초로 지정했다.




감독관은 '1'이 적힌 쪽지를 전달했고, A물통의 물은 0.001초(delta)마다 한번씩 채워진다. 그래서 당신은 0.001초마다 A물통의 물을 B물통(currentTime)에 채운다. B물통의 관점에 본다면 0.001초마다 물이 채워지고 있고, 눈금 '1'에 다다를 동안 A물통은 1000번 움직인다. 그리고 B물통의 눈금이 점차 올라갈 때마다 index값은 바뀐다.


"B물통에 채워진 물의 양을 index공식에 넣어서, 

정수값(0,1,2 ...)이 되면 책장을 한장 넘겨라"

 
현재 Duration이 1이므로 index공식에 넣어보자. 

(B물통의 물의 양 / 1) * 이미지의 개수

이미지의 개수는 현재 8장이며, (B물통의 물의 양 / 1)은 1보다 클 수 없다. 규칙1에 의해, B물통의 눈금이 1이 되면 A물통에 채워진 양만큼 다시 비워야하기 때문이다. 따라서 index값을 내림했을 때 0부터 8까지 정수가 나온다. 

이 상황을 코드로 그대로 구현해보면 아래와 같다.

//캔버스 구현(안 보일시 F5 새로고침)


현재 delta값을 이미지의 x, y값에 더하지 않았기 때문에 제자리걸음을 하는 것처럼 보인다. delta값을 더한다면 이동거리가 달라지며, 여기서 우리는 다음과 같은 부분을 질문을 할 수 있다. 

"A물통의 물을 더 빨리 채우면 어떻게 될까?"
"이미지가 x축 방향으로 움직이려면 어떻게 될까?"
"이미지의 이동속도를 높이려면 어떻게 해야할까?"

하나씩 체크해보자.

일단 '물통 예시'를 생각했을 때, 이미지의 반복속도를 빠르게 한다는 의미는 A물통의 물을 더 빠르게 채우면 된다. B물통의 물을 빨리 채울수록 index값이 빠르게 올라가기 때문이다. 그리고 위의 제자리걸음 코드에서 delta값은 "1000/1초"로 설정되어 있다. 

 const now = Date.now();
 const dt = (now - this.lastTime) / 1000.0;

dt값에서 분모를 1000이 아닌 100으로 했을 경우, dt값은 10배 커진다. 그리고 dt값이 10배 커진다는 의미는 B물통을 채우는 속도가 10배 빨라진다는 의미와 같다. dt값을 아래와 같이 고쳐보자. 

const dt = (now - this.lastTime) / 100.0;

//dt값 10배 증가(안 보일시 F5 새로고침)


A물통의 물을 10배 빨리 채웠더니 플립 애니메이팅이 10배 빨라졌다. 


3이동거리
여기서 이미지를 x축으로 움직이게 하려면 어떻게해야 할까? 이 역시 dt를 활용해야하는데 sprite이미지의 x축 방향에 dt값을 더해주면 된다. 

현재 sprite의 x축과 관련된 코드를 아래와 같이 작성해보자.

(안 보일시 F5 새로고침)


루프까지 적용한 코드이지만 현재 세부적인 코드는 중요하지 않다. 델타값(A물통)에 따라 결과가 어떻게 달라지는지 전체적으로 이해하는 것이 중요하다. 

*델타값 활용범위
1) 이미지 반복속도
2) 이미지 거리속도

4 Duration(반복주기)
마지막으로 duration값을 생각해보자.
Duration값은 제작자가 정한 fps값에 따라 달라진다. 

*Duration 구하기
Duration = (이미지의 개수) x (1.0 / fps )

현재 물통 이미지의 동작은 총 8장이며, fps는 10으로 설정했으며, 
Duration값은 0.8이다.

Duration = 8 x (1.0 / 10)

그리고 index공식은 아래와 같다.

*index공식: (B물통의 물의 양 / Duration) * 책장에 그려진 그림의 개수

B물통의 물의 양은 Duration을 넘을 수가 없다. Duration값을 넘어서면 delta값이 음수로 바뀌면서 물의 양이 줄어들기 때문이다. 따라서 (B물통의 물의 양 / Duration)은 '1'을 넘을 수 없다. Duration이 0.8(현재)일 경우, 0.8초마다 8장의 이미지가 반복된다.

Duration 공식에서 알 수 있는 사실은 "Duration은 fps값과 반비례로 움직인다"는 정보다. 만일 fps를 20으로 한다면 위의 Duration값은 0.4가 된다. 이는 8장의 이미지가 0.4초만에 반복된다는 의미다. fps20의 반복속도를 감상해보자. 

//fps 20


5결론
물통 사례부터 시작한 프레임 계산을 종합해보면 다음과 같은 결론을 낼 수 있다. 다음 포스팅에서는 시간, 프레임률을 계산한 총 코드를 정리해보자.

 

 Delta

 FPS

Duration

 증가 

 반복속도 증가

 이동속도 증가

 반복속도 증가 

반복속도 감소

감소

반복속도 감소

 이동속도 감소 

 반복속도 감소

 반복속도 증가 



댓글

최신글 전체

이미지
제목
글쓴이
등록일