Inside

Matter.js 사용법부터 성능 개선 팁까지

“당신이 119를 누르는 순간”에서 사용한 예시를 중심으로
신성일 뉴스룸 디벨로퍼|디지털이노베이션팀 2022-08-26 09:00:02
히어로콘텐츠 5기 디지털 기사인 “당신이 119를 누르는 순간” 중 part 1의 “목숨이 걸린 찰나”에서 2.6초에 한 번씩 소방서에 신고가 들어오는 것을 표현할 방법으로 Matter.js를 활용하기로 하였다.

Matter.js란 자바스크립트 라이브러리로, 웹에서 2D 물리 효과를 재현하기 위해 사용한다. 이 라이브러리를 활용하여 간단한 게임을 만들거나 인터랙티브 웹에 활용하기도 한다. “목숨이 걸린 찰나”에서는 신고를 형상화한 구슬이 하나씩 페이지 상단에서 떨어지고, 하단에서는 떨어진 구슬을 모아 신고가 얼마나 들어왔는지 보여주는 형식을 사용하기로 했다.

이 글에서는 Matter.js를 사용하여 구슬이 떨어지는 효과를 내는 법과 성능을 개선하기 위한 팁을 작성하였다.
구슬 떨어뜨리기
기획을 구현하기 위해서는 먼저 페이지 상단에서 구슬이 떨어져야 한다. Matter.js 공식 사이트에 있는 튜토리얼을 바탕으로 기본적인 세팅을 완료하여, 하나의 Renderer를 만들었다고 해보자. 그 다음으로 할 일은 구슬을 만드는 일이다. Matter.js에서 새로운 요소를 만들기 위해서는 “Matter.Bodies” 내의 메소드를 사용해야 한다.
위 코드는 이번 기획에서 사용한 코드를 가공한 것이다. 구슬을 만들기 위해 “Matter.Bodies.circle()” 메소드를 사용하였다. 구슬에 입힐 이미지는 옵션을 통해 sprite로 넣어주었다. 구슬 이미지의 크기는 40px*40px이고, Matter.js에서 띄워질 구슬의 크기는 20px*20px이었기에 sprite의 xScale, yScale 옵션을 통해 크기를 조절해주었다.

다음으로는 페이지 상단에 랜덤하게 띄우는 코드를 추가하였다.
페이지 내 좌우 50px 안 랜덤한 위치에 구슬이 떨어지도록 하였다. 또 옵션에 force를 추가함으로써, 구슬이 페이지 가운데로 모이도록 하였다.
구현이 완료된 후의 사진이다. “목숨이 걸린 찰나”에 실제로 구현된 것처럼 페이지 최상단이 아닌, 어느 정도 떨어진 위치에서 구슬이 생성하고 싶을 수도 있다. 그럴 때는 원하는 위치 상단의 높이를 계산하고, “Matter.Bodies.circle()”의 두 번째 인자를 조절하면 된다.
구슬 모으기
상단에서 생성된 구슬은 Matter.js 상의 중력 작용으로 아래로 떨어진다. 아래로 떨어진 구슬을 모아줄 구덩이가 필요한데, 구슬을 만들때와 마찬가지로 “Matter.Bodies” 안의 메소드를 사용한다. 다만 가운데가 움푹들어간 구덩이의 모양은 “Matter.Bodies” 내에 정의된 “circle”, “rectangle”, “trapezoid”, “polygon”의 모양과는 다르기에, “fromVertices”라는 메소드로 vertice로부터 모양을 직접 정의해줘야 한다.
이해를 돕기 위해, 먼저 최종적으로 만들어진 구덩이 이미지를 첨부하였다. 위와 같이 가운데가 움푹 들어간 오각형이 우리가 최종적으로 만들 구덩이의 모습이다. 이 오각형을 Matter.js 상에서 그리기 위해, 오각형을 구성하는 점들을 “fromVertices”의 인자로 전달하였다.
하지만 처음에 시도했을 때는 오각형이 의도한 대로 그려지지 않고 다음과 같이 평평한 사각형이 그려졌다.
이 현상에 대해서 Matter.js 공식 문서에는 오목한 형태의 도형을 그릴 때는 “poly-decomp”라는 패키지를 설치해야 하고, Common.setDecomp에 설정해야 한다고 기술되어있다.
이 설명대로 패키지를 추가하고 설정하면, 위에서 봤던 오각형이 제대로 그려진다.
성능 개선
Matter.js는 웹에서 간단히 물리 효과를 구현할 수 있도록 한다. 하지만 물리 효과를 구현하는 만큼 사용자 기기에 상당한 부하가 걸린다. 만약 Matter.js를 사용함으로써 웹 전체에 성능이 안 좋아진다면, 사용자 경험에 매우 안 좋은 영향을 끼친다. 따라서 Matter.js를 사용할 때는 적절한 성능 개선이 필요하다. 이 챕터에서는 세 가지 정도 성능 개선법을 기술하였다.

첫 번째 방법은 앞으로 안 움직일 요소는 “Sleeping” 상태로 전환하는 것이다. Matter.js 에서 “Matter.Bodies”로 생성한 각 요소는 프레임마다 항상 업데이트되고, 다른 요소와 충돌이 일어나는지 검사받는다. 하지만 “Matter.Sleeping.set()” 메소드를 활용하여 요소를 Sleeping 상태로 전환하면, 해당 요소의 업데이트는 정지되고, 충돌 검사를 받지 않는다.

이번 프로젝트에서는 구슬이 2.6초마다 페이지 상단에서 생성되는데, 사용자가 페이지에 오래 머물러 구슬이 많이 생성된다면 큰 부하가 걸리게 된다. 따라서 다음과 같이 구슬 생성 후 10초가 지나면 구슬 요소를 Sleeping 상태로 전환하는 방식을 통해 성능 최적화를 하였다.
두 번째는 Matter.js의 FPS를 제한하는 방법이다. Matter.js의 렌더링 주기는 기본적으로 브라우저 API인 “requestAnimationFrame()”으로 정해진다(일반적으로 60FPS). 웹에서 애니메이션을 그릴때, 15FPS만 되어도 유저들은 충분히 부드럽다고 느낀다. 따라서 Matter.js의 FPS를 적절히 낮추면, UX를 크게 해치지 않으면서도 큰 성능개선 효과를 얻을 수 있다.

Matter.js의 FPS를 제한하기 위해서는 라이브러리를 직접 수정해야 한다. CDN으로 추가하였다면 라이브러리를 다운받은 후 코드를 직접 수정하고, npm으로 설치하였다면 patch-package를 사용한다. 다음 코드는 CDN으로 추가하였을 경우의 예시이다.
위 코드와 같이 라이브러리 상의 모든 requestAnimationFrame을 본인이 원하는 FPS로 setTimeout을 활용하여 지정해준다. 이때 유심히 볼 것이 수정 전에 라이브러리에서는 “_requestAnimationFrame” 이 사용되었다는 것이다. 브라우저 API는 “requestAnimationFrame”인데, 이름이 다른 이유는 Matter.js 코드 안에서 “_requestAnimationFrame”을 따로 선언하였기 때문이다. 일반적으로는 브라우저 API가 그대로 들어가는데, 지원하지 않는 브라우저를 대비하기 위해 Matter.js에서 사용한 방법이다. 이렇게 선언한 부분 또한 찾아내어 다음과 같이 고쳐주면 된다.
세 번째는 Matter.js 렌더러의 pixelRatio를 조절하는 방법이다. 이는 Matter.js의 퀄리티를 크게 떨어뜨리기에 위 두 단계를 먼저 진행했음에도 부하가 많이 걸릴 때 사용하는 것이 좋다. 방법은 렌더러를 생성할 때, pixelRatio 옵션을 주면 된다. 낮으면 낮을수록 퀄리티는 낮아지며 성능은 향상된다.
Matter.js는 인터랙티브 웹을 제작할 시 유용하게 활용할 수 있는 라이브러리다. 활용도가 매우 높고 적절히 사용한다면 좋은 사용자 경험을 줄 수 있다. 하지만 그만큼 사용자 기기에 부하가 많이 걸리는데 적절히 퀄리티를 낮추는 방식으로 부하를 줄일 수 있다. 만약 Matter.js와 같은 효과를 내고 싶지만, 다른 라이브러리를 사용하고 싶을 때는 “Planck.js”, “Box2D”, “Ammo.js” 등을 조사해보는 것을 추천한다.
관련 콘텐츠 더보기
살아간다, 당신을 위해 타인을 위해 목숨까지 거는 사람들이 있다. 다치고 무너지며 때로는 생명을 잃기도 하는 제복 공무원.
공동체를 위해 모든 것을 바치고 떠난 이의 가족과, 가장 위험한 현장에서 싸우고 있는 현직 소방관을 만났다.
2022.08.07~08.12·시리즈 2화·히어로콘텐츠 5기
신성일 뉴스룸 디벨로퍼
신성일 뉴스룸 디벨로퍼|디지털이노베이션팀

퍼즐조각을 하나하나 맞춰나가며 문제를 해결하고 화면을 구성하는 일에 재미를 느낍니다. 기술자로서 충실할 생각이지만, 기회가 주어지면 제 아이디어를 제안할 때도 많습니다. 완성된 결과물을 유저들이 즐겨 사용해줄때 보람을 느낍니다.