ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • TIL_200911(PixiJS-character)
    Today I Learned 2020. 9. 11. 02:46

     

     

    캐릭터를 화면에 만들어 봅니다.

     

     

    먼저 캐릭터를 다운로드합니다.

     

    lhteam.itch.io/finn-sprite

     

    Adventure Time | Finn Pixel Sprite by LHTeam

    32x32 Animated Pixel Fan Art Spirite Finn the human from Adventure Time

    lhteam.itch.io

     

    이러한 이미지 파일을 받게 되죠.

     

     

    이전에 타일을 잘라서 화면을 만들었듯이 이번에도 이미지를 잘라서 사용합니다.

    이미지의 크기는 32x32

     

    const tileSize = 16;
    const characterSize = 32;  // 캐릭터 사이즈
    
    app.loader.add("tileset", "asset.png");
    app.loader.add("character", "FinnSprite.png");
    
    app.loader.load((loader, resources) => {
    
      ...  // tileTexture
      
      let characterFrames = [];
      for (let i = 0; i < 25; i++) {
        characterFrames[i] = new PIXI.Texture (
          resources.character.texture,
          new PIXI.Rectangle(i * characterSize, 0, characterSize, characterSize)
        );
      };
      
      const blob = new PIXI.Sprite(characterFrames[0]);
      blob.scale.x = 2;
      blob.scale.y = 2;
      
      ...
      
      app.stage.addChild(sky);
      app.stage.addChild(background);
      app.stage.addChild(blob);
      
    }

     

    캐릭터를 불러오는데 성공했지만 공중에 떠있네요

     

     

    캐릭터를 바닥에 내리려면

    중력을 적용하여 캐릭터를 내려오게 하고, 충돌조건을 정해야 합니다.

     

    먼저, collision 배열을 선언합니다.

    복잡해보이네요. 다른 방법이 있겠지만 일단 해봅니다.

    let map = {
      width: 16,
      height: 10,
      tiles: [
        12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,
        12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,
        12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,
        12,12,12,23,12,12,12,12,12,12,12,12,12,12,12,12,
        12,12,12,30,12,12,3, 4, 4, 5, 12,12,12,12,12,12,
        12,12,12,30,12,12,12,12,12,12,12,12,12,12,12,12,
        12,12,12,30,12,12,12,12,12,12,12,12,12,12,12,12,
        12,12,12,37,12,12,12,12,12,12,12,12,12,12,12,12,
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
        8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
      ],
      collision: [
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
      ],
    };

     

    ticker에 프레임마다 실행되는 코드를 작성합니다.

     

    app.loader.load((loader, resources) => {
    
      ...
      
      let character = {
        x: 0, y: 0,
        vx: 0, vy: 0,
      };
      
      // Listen for frame update
      app.ticker.add(() => {
      
        blob.x = character.x;
        blob.y = character.y;
    
        character.vy = character.vy + 1;
        character.x += character.vx;
    
        if (character.vy > 0) {
          for (let i = 0; i < character.vy; i++) {
            let testX1 = character.x;
            let testX2 = character.x + tileSize * SCALE - 1;
            let testY = character.y + tileSize * SCALE * 2 - 10;
            if (testCollision(testX1, testY) || testCollision(testX2, testY)) {
              character.vy = 0;
              break;
            }
            character.y = character.y + 1;
          }
        }
      })
    }

     

    collision 함수로 충돌인지 아닌지 확인합니다.

     

    function testCollision(worldX, worldY) {
      let mapX = Math.floor(worldX / tileSize / SCALE)
      let mapY = Math.floor(worldY / tileSize / SCALE)
      return map.collision[mapY * map.width + mapX];
    }

     

     

    점점 게임이 만들어지고 있네요

    이제는 캐릭터를 움직여 볼까요?

     

    전역에 class를 하나 선언합니다.

     

    class Keyboard {
      constructor () {
        this.pressed = {};
      }
    
      watch (el) {
        el.addEventListener('keydown', (e) => {
          console.log(e.key)
          this.pressed[e.key] = true;
        })
        el.addEventListener('keyup', (e) => {
          this.pressed[e.key] = false;
        })
      } 
    }
    
    app.view.setAttribute('tabindex', 0);

     

    ticker에 다음 조건들을 추가합니다.

     

    app.ticker.add(() => {
    
      ...
      
      // 키입력 조건
      if (kb.pressed.ArrowUp) {
        character.vy = -10;
      }
      if (kb.pressed.ArrowRight) {
        character.vx += 2;
      }
      if (kb.pressed.ArrowLeft) {
        character.vx -= 2;
      }
      
      // 움직임 조건
      if (character.vy < 0) {
        character.y += character.vy;
      }
      if (character.vx > 0) {
        character.vx -= 1;
      }
      if (character.vx < 0) {
        character.vx += 1;
      }
    
      // 바닥에 닿은 여부 확인
      let touchingGround = testCollision(
        character.x,
        character.y + tileSize * SCALE * 2 + 1
      );
        
      if (!touchingGround) {
        blob.texture = characterFrames[10];
      } else {
        blob.texture = characterFrames[0];
      }
      
    }

     

     

     

    이제 조작이 가능해졌어요! 거의 다 된 것 같죠?

    이제는 캐릭터 애니메이션을 구현하여 봅시다.

     

    app.ticker.add(() => {
    
      ...
      
      blob.texture = characterFrames[(Math.floor(Date.now() / 200) % 6) + 0];
    });

     

    ticker에서 캐릭터의 Frame을 계속 변경하면 됩니다.

    위의 코드에서는 시간에 따라서 Frame이 변합니다.

     

     

    이런식으로 앞으로 움직이는 애니메이션을 만들어 봅니다.

     

    app.ticker.add(() => {
    
      ...
      
      blob.texture = characterFrames[(Math.floor(Date.now() / 200) % 6) + 0];
      
      if (character.vx > 0) {
        blob.texture = characterFrames[(Math.floor(Date.now() / 100) % 6) + 10];
      }
    });

     

     

    반대로 움직이는 경우를 위해서

    뒤집은 FinnSpriteMirror 이미지를 추가하여 자른 다음 사용했습니다.

     

     

    let characterFramesMirror = [];
    
    for (let i = 0; i < 25; i++) {
      characterFramesMirror[i] = new PIXI.Texture(
        resources.characterMirror.texture,
        // new PIXI.Rectangle(tx, ty, tw, th)
        new PIXI.Rectangle(i * characterSize, 0, characterSize, characterSize)
      );
    }
    
    
    app.ticker.add(() => {
      
      ...
      
      if (character.vx > 0) {
        blob.texture = characterFrames[(Math.floor(Date.now() / 100) % 6) + 10];
        character.dir = true;
      } else if (character.vx < 0) {
        blob.texture = characterFramesMirror[(Math.floor(Date.now() / 100) % 6) + 14];
        character.dir = false;
      } else {
        blob.texture = character.dir 
          ? characterFrames[(Math.floor(Date.now() / 200) % 6) + 0] 
          : characterFramesMirror[(Math.floor(Date.now() / 200) % 6) + 19];
      }
    });

     

     

     

     

     

     

     

     

     

     

     

     

    'Today I Learned' 카테고리의 다른 글

    TIL_200915([JS33] 3. Value Type, Reference Type)  (0) 2020.09.15
    TIL_200912([JS33] 2. Primitive Types)  (0) 2020.09.12
    TIL_200910(PixiJS-Container)  (0) 2020.09.10
    TIL_200909(PixiJS-tileTexture)  (0) 2020.09.10
    TIL_200907(PixiJS - sprite)  (0) 2020.09.07

    댓글

Designed by CHANUL