본문 바로가기
react

[Numble] 다른 색깔 찾기 게임

by HomieKim 2022. 1. 27.

넘블에서 진행한 다른 색깔 찾기 게임 챌린지 참여!

https://www.numble.it/45cee9d3-49ad-4f67-9d2a-14607c2eeba7

 

[React] 상태관리 라이브러리를 사용하지 않고 다른 색깔 찾기 게임 제작

시간은 금! 챌린지를 딱 4줄로 요약해볼게요 😉

www.numble.it

 

✔ 구현 요구사항

  1. 각 스테이지마다 Math.pow(Math.round((stage + 0.5) / 2) + 1, 2)개의 사각형이 표시됨
  2. 한 스테이지의 제한 시간은 15초
  3. 스테이지 바뀔 때 마다 random으로 사각형의 색상을 적용, 하나만 다른색깔 적용(정답 사각형)
  4.  정답 사각형을 클릭한 경우 다음 스테이지로 넘어가고 Math.pow(stage, 3) * 남은시간 만큼의 점수가 누적, 오답을 클릭하면 제한시간 -3
     
  5. stage가 올라갈 수록 정답과 오답의 색상 차이가 줄어듬

✔ 사용한 라이브러리

  • CRA : 초기 프로젝트 세팅
  • emotion : 스타일 컴포넌트 사용(각 아이템 마다 크기와 색상을 props로 받아옴)

 

주요 구현 로직

  • App.js에서 상태를 정의하고 관리
  const [answer, setAnswer] = useState();
  const [answerColor, setAnswerColor] = useState();
  const [baseColor, setBaseColor] = useState();
  const [isPlaying, setIsPlaying] = useState(true);
  const [score, setSocore] = useState(0);
  const [stage, setStage] = useState(1);
  const [time, setTime] = useState(15);
  const [itemList, setItemList] = useState([]);

- 게임 내에서 바뀌어야 되는 부분을 상태로 정의하고 관리 함

 

  • 프로그램 시작 시 초기 세팅
 useEffect(() => {
    setIsPlaying(true);
    // item 갯수
    const itemCnt = Math.pow(Math.floor((stage + 1) / 2 + 1), 2);
    // 0~ itme갯수 -1 사이의 숫자 중에서 난수 생성 -> answer 값으로 사용
    const ans = Math.floor(Math.random() * itemCnt);
    setAnswer(ans);
    const baseColor = getBaseColor();
    const getAnsColor = getAnswerColor(stage, baseColor);
    console.log('baseColor : ', baseColor);
    console.log('answerColor : ', getAnsColor);
    setAnswerColor(getAnsColor);
    setBaseColor(baseColor);
    const tmpList = [];
    for (let i = 0; i < itemCnt; i++) {
      tmpList.push(i);
    }
    setItemList(tmpList);
  }, [stage, isPlaying]);

  useEffect(() => {
    if (isPlaying) {
      let timer = setInterval(() => {
        setTime(time - 1);
      }, 1000);
      if (time <= 0) {
        clearInterval(timer);
        setTimeout(() => {
          setTime(15);
          alert(`GAME OVER! \n 스테이지 : ${stage}, 점수 : ${score}` );
          setStage(1);
          setIsPlaying(false);
        }, 1000);
      }
      return () => clearInterval(timer);
    }
  }, [time, isPlaying]);

- 시작 시 사각형을 렌더링 하는 것을 itemList에 스테이지 마다 item 갯 수 만큼 배열로 관리 -> 이건 item의 key값으로 사용

- setInterval 이용해서 타이머 구현 스테이지 끝나고 다시 재시작하기위해 isPlaying 상태 정의

 

  • 랜덤 컬러 지정 로직
    const getBaseColor = () => {
      const ran1 = Math.floor(Math.random() * 255 + 1);
      const ran2 = Math.floor(Math.random() * 255 + 1);
      const ran3 = Math.floor(Math.random() * 255 + 1);
    
      return `rgb(${ran1}, ${ran2}, ${ran3})`;
    }
    
    const randomCalc = (num, ranNum) => {
      const flag = Math.round(Math.random());
      if(flag == 1){
        return ranNum + num;
      }else{
        return ranNum - num;
      }
    }
    
    const getAnswerColor = (stage, baseColor)=> {
      const colorArr = baseColor.slice(4,-1).split(',');
      const num = Math.floor(Math.random() * (100-stage)+10);
    
      const ran1 = randomCalc(num, parseInt(colorArr[0]));
      const ran2 = randomCalc(num, parseInt(colorArr[1]));
      const ran3 = randomCalc(num, parseInt(colorArr[2]));
      console.log('num : ', num);
      return `rgb(${ran1}, ${ran2}, ${ran3})`;
      
    }​

- answer컬러를 어떻게 지정할 지 고민을 좀 했음 내가 사용한 방법은

1. 우선 baseColor를 생성 
2. 생성한 baseColor를 받아와서 컬러 값만 배열로 생성
3. 10~110 사이의 숫자 랜덤 생성 이 범위는 스테이지 올라갈 수록 점점 줄어듬
4. baseColor와 생성한 랜덤 숫자를 더하거나 혹은 빼거나를 랜덤으로 생성 -> 3번 반복해서 rgb값 생성

 

  • 사각형 클릭 시 점수 처리
      const onSelect = useCallback((id) => {
        if (id === answer) {
          // 정답일 때
          const newScore = score+(stage * stage * time);
          const newStage = stage + 1;
          setSocore(newScore);
          setStage(newStage);
          setTime(15);
        } else {
          // 아닐 때
          if (time > 3) {
            setTime(time - 3);
          } else {
            setTime(0);
          }
        }
      },[time, itemList]);
    - 오답 클릭시 빠르게 여러번 클릭하면 점수가 0에서 안끝나고 -1, -2 이렇게 끝나서 0으로 끝나게 만들고 useEffect보면 setTimeout으로 1초 있다가 종료하게 만들었음

 

  • App.js 에서 주요 로직을 구현하고 props로 전달해서 header와 Board 컴포넌트를 렌더링
  • 사각형 크기만 조절해서 렌더링 하면되서 Container랑 Item 컴포넌트 분리안하고 Board 컴포넌트에 한번에 작성함

 

✔ 어려웠던점 / 개선방안

  • 사각형 렌더링 하는데 자동으로 사이즈를 조절할 수 있는 방법이 없을까 고민했는데 좋은 방법으로 구현하지 못했던 것 같음 개발자 도구 보고 Board Container 사이즈를 360 * 360으로 고정하고 stage 값을 받아와서 스테이지 마다 사각형의 가로 세로를 직접 계산해 줬음
    const getLength = (stage) => {
        const divide = Math.floor(((stage+1)/2) + 1);
        const rst = (360 / divide) - 4;
        return rst;
    };​
  • 컴포넌트 최적화 하는 부분을 개선해야함
  • timer 1초마다 Board 컴포넌트에 있는 하위 item이 전부 렌더링 됨 React.memo로 해결 하려 해봤는데 그대로 1초마다 계속 렌더링 된다

 

전체 코드!

https://github.com/HomieKim/React_PlayGround/tree/main/diff-color-game

댓글