본문 바로가기
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

댓글