카드 짝 맞추기 게임 (feat.Element.animate())

hyoon·2022년 10월 24일
0

Toy Project

목록 보기
1/1
post-thumbnail

고민한 점들

1. 카드 랜덤하게 배치하는 방법

카드를 랜덤하게 배치하기 위해서 index를 random 하게 생성하는 방식을 생각했었다. 하지만 동일한 index 값이 나올 가능성이 있고, 유일한 index 값으로만 구성되게끔 랜덤 값을 생성하기에는 실행 시간이 길어지는 문제가 발생했다.

참고한 자료를 통해서 카드 배열 자체를 sort 메소드를 사용하여 랜덤하게 섞는 방식을 적용했다.

export const CARD_DATA = [
    {img: '🐔', isOpen: false},
    {img: '🐸', isOpen: false},
    {img: '🐥', isOpen: false},
    {img: '🐇', isOpen: false},
    {img: '🦊', isOpen: false},
    {img: '🐳', isOpen: false},
    {img: '🐒', isOpen: false},
    {img: '🐧', isOpen: false},
];

export function generateRandomCardArr (arr) {
    return arr.sort(() => 0.5 - Math.random())
              .map(card => ({...card}));
}

2. 클릭 여부 관리하기

클릭이 금지되어야 하는 경우는 다음과 같다.

  1. 게임 시작 전과 끝

  2. 클릭되어(또는 정답인 경우) 앞면을 보여주고 있는 카드

  3. 클릭된 카드 개수가 2개인 경우


첫번째는 게임 시작 전 후, 끝난 경우를 구분하기 위해 게임의 status를 READY, START, LOSE, WIN 으로 관리하였다.

두번째는 boolean 데이터를 사용하여 클릭 여부를 구분하였다. 클릭된 target의 dataset 속성 값으로 부여한 idx 값을 가져온 후 기존 카드 배열에, 해당하는 idx 원소인 카드 객체를 가져와 isOpen 프로퍼티를 true처리 해주었다.

flipCardToFront (card) {
        this.cardList[card.dataset.idx].isOpen = true;
}

세번째는 클릭된 카드 개수가 2개면 카드의 match 여부를 확인해야 하기 때문에, 해당 작업 동안에는 카드 클릭을 금지해야 한다. 따라서 클릭된 카드를 담은 배열의 개수를 체크해주었다.


3. 비동기로 카드 뒤집는 효과주기

Element.animate()

카드가 뒤집히는 효과를 주기 위해 Element.animate() 메소드를 사용하였다.
해당 메소드는 비동기로 동작하며, 적용할 keyframes와 option을 인자로 전달해주면 animation 객체를 return 한다.

function animate(target, keyframes, option){
    const [front, back] = target.children;
    return (
        front.animate(keyframes[0], option).finished,
        back.animate(keyframes[1], option).finished
    )
}

animation.finished

애니메이션의 동작이 완료되면 카드의 match 여부 확인 또는 클릭을 가능하게 해주는 등과 같은 후 작업의 실행을 보장해주어야 한다.

return 된 animation 객체의 finished 프로퍼티는 애니메이션 작업이 완료된 후 애니메이션 객체를 resolve하는 promise를 반환해준다.


다음은 게임 초반에 모든 카드의 앞면을 보여준 후 다시 뒷면으로 뒤집는 애니메이션 작업 코드이다.

promise.all() 메소드를 사용하여 모든 promise의 이행을 기다렸다가 fulfilled 되었다면 await으로 후 작업의 실행을 보장해주었다.

export function showFrontBackAnimation(cards) {
    let animation = [];
    
    cards.forEach(card => 
        animation.push(animate(card, [show, hide], flipAlterOption))
    )
    return Promise.all(animation)
}


async startGame () {
        const cards = $All('.card-wrapper');
        await showFrontBackAnimation(cards);
        this.status = GAME_STATUS.START;
        this.startTimer();
}

4. 함수는 기능별로 최대한 분리하고 함수명으로 의미를 나타내기

코드를 이해하기 쉽고 구조화하기 위해 최대한 기능별로 분리하려 했다. 함수가 하나의 기능만 할 수 있게끔 코드를 작성하는 데 노력하고 집중을 했는데 확실히 가독성도 좋아짐을 느꼈다. 또한 어떤 작업을 하는 코드인지 역할을 명확히 나타내는 이름으로 함수명을 지으려고 노력했다.

클린하게 코드를 작성하는 방법을 좀 더 배워서 프로젝트의 규모가 커지더라도 유지보수가 쉽고 가독성이 좋은, 사람이 이해하기 쉬운 코드로 작성할 수 있도록 더 공부해서 적용해나가야겠다-!


게임 하러 가기 🎮

https://nahyyun.github.io/card-memory-game

profile
FE Developer

0개의 댓글