Numble '다른 색깔 찾기 게임 제작 챌린지' -3 (게임 플레이 로직)

2022. 2. 12. 20:02
반응형

- 현재 성공상태인지(=제한시간 지나기 전), 실패상태인지

- 스코어

- 스테이지

- 남은시간

 

위 정보들을 모두 담고있는 index.tsx에서 함수를 정의하여 다른 컴포넌트들에게 넘겨주는 형태이다.

 

const App: React.FC = () => {

  //필요한 정보들을 useState에 담는다
  const [isSuccess, setIsSuccess] = useState(true);
  const [stage, setStage] = useState(INITIAL_STAGE);
  const [second, setSecond] = useState(INITIAL_TIME);
  const [score, setScore] = useState(INITIAL_SCORE);


  //동작에 따라 위 상태들을 제어하는 함수를 정의한다
  
  //초기화(게임 다시하기 또는 next level 넘어갔을 때)
  const handleInitialize = () => {
    setIsSuccess(true);
    setSecond(INITIAL_TIME);
  };

  //정답 클릭했을 때
  const handleCorrect = useCallback(() => {
    setStage((prev) => prev + 1);
    setSecond(INITIAL_TIME);
  }, []);

 //오답 클릭했을 때
  const handleIncorrect = useCallback(() => {
    setSecond((prev) => prev - 3);
  }, []);

  //점수 올려주기
  const handleSetScore = useCallback(() => {
    setScore((prev) => prev + Math.pow(stage, 3) * stage);
  }, [stage]);
  
  
  ...(타이머 부분은 생략)
  
  return (
    <div className="App">
      {isSuccess ? (
        <div>
          <Status second={second} stage={stage} score={score} />
          <Board
            stage={stage}
            handleSetScore={handleSetScore}
            handleCorrect={handleCorrect}
            handleIncorrect={handleIncorrect}
          />
        </div>
      ) : (
        <Fail handleInitialize={handleInitialize} />
      )}
    </div>
  );
  
}

각 함수들을 useCallback으로 감싸지 않으면 1초에 한 번씩 second가 바뀔 때마다

빙고판이 새로 그려지는 사태가 발생한다...

 

(리액트의 useCallback은 특정 함수를 새로 만들지 않고 재사용할 때 사용하는 hook)

 

 

다만 함수 구현 중 막히는 부분이 있는데,

정답을 클릭했을 때 실행되는 함수인 handleCorrect에서 setScore까지 같이 담고 싶었지만 실패했다.

 

넘블로부터 주어진 점수의 조건은 아래와 같았는데,

 Math.pow(stage, 3) * second

점수 계산에 second의 값이 이용되다보니

남은 시간이 줄어드는 1초마다 빙고판이 rerendering 되는 이슈가 발생했다...

 

 

  const [isClickCorrect, setIsClickCorrect] = useState(false);
  
  
  useEffect(() => {
    if (isClickCorrect) {
      setScore((prev) => prev + Math.pow(stage, 3) * second);
      setIsClickCorrect(false);
    }
  }, [isClickCorrect, second, stage]);

삽질을 반복하며 이런 저런 시도를 해보다가...

 

결국 위와 같이 정답을 클릭하는 순간 isClickCorrect의 값을 true로 바꿔주고,

점수 계산을 한 다음 isClickCorrect를 false로 다시 바꿔주는 부분을 추가해서 해결은 했다.

 

그러나 지저분하고 비효율적으로 느껴진다. 모범 답안이 궁금하다...

 

 

custom hook 이용하여 refactoring 하기

color array를 생성하는 함수가 길다보니, Board.tsx 안에 들어가있는게 지저분해보였다.

useCreateColorArray.ts라는 커스텀 훅을 만들어서 따로 정리했다.

 

//useCreateColorArray.ts

const useCreateColorsArray = (stage: number) => {
  const r = Math.round(Math.random() * 255);
  const g = Math.round(Math.random() * 255);
  const b = Math.round(Math.random() * 255);

  //rgba의 투명도 차이를 인자로 넘겨받는 함수
  const rgabGenerator = (transGap: number) => {
    return `rgba(${r}, ${g}, ${b}, ${0.5 - transGap})`;
  };

  //투명도가 0.5인 main color 생성
  const MAIN_COLOR = rgabGenerator(0);

  //배열의 길이(=빙고판의 칸 개수)
  const ARRAY_LENGTH = Math.pow(Math.round((stage + 0.5) / 2) + 1, 2);

  //빈 배열을 메인컬러로 꽉 채움
  const colorArray = Array.from(Array(ARRAY_LENGTH)).map(
    (x) => `${MAIN_COLOR}`,
  );

  //다른 색상이 배열 중 몇 번째 요소에 들어갈지 선택
  const answerNumber = Math.floor(Math.random() * ARRAY_LENGTH);

  //DIFF_COLOR의 투명도 차이 (+면 진해지고, -면 연해진다. stage가 올라감에 따라 차이가 줄어든다)
  const colorGap = 1 / (stage * 3) + 0.05;

  //메인컬러보다 진하게해줄지(+), 연하게해줄지(-) 랜덤
  const plusOrMinus = Math.round(Math.random()) * 2 - 1;

  const DIFF_COLOR = rgabGenerator(colorGap * plusOrMinus);

  //원래 배열에서 다른 색상으로 요소를 하나 교체한다
  colorArray.splice(answerNumber, 1, DIFF_COLOR);

  return { colorArray, answerNumber };
};

export default useCreateColorsArray;

 

Board 컴포넌트에서 import 해서 사용하는 방식으로 수정했다.

//Board.tsx

import useCreateColorArray from 'hooks/useCreateColorsArray';

const Board: React.FC<Props> = ({...}) => {

...
  const { colorArray, answerNumber } = useCreateColorArray(stage);
...
}

export default Board

 

모호한 변수명 변경

현재 상태가 진행 중인지, 실패상태인지 여부를 표시하는 이름은 원래 isSucess, setIsSucess였는데, 네이밍이 헷갈리는 것 같아 isPlaying, setIsPlaying으로 변경했다.

 

 

남은시간이 이상하게 동작하는 부분 수정

게임 플레이 조건 중, 오답을 눌렀을 시 남은 시간이 3초 줄어든다는 룰이 있었다.

카운트다운이 정상적으로 15, 14, 13... 될 때는 문제가 없지만,

만약 2초 남았을 때 오답을 누르면 남은 시간이 -1초로 보인 후 게임종료가 되는 문제가 있었다.

또 -1초가 떴을 때 오답을 무한 클릭하면 남은 시간도 무한 음수로 줄어든다...

 

이를 방지하기 위해 아래 코드를 추가했는데, 역시 지저분하고 덕지덕지 해보여서 마음에 들지 않는다...

 

  //오답눌러서 3초깎일때, 남은시간이 음수로 넘어가는 현상 방지를 위해 추가
  useEffect(() => {
    if (second < 0) {
      setIsPlaying(false);
    }
  }, [second]);

 

반응형

BELATED ARTICLES

more