React를 사용한 슬라이딩 퍼즐 게임 구현하기

woori kim·2023년 7월 5일
0

react

목록 보기
2/2

소개

이번 포스트에서는 React와 styled-components를 활용하여 슬라이딩 퍼즐 게임을 구현하는 방법에 대해 알아보겠습니다. 슬라이딩 퍼즐은 사용자가 조각을 이동시켜 원래의 이미지를 재구성하는 게임으로, 이 게임을 구현함으로써 React 컴포넌트와 상태 관리, 이미지 처리 등에 대한 이해를 높일 수 있습니다.

프로젝트 구조 설정

- src
  - components
    - Puzzle.tsx
    - PuzzleContainer.tsx
    - PuzzleControls.tsx
    - PuzzlePiece.tsx
    - PuzzleRow.tsx
    - PuzzleUploader.tsx
    - RenderPuzzle.tsx
  - utils
    - util.ts
  - atom
    - atom.ts
  - App.tsx
  - index.tsx

리코일을 이용한 상태관리

Slide Puzzle 게임을 만들기 위해 상태 관리를 위한 Recoil 패키지를 설치하고 설정합니다. Recoil은 React 상태를 관리하기 위한 라이브러리로, 상태를 전역으로 관리하고 컴포넌트 간에 상태를 공유할 수 있게 해줍니다.

  • 리코일 설치

npm install recoil

  • atom.ts파일에 atom을 생성하고 상태를 정의
// atom.ts

import { atom } from "recoil";

export const puzzleStateAtom = atom<number[][]>({
  key: "puzzleState",
  default: [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, null],
  ],
});

export const moveCountAtom = atom<number>({
  key: "moveCount",
  default: 0,
});

export const userImageAtom = atom<string | null>({
  key: "userImage",
  default: null,
});

export const isChallengeStartedAtom = atom<boolean>({
  key: "isChallengeStarted",
  default: false,
});

// 다른 atom들도 추가적으로 정의합니다.

컴포넌트 구현

  • Puzzle.tsx
    • slidePuzzle 게임의 최상위 컴포넌트
  • **PuzzleContainer.tsx**
    • Slide Puzzle 게임의 컨테이너 역할을 하는 PuzzleContainer 컴포넌트를 구현합니다.
  • **PuzzleControls.tsx**
    • Slide Puzzle 게임의 컨트롤러 역할을 하는 PuzzleControls 컴포넌트를 구현합니다. 컴포넌트의 주요 기능은 다음과 같습니다:
      • 퍼즐 리셋 (resetPuzzle): 리셋 버튼을 클릭하면 호출되는 함수입니다. 이 함수는 퍼즐 상태를 초기 상태로 되돌리고, 이동 횟수를 0으로 설정하며, 도전 중 및 완료 상태를 초기화합니다.
      • 퍼즐 셔플 (shufflePuzzle): 셔플 버튼을 클릭하면 호출되는 함수입니다. 이 함수는 퍼즐을 무작위로 섞어 새로운 퍼즐 상태를 생성합니다. 또한 이동 횟수를 0으로 설정하고, 도전 중 상태로 설정하며, 완료 상태를 초기화합니다.
        • 퍼즐 셔플은 Fisher-Yates 알고리즘을 사용하여 구현되었습니다.
          1. originalPuzzleState 배열을 펼쳐서 각 피스의 인덱스와 이미지를 가진 객체로 변환합니다.
          2. 빈 피스의 인덱스를 찾습니다. 빈 피스는 undefined 또는 null 값으로 표시됩니다.
          3. 빈 피스의 인덱스가 1인 경우, 마지막 피스와 빈 피스의 위치를 교환합니다. 이렇게 함으로써 빈 피스를 맨 뒤로 이동시킵니다.
          4. 피스 배열을 Fisher-Yates 알고리즘을 사용하여 무작위로 섞습니다. 배열의 각 요소를 뒤섞기 위해 현재 요소와 랜덤하게 선택한 다른 요소를 교환합니다.
          5. 인버전(inversions) 수를 다시 계산합니다. 인버전은 퍼즐 조각들의 순서가 올바르게 배열되어 있는지를 나타내는 값으로, 순서가 올바르지 않으면 홀수 개의 인버전이 존재합니다.
          6. 인버전 개수가 홀수인 경우에만 셔플을 종료합니다. 홀수 개의 인버전을 가지고 있으면 퍼즐이 완전히 섞인 상태가 되므로 종료합니다.
          7. 셔플된 퍼즐 배열을 생성합니다. 이때 각 피스의 이미지를 새로운 배열로 추출하여 셔플된 퍼즐 상태를 생성합니다.
    • 해당 컴포넌트를 구현할때 인덱스의 값이 아닌 이미지의 데이터를 가지고 비교하는 문제로 셔플이 안되거나 셔플후 완성이 안되는 버그가 발생하였습니다. 이를 해결하기 위해 배열에 인덱스 값을 추가하여 인덱스값으로 비교하도록 수정하였습니다.
  • **PuzzlePiece.tsx**
    • Slide Puzzle 게임의 퍼즐 조각을 나타내는 PuzzlePiece 컴포넌트를 구현합니다.
    • 해당 컴포넌트에서는 피스의 크기와 이동가능 여부등을 설정할 수 있습니다. 해당 컴포넌트는 이동이 가능한지를 판단해 이동이 가능한 경우에만 조각을 이동할 수 있습니다.
  • **PuzzleRow.tsx**
    • Slide Puzzle 게임의 퍼즐 행을 나타내는 PuzzleRow 컴포넌트를 구현합니다.
  • **PuzzleUploader.tsx**
    • Slide Puzzle 게임에서 이미지 업로드를 담당하는 PuzzleUploader 컴포넌트를 구현합니다.
      • 이미지 파일 선택 이벤트 처리:
        • handleImageUpload 함수가 이미지 파일 선택 이벤트를 처리합니다.
        • 선택한 이미지 파일은 event.target.files를 통해 가져옵니다.
      • 이미지 리사이징:
        • 선택한 이미지 파일을 resizeImage 함수에 전달하여 리사이징합니다.
        • resizeImage 함수는 이미지를 지정된 최대 너비와 높이에 맞게 조정하고, 리사이즈된 이미지의 데이터 URL을 반환합니다.
        • 리사이즈된 이미지는 퍼즐 조각의 크기를 조정하고 일관성을 유지하는 데 사용됩니다.
        • resizeImage 함수는 Promise를 사용하여 비동기적으로 이미지 리사이징 작업을 처리합니다.
      • 이미지 분할:
        • 리사이즈된 이미지는 splitImage 함수를 통해 퍼즐 조각으로 분할됩니다.
        • splitImage 함수는 canvas를 사용하여 이미지를 3x3 격자로 분할하고, 각 조각의 데이터 URL과 인덱스를 반환합니다.
        • 분할된 조각은 pieces 배열에 객체 형태로 저장됩니다.
  • **RenderPuzzle.tsx**
    • Slide Puzzle 게임에서 퍼즐을 렌더링하는 RenderPuzzle 컴포넌트를 구현합니다.
  • **util.ts**
    • Slide Puzzle 게임에서 사용되는 유틸리티 함수를 구현합니다.

기능 설명

  1. 이미지 업로드: 사용자는 이미지 파일을 업로드하여 퍼즐의 이미지로 사용할 수 있습니다.
  2. 퍼즐 셔플: 퍼즐을 섞어 다시 시작할 수 있습니다.
  3. 퍼즐 이동: 사용자는 클릭을 통해 퍼즐 조각을 이동시킬 수 있습니다.
  4. 이동 횟수 카운트: 사용자는 현재까지의 이동 횟수를 확인할 수 있습니다.
  5. 퍼즐 완료 확인: 퍼즐이 완료되면 완료 메시지가 표시됩니다.

마무리

이번 포스트에서는 React를 사용하여 슬라이딩 퍼즐 게임을 구현한 방법에 대해 작성해 보았습니다. 이미지를 불러와서 셔플해서 슬라이드하는 간단한 퍼즐이라고 생각했는데. 생각보다 배열구조를 신경써야하는 부분이 많았던것같아요. 처음엔 useState를 이용해서 관리했는데 점점 파일이 커지면서 컴포넌트를 나누다보니 확실히 리코일을 쓰는게 장점이 있겠다고 생각했고. 좋은 샘플코드가 되었다고 생각합니다.

자세한 코드는 해당리포지토리에서 참고 가능합니다.
https://github.com/woori3104/slidePuzzle.github.io

0개의 댓글