Vue.js로 스프라이트 시트 동작 시키기 (3) - 프레임 활용

Dev Smile·2024년 8월 14일
1

앞선 스프라이트 시트 재생에 이어서 방향키로 원하는 프레임을 표시하는 방법을 알아보겠습니다.

1. SpriteAnimator 컴포넌트 수정

  • src/components/SpriteAnimator.vue 파일을 수정합니다.
<template>
  <p>현재 입력 : {{ currentInput }}</p>
  <div :style="spriteStyle"></div>
</template>

<script>
export default {
  props: {
    frameWidth: {
      type: Number,
      required: true
    },
    frameHeight: {
      type: Number,
      required: true
    },
    // 그냥 재생이 아닌 키 입력에 의한 동작으로 totalFrames 삭제
    animationSpeed: {
      type: Number,
      default: 100
    },
    spriteSheet: {
      type: String,
      required: true
    },
    scale: {
      type: Number,
      default: 1
    }
  },
  data() {
    return {
      currentFrame: 0,
      currentInput: '', // 현재 입력된 키를 나타내는 문자열
      direction: 2, // 캐릭터의 현재 방향 (0: 위, 1: 오른쪽, 2: 아래, 3: 왼쪽)
      isMoving: false, // 캐릭터가 움직이고 있는지 여부 (키 입력이 지속되고 있는지)
      frames: {
        // 캐릭터의 애니메이션 프레임을 정의한 객체
        idle: [
          [12, 13, 14, 15], // 위쪽 idle 애니메이션 프레임 번호들
          [8, 9, 10, 11], // 오른쪽 idle 애니메이션 프레임 번호들
          [0, 1, 2, 3], // 아래쪽 idle 애니메이션 프레임 번호들
          [4, 5, 6, 7] // 왼쪽 idle 애니메이션 프레임 번호들
        ],
        move: [
          [34, 35, 36, 37, 38, 39], // 위쪽 이동 애니메이션 프레임 번호들
          [28, 29, 30, 31, 32, 33], // 오른쪽 이동 애니메이션 프레임 번호들
          [16, 17, 18, 19, 20, 21], // 아래쪽 이동 애니메이션 프레임 번호들
          [22, 23, 24, 25, 26, 27] // 왼쪽 이동 애니메이션 프레임 번호들
        ],
        die: [40, 41, 42, 43] // 캐릭터가 사망할 때의 애니메이션 프레임 번호들 (사용 x)
      }
    }
  },
  computed: {
    spriteStyle() {
      return {
        width: `${this.frameWidth}px`, 
        height: `${this.frameHeight}px`, 
        backgroundImage: `url(${this.spriteSheet})`, 
        backgroundPosition: `-${this.getCurrentAnimationFrame() * this.frameWidth}px 0px`, // 현재 프레임에 맞게 배경 위치 조정
        backgroundRepeat: 'no-repeat', 
        transform: `scale(${this.scale})`, 
        transformOrigin: 'top left' 
      }
    }
  },
  mounted() {
    this.startAnimation() 
    window.addEventListener('keydown', this.handleKeydown) // 키가 눌릴 때 이벤트 처리기 추가
    window.addEventListener('keyup', this.handleKeyup) // 키에서 손을 뗄 때 이벤트 처리기 추가
  },
  beforeUnmount() {
    this.stopAnimation() 
    window.removeEventListener('keydown', this.handleKeydown) // 키다운 이벤트 리스너 제거
    window.removeEventListener('keyup', this.handleKeyup) // 키업 이벤트 리스너 제거
  },
  methods: {
    startAnimation() {
      this.animationInterval = setInterval(() => {
        if (this.isMoving) {
          // 캐릭터가 움직이고 있을 경우
          this.currentFrame = (this.currentFrame + 1) % this.frames.move[this.direction].length
        } else {
          // 캐릭터가 움직이지 않고 있을 경우
          this.currentFrame = (this.currentFrame + 1) % this.frames.idle[this.direction].length
        }
      }, this.animationSpeed)
    },
    stopAnimation() {
      clearInterval(this.animationInterval)
    },
    getCurrentAnimationFrame() {
      // 현재 방향과 상태(움직임 여부)에 따라 적절한 애니메이션 프레임을 반환
      if (this.isMoving) {
        return this.frames.move[this.direction][this.currentFrame]
      } else {
        return this.frames.idle[this.direction][this.currentFrame]
      }
    },
    handleKeydown(event) {
      // 사용자가 특정 방향키를 눌렀을 때 처리
      switch (event.key) {
        case 'ArrowUp':
          this.direction = 0 // 위쪽 방향으로 설정
          this.currentInput = '↑' // 현재 입력 표시를 위쪽 화살표로 설정
          break
        case 'ArrowRight':
          this.direction = 1 // 오른쪽 방향으로 설정
          this.currentInput = '→' // 현재 입력 표시를 오른쪽 화살표로 설정
          break
        case 'ArrowDown':
          this.direction = 2 // 아래쪽 방향으로 설정
          this.currentInput = '↓' // 현재 입력 표시를 아래쪽 화살표로 설정
          break
        case 'ArrowLeft':
          this.direction = 3 // 왼쪽 방향으로 설정
          this.currentInput = '←' // 현재 입력 표시를 왼쪽 화살표로 설정
          break
        default:
          return // 지정된 키가 아니면 아무런 동작도 하지 않음
      }
      this.isMoving = true // 방향키가 눌리면 움직임 상태로 설정
    },
    handleKeyup() {
      // 사용자가 방향키에서 손을 뗐을 때 처리
      this.isMoving = false // 움직임 상태 해제
      this.currentInput = '' // 현재 입력 표시 초기화
    }
  }
}
</script>

<style scoped>
div {
  overflow: hidden;
}
</style>

1.1. SpriteAnimator.vue 코드 상세 설명

1.1.1. Data

  • currentFrame : 현재 재생 중인 애니메이션 프레임의 인덱스입니다. 이 값은 애니메이션이 진행됨에 따라 변경됩니다.
  • currentInput : 현재 입력된 키
  • direction : 캐릭터가 바라보고 있는 현재 방향을 나타냅니다. 이 값은 0: 위, 1: 오른쪽, 2: 아래, 3: 왼쪽의 숫자 값으로 표현됩니다. 기본값은 2(아래)로 설정되어 있습니다.
  • isMoving : 캐릭터가 현재 움직이고 있는지 여부를 나타내는 불리언 값입니다. 이 값은 true이면 캐릭터가 움직이고 있는 상태를 나타내며, false이면 움직임이 없는 상태를 나타냅니다.
  • frames : 캐릭터의 애니메이션 프레임 번호들을 저장하는 객체입니다.

1.1.2. Lifecycle Hooks

  • mounted와 beforeUnmount 훅에서 애니메이션 시작 및 중지, 그리고 키보드 이벤트 리스너 등록 및 해제를 처리합니다.

1.1.3. Methods

  • startAnimation()
    • 애니메이션을 시작하는 메서드로, setInterval을 사용해 정해진 시간 간격(예: 100ms)으로 애니메이션 프레임을 업데이트합니다.
    • 캐릭터가 움직이고 있는지(isMoving) 여부에 따라 다른 애니메이션 프레임을 사용합니다.
      움직이는 경우 move 프레임을, 그렇지 않은 경우 idle 프레임을 순환하며 보여줍니다.
  • getCurrentAnimationFrame()
    • 현재 애니메이션에서 표시해야 하는 프레임 번호를 계산해 반환합니다.
    • 캐릭터가 움직이고 있는지 여부에 따라 move 또는 idle 상태의 프레임을 반환합니다.
    • 이 값은 spriteStyle 계산 속성에서 사용되어 실제 화면에 표시될 이미지의 위치를 설정하는 데 사용됩니다.
  • handleKeydown()
    • 사용자가 키보드의 방향키(위, 오른쪽, 아래, 왼쪽)를 눌렀을 때 호출됩니다.
    • 입력된 키에 따라 캐릭터의 방향(direction)을 설정하고, currentInput 변수를 업데이트합니다.
    • 캐릭터가 움직이고 있음을 나타내기 위해 isMoving을 true로 설정합니다.
  • handleKeyup()
    • 사용자가 방향키에서 손을 뗐을 때 호출됩니다.
    • isMoving을 false로 설정해 캐릭터의 움직임을 멈추고, currentInput을 빈 문자열로 초기화합니다.

2. 컴포넌트 사용

  • 이제 App.vue 파일을 일부 수정합니다.
  • 제거된 totalFrames를 제외합니다.
<template>
  <div id="app">
    <SpriteAnimator
      :frameWidth="24"
      :frameHeight="24"
      :animationSpeed="100"
      :spriteSheet="spriteSheetPath"
      :scale="2"
    />
  </div>
</template>

<script>
import SpriteAnimator from './components/SpriteAnimator.vue'
import spriteSheetPath from '@/assets/Girl-Sheet.png'

export default {
  name: 'App',
  components: {
    SpriteAnimator
  },
  data() {
    return {
      spriteSheetPath
    }
  }
}
</script>

<style>
#app {
  /* text-align: center; */
}
</style>

3. 실행 결과

  • 실행하면 아래 사진과 같이 동작을 볼 수 있습니다.
  • 방향키에 따라 캐릭터가 해당 방향을 향해 움직이는 것처럼 보입니다.
  • 다음 글에서는 유저의 입력에 따라 캐릭터가 해당 방향으로 움직이는 기능을 구현해보도록 하겠습니다.

0개의 댓글

관련 채용 정보