Numble '다른 색깔 찾기 게임 제작 챌린지' -2 (빙고판 구현, random color array 생성)
스테이지 별로 빙고판(cell) 개수 맞춘 컴포넌트 출력
빙고판에서 셀의 개수는
Math.pow(Math.round((stage + 0.5) / 2) + 1, 2)
개여야 한다는 조건이 미리 주어져있었다.
* Math.pow함수는 주어진 밑 값을 주어진 지수 값으로 거듭제곱한 숫자 값이다.
위의 식에서는 Math.round(( stage + 0.5 ) / 2) + 1)의 값을 2제곱 하라는 의미인 것.
즉, Math.round(( stage + 0.5 ) / 2) + 1)는 빙고판의 한 변의 개수가 된다.
// Board.tsx
// styled-components를 이용해 div가 한 변(side)을 props로 받을 수 있도록 한다
const Container = styled.div<{ side: number }>`
display: grid;
grid-template-columns: ${({ side }) => `repeat(${side}, 120px)`};
grid-template-rows: ${({ side }) => `repeat(${side}, 120px)`};
grid-gap: 5px;
`;
const Board: React.FC = () => {
const side = Math.round((stage + 0.5) / 2) + 1;
...
}
grid display와 grid-template 속성을 이용해서 특정한 값을 프로퍼티로 받아 출력하는 컴포넌트를 만들었다.
예를 들어 1스테이지에서는 한 변의 길이가 2이고 셀의 총 개수가 4개인 정사각형 빙고판이 출력되는 것이다.
cell에 하나만 다른 색깔 넣기
이제 위에서 만든 빙고판의 각 셀들에 색상을 채워넣어야 한다.
메인 컬러를 랜덤으로 정하고, 다른 색상 1개에는 약간의 차이가 있도록 해야 한다.
(스테이지가 올라갈수록 색상의 차이값이 적어져서, 갈수록 육안으로 구분하기 힘들어져야 한다)
처음 랜덤 컬러를 만들어내는 로직을 찾아서 적용했던 건 아래의 방법이었다.
const randomColor = Math.floor(Math.random()*16777215).toString(16);
위 함수의 결과값으로는 '#26f0d4' 와 같은 Hex Color Code가 반환된다.
이 값을 Polished 스타일 유틸 함수의 lighten, darken 등을 이용해 어떻게 변환해보려고 했으나 생각처럼 잘 되지 않았다.
이걸 해결하려고 계속 붙잡고 있는 것보다 더 좋은 방법은 없을까... 하다가 떠오른 것!
hex 코드가 아닌 rgba 값을 이용하는게 훨씬 쉽게 구현할 수 있을 것 같은데..?
(평소에 hex code만 주로 이용하다보니 RGBA를 사용한다는 생각 자체를 하지 못했다..)
const r = Math.round(Math.random() * 255);
const g = Math.round(Math.random() * 255);
const b = Math.round(Math.random() * 255);
r, g, b 값이 될 각각의 숫자를 0~255 사이에서 랜덤으로 뽑아낸 뒤,
rgbg generator라는 이름의 함수를 만들고 메인컬러 변수를 생성한다.
이 때 main color의 투명도는 중간값인 0.5이다.
//rgba의 투명도 차이를 인자로 넘겨받는 함수
const rgabGenerator = (transGap: number) => {
return `rgba(${r}, ${g}, ${b}, ${0.5 - transGap})`;
};
//투명도가 0.5인 main color 생성
const MAIN_COLOR = rgabGenerator(0);
다음으로는 빙고판의 칸 개수만큼의 길이를 가진 빈 배열을 만들고,
배열의각 요소들을 위에서 생성한 main color로 채운다.
//배열의 길이(=빙고판의 칸 개수)
const ARRAY_LENGTH = Math.pow(Math.round((stage + 0.5) / 2) + 1, 2);
//빈 배열을 메인컬러로 꽉 채움
const colorArray = Array.from(Array(ARRAY_LENGTH)).map(
(x) => `${MAIN_COLOR}`,
);
만약 변수 MAIN_COLOR가 'rgba(5,100,200,0.5)' 이고, 배열의 길이가 4라면
위의 코드를 실행했을 때 나오는 결과는
colorArray = ['rgba(5,100,200,0.5)', 'rgba(5,100,200,0.5)', 'rgba(5,100,200,0.5)', 'rgba(5,100,200,0.5)']가 되는 것이다.
main color와 diff color(정답이 될 색상)의 차이는 colorGap이라는 변수로 설정했다.
난이도 조정을 위해 수식에 이것저것 시도해보다가.. 15레벨이 되면 0.07 차이가 나게 되니 저 정도가 적당해보였다.
메인 컬러보다 연하게 해줄지, 진하게 해줄지는 +와 - 중 랜덤으로 고르게 하여 rgbaGenerator로 넘겨준다.
//DIFF_COLOR의 투명도 차이 (+면 진해지고, -면 연해진다. stage가 올라감에 따라 차이가 줄어든다)
const colorGap = 1 / (stage * 3) + 0.05;
//메인컬러보다 진하게해줄지(+), 연하게해줄지(-) 랜덤
const plusOrMinus = Math.round(Math.random()) * 2 - 1;
const DIFF_COLOR = rgabGenerator(colorGap * plusOrMinus);
다음으로는 위에서 만든 DIFF_COLOR가 배열의 몇 번째에 들어갈지 정하는 로직이다.
//다른 색상이 배열 중 몇 번째 요소에 들어갈지 선택
const answerNumber = Math.floor(Math.random() * ARRAY_LENGTH);
//원래 배열에서 다른 색상으로 요소를 하나 교체한다
colorArray.splice(answerNumber, 1, DIFF_COLOR);
위의 코드까지 실행하고 나면 아래와 같이 원하는 결과가 나오게 된다.
Board.tsx 컴포넌트의 전체 코드는 다음과 같다(위 설명에서 생략한 부분 포함).
import React from 'react';
import styled from 'styled-components';
const Container = styled.div<{ side: number }>`
display: grid;
grid-template-columns: ${({ side }) => `repeat(${side}, 120px)`};
grid-template-rows: ${({ side }) => `repeat(${side}, 120px)`};
grid-gap: 5px;
`;
const Cell = styled.div<{ bgColor: string }>`
width: 100%;
height: 100%;
background-color: ${({ bgColor }) => bgColor};
`;
type Props = {
stage: number;
handleNextStage: () => void;
};
const Main: React.FC<Props> = ({ stage, handleNextStage }) => {
//빙고판 한 면의 길이
const side = Math.round((stage + 0.5) / 2) + 1;
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);
const handleAnswer = (idx: number) => {
if (idx === answerNumber) {
handleNextStage();
} else {
console.log('X');
}
};
return (
<Container side={side}>
{colorArray.map((color, idx) => (
<Cell bgColor={color} onClick={() => handleAnswer(idx)} />
))}
</Container>
);
};
export default React.memo(Main);
'한 걸음 > React & Next.js' 카테고리의 다른 글
Numble '다른 색깔 찾기 게임 제작 챌린지' -4 (배포) (0) | 2022.02.13 |
---|---|
Numble '다른 색깔 찾기 게임 제작 챌린지' -3 (게임 플레이 로직) (0) | 2022.02.12 |
노마드코더 영화 앱 만들기(Next.js) (0) | 2022.02.11 |
Numble '다른 색깔 찾기 게임 제작 챌린지' -1 (개발순서, 컴포넌트 구조, 카운트다운 로직구현) (0) | 2022.02.07 |
[React, typescript] 다른 컴포넌트에 props로 함수 넘겨주기 (1) | 2022.02.06 |