PixiJS 시작하기
📄

PixiJS 시작하기

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>

Loading Comments...