PixiJS 시작하기 (Learning Pixi)
📄

PixiJS 시작하기 (Learning Pixi)

Created
May 7, 2021 03:47 PM
Tags
js
webgl
 
notion image
 
 
Pixi V4 기준으로 작성된 글이며(최신은 V6인데 API는 별 차이 없다),
Pixi를 사용하기에 앞서 '어떻게 시작해야 하는지' 까지만 알려줌을 목적으로 작성한 글

Getting started

Pixi는 2D Sprites를 렌더링하는 엔진이다
JS만으로 쉽고 빠르게 게임이나 여러 그래픽을 만들 수 있다는 말

Installing static web server

먼저 프로젝트에 대한 디렉터리를 하나 만들자
웹 서버의 Root가 될 디렉터리인데, Pixi를 실행하기 위해 웹 서버는 필수기 때문
 
디렉터리를 만든 뒤 아래의 명령을 통해 live-server 모듈을 설치해주자
 
# npm npm i -g live-server # yarn yarn global add live-server # start live-server cd <directory-name> live-server
 
이후 브라우저를 켠 뒤, localhost:8080 접속하면 텅 빈 화면이 출력될텐데
 
notion image
 
여기까지 오면 일단 웹 서버 준비는 끝

Installing Pixi

말했듯이, 여기서는 v4.5.5 버전을 사용해 진행한다
 
이를 위해 먼저 pixi.min.js 라는 이름의 파일을 여기에서 다운로드 받은 뒤
위에서 만든 Root 디렉터리에 lib 디렉터리 추가한 후 해당 스크립트를 넣어주자
 
/ - lib - pixi.min.js
 
이후 다음과 같이 index.html 파일을 하나 만들어 Pixi를 실행해보자
 
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Hello Pixi</title> </head> <body> <script src="./lib/pixi.min.js"></script> <script> let type = 'WebGL'; if (!PIXI.utils.isWebGLSupported()) { type = 'canvas'; } PIXI.utils.sayHello(type); </script> </body> </html>
 
아까 실행했던 live-server 를 통해 띄워진 웹 서버(localhost:8080) 으로 들어간 뒤
콘솔 창(F12)을 켜보면 다음과 같은 메시지가 나타날 것이다
 
notion image
 
여기까지 왔으면 기본적인 준비는 이제 끝
 

Creating the Pixi application and stage

가장 먼저, Sprites를 나타낼 수 있는 사각형의 창을 하나 만들어보자
 
// Pixi 애플리케이션 생성 const app = new PIXI.Application({ width: 256, height: 256, }); // Canvas 엘리먼트를 가져오고, 이를 DOM에 추가 document.body.appendChild(app.view);
 
해보면 아래와 같이 검정색 사각형이 브라우저에 나타나게 된다
 
notion image
 
참고로, PIXI.Application 객체를 생성해주며 전달해주는 옵션에는 여러가지가 있다
 
const app = new PIXI.Application({ width: 256, // default: 800 height: 256, // default: 600 antialias: true, // default: false transparent: false, // default: false });
 
Defaults를 이용해도 상관은 없고, 좀 더 자세히 알고싶다면 PIXI Doc을 참고
(계속 언급했듯이 v4.5.5 기준으로 다루고 있으며, 최신은 v6)
 
  • antialias : 글꼴이나 그래픽 객체의 모서리를 부드럽게
  • transparent : Canvas 객체의 뒷 배경 투명하게
 
추가로, 배경 색을 바꾸고자 한다면 renderer 객체에 접근해 프로퍼티 값을 수정하면 된다
 
const app = /* ... */; app.renderer.backgroundColor = 0x061639; // hex color
 
화면을 꽉 채우고 싶다면? 이렇게 해주자
 
const app = /* ... */; app.renderer.view.style.position = 'absolute'; app.renderer.view.style.display = 'block'; app.renderer.autoResize = true; app.renderer.resize(window.innerWidth, window.innerHeight);
 
물론 HTML Body에 margin 이 기본적으로 지정되어 있기에, 이를 제거해줘야만 하겠다
 
<head> <style> body { margin: 0; } </style> </head>

Pixi Sprites

Renderer 객체를 만들어줬으니 이제 Sprites를 추가해보자
Stage라는 객체를 이용하는데, Application 객체의 stage 프로퍼티를 이용해 접근이 가능하다
 
app.stage
 
잠깐 Stage를 설명하자면...
Pixi에는 Container라는 일종의 '빈 무대'가 있으며,
stage 는 Scene에 나타나는 Root Container 개념이다
따라서 어떠한 것이든 Scene 안에 존재해야 Canvas에 렌더링 될 수 있다
 
이 Sprite는 Sprite 클래스를 이용해 만드는데, 방법은 세 가지가 있다 (v4.5.5)
 
  • 이미지 파일
 
그 전에 먼저 Pixi에서는 어떻게 이미지를 불러오는지 보도록 하자

Loading images into the Texture caches

Pixi는 이미지를 WebGL GPU로 렌더링하기 때문에...
이미지는 반드시 GPU에서 처리할 수 있는 포맷이어야 한다
 
따라서 반드시 WebGL 텍스처로 이미지를 변환시켜줘야 하는데
이건 그냥 Pixi의 API를 이용하면 된다
 
// '/images/cat.png' 경로에 위치한 이미지 파일을 Pixi로 불러오기 PIXI.utils.TextureCache['/images/cat.png'];
 
이렇게 Pixi로 이미지를 Load할 수 있으며, Pixi는 이를 TextureCache 라는 것으로 저장한다
 
const texture = PIXI.utils.TextureCache['/images/cat.png']; const sprite = new PIXI.Sprite(texture);
 
Texture Cache를 거치지 않고, 아니라 바로 Texture로 가져오는 방법도 있다
loader 프로퍼티를 이용한다
 
PIXI.loader .add('/images/cat.png') .load(() => { // 이미지를 불러온 뒤 실행할 callback const sprite = new PIXI.Sprite( PIXI.loader.resources['/images/cat.png'].texture, ); });
 
참고로 Loader를 이용해 이미지를 불러오는 경우, Sprite 생성은 다음 순서를 따른다
 
  1. loader.resources 프로퍼티를 통해 해당 이미지 객체에 접근이 가능하며
  1. 객체 내 texture 프로퍼티를 이용해 Texture 인스턴스로 접근이 가능하다
  1. 이를 이용해 Sprite 객체를 생성
 
한 번에 여러 이미지를 Load 할 수도 있다
 
PIXI.loader .add('/images/cat1.png') .add('/images/cat2.png') .add('/images/cat3.png') .load(() => { /* ... */ }) .load(() => { /* ... */ });
 

Displaying sprites

이미지를 불러온 후에는 Sprite를 만들 수 있다
실습해보자, 일단 이 이미지를 다운로드 받은 뒤 아래 경로에 저장한다
 
/ - images - cat.png <-- 여기 - lib
 
cat.png 라는 이름으로 images 디렉터리 만들고 이 곳에 넣어준다
이 이미지를 Scene에 추가하는 코드는 아래와 같다
 
PIXI.loader // 이미지 Load .add('/images/cat.png') .load(() => { // Sprite 생성 const sprite = new PIXI.Sprite( PIXI.loader.resources['/images/cat.png'].texture, ); // Sprite를 Stage에 추가(addChild) app.stage.addChild(sprite); });
 
앞서 말했듯이 Scene에 addChild() 하지 않으면 보이지 않는다
아무튼 제대로 실행하면 결과는 아래와 같다
 
notion image
 
만약 Sprite를 Stage에서 지우고 싶다면? 그냥 removeChild() 해주면 된다
 
app.stage.removeChild(sprite);
 
보이지 않게만 하고 싶다면 Sprite의 visible 프로퍼티의 값을 flase 로 바꿔준다
 
sprite.visible = false;
 

Positioning sprites

위에서 만들었던 고양이 Sprite를 움직여보자
Sprite의 xy 프로퍼티를 이용해 위치를 지정할 수 있으며, Default는 0 이다
 
/* ... */ .load(() => { const sprite = new PIXI.Sprite(/* ... */); sprite.x = 96; sprite.y = 96; app.stage.addChild(sprite); });
 
여기에 96 이라는 값을 넣어 Sprite를 Scene 중앙에 위치하도록 해봤다
 
notion image
 
다음과 같이 한 번에 xy 의 값을 지정할 수도 있다
 
sprite.position.set(96, 96);
 

Size and Scale

Sprite 자체의 크기를 바꿀 수도 있다
 
// size sprite.width = 80; sprite.height = 120;
 
// scale sprite.scale.x = 0.5; sprite.scale.y = 1.5; // or sprite.scale.set(0.5, 1.5);
 

Rotation

Radian을 이용해 Sprite의 회전도 가능하다
 
sprite.rotation = Math.PI / 2; // rotate +90 deg
 
이 때 Anchor point라는 것을 기준으로 회전하며,
아무런 설정을 하지 않았다면 Sprite의 좌측 상단 부분이 Anchor point가 된다
 
notion image
 
이를 변경하고자 한다면 anchor 프로퍼티를 이용해 설정이 가능하다
 
sprite.anchor.x = 0.5; sprite.anchor.y = 0.5; // or sprite.anchor.set(0.5, 0.5);
 
참고로, Sprite 우측 하단을 1 이라 생각하고 설정하면 되겠다
 
notion image
 
만약 Pixel 단위로 지정하고자 한다면 pivot 프로퍼티를 이용한다
 
sprite.pivot.x = 32; sprite.pivot.y = 32; // or sprite.pivot.set(32, 32);
 

Make a sprite from a tilset sub-images

다음과 같은 Tileset 이미지를 이용해 Sprite를 만들어보자
(참고로, 이 방식이 리소스를 한 번만 요청하기에 조금 더 효율적)
 
notion image
 
위 이미지는 전체 크기가 384x384 픽셀이며, 각 Tile은 64x64 의 크기를 갖는다
이걸 /images 디렉터리 아래에 tileset.png 라는 이름으로 저장하자
 
notion image
 
최종적으로는 위와 같은 모습으로 구현할 것이며,
이를 위해 먼저 tileset.png 이미지를 가져온다
 
PIXI.loader .add('/images/cat.png') // 이전에 가져왔던 '/images/cat.png' .add('/images/tileset.png') .load(() => { // 앞서 '/images/cat.png'으로 가져온 이미지를 Stage에 추가했던 코드 const sprite = new PIXI.Sprite( PIXI.loader.resources['/images/cat.png'].texture, ); sprite.position.set(96, 96); app.stage.addChild(sprite); }) .load(() => { // 캐시된 이미지 데이터를 이용해 Texture 생성 const textrue = PIXI.utils.TextureCache['/images/tileset.png']; /* ... 로켓 추가하는 코드 ... */ });
 
Tileset에서 일부 Tile만을 가져오기 위해서는 Rectangle 클래스를 이용한다
 
여기서는 Tileset 이미지 내 x: 192 y: 168 에 위치해 있는
64x64 크기의 우주선 이미지를 가져와보도록 하겠다
 
.load(() => { const textrue = PIXI.utils.TextureCache['/images/tileset.png']; // Rectangle 생성 // 우주선은 Tileset 이미지 내 `x: 192`, `y: 168`에 위치해 있으며 // `64x64` 크기를 가지기 때문에 아래와 같이 args 전달 const rect = new PIXI.Rectangle(192, 128, 64, 64); // (x, y, width, height) // 이미지에 프레임 씌워 해당 부분만 보여주도록 함 texture.frame = rect; // Texture를 이용해 Sprite 생성 const rocket = new PIXI.Sprite(texture); // 위치 지정 rocket.position.set(32, 32); // Stage에 추가 app.stage.addChild(rocket); });
 
실행해보면 아래와 같이 나오는데
 
notion image
 
이러면 별로 보기에 좋지 않으니 /images/cat.png Sprite를 Invisible하게 만들어주자
 
PIXI.loader .add('/images/cat.png') .add('/images/tileset.png') .load(() => { const sprite = new PIXI.Sprite( PIXI.loader.resources['/images/cat.png'].texture, ); sprite.position.set(96, 96); app.stage.addChild(sprite); // invisible하게 만들어주기 sprite.visible = false; }) .load(() => { // 로켓 추가하는 코드 const texture = PIXI.utils.TextureCache['/images/tileset.png']; const rect = new PIXI.Rectangle(192, 128, 64, 64); texture.frame = rect; const rocket = new PIXI.Sprite(texture); rocket.position.set(32, 32); app.stage.addChild(rocket); });
 
notion image
 
물론 그냥 /images/cat.png 리소스 관련 코드를 삭제해줘도 된다
 
아무튼 이렇게 Sprite의 추가 및 제어가 가능하며,
이를 기반으로 목적에 맞춰 검색 등을 이용해 구현해 나가면 된다
 

전체 코드

튜토리얼에서 다룬, 동작하는 전체 코드는 아래와 같다
 
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Hello Pixi</title> <style> body { margin: 0; } </style> </head> <body> <script src="./lib/pixi.min.js"></script> <script> let type = 'WebGL'; if (!PIXI.utils.isWebGLSupported()) { type = 'canvas'; } PIXI.utils.sayHello(type); const app = new PIXI.Application({ width: 256, height: 256, }); document.body.appendChild(app.view); PIXI.loader .add('/images/cat.png') .add('/images/tileset.png') .load(() => { const sprite = new PIXI.Sprite( PIXI.loader.resources['/images/cat.png'].texture, ); sprite.position.set(96, 96); app.stage.addChild(sprite); sprite.visible = false; }) .load(() => { const texture = PIXI.utils.TextureCache['/images/tileset.png']; const rect = new PIXI.Rectangle(192, 128, 64, 64); texture.frame = rect; const rocket = new PIXI.Sprite(texture); rocket.position.set(32, 32); app.stage.addChild(rocket); }); </script> </body> </html>