2048 게임 만들기 - 그리드 CSS, 타일 생성 로직

지은·2023년 6월 27일
0

🚂 토이 프로젝트

목록 보기
5/10
post-custom-banner

4 x 4 Grid 만들기

display 속성을 grid로 설정해주고,
grid-template-rowsgrid-template-columns 속성을 이용해 가로 4, 세로 4 너비와 높이가 20vmin인 그리드를 만들어준다.

Grid.js

const Board = styled.div`
  display: grid;
  grid-template-rows: repeat(4, 20vmin);
  grid-template-columns: repeat(4, 20vmin);
  gap: 2vmin;
  border-radius: 1vmin;
  padding: 2vmin;
  position: relative; // 나중에 위에 타일을 올려야하므로 relative로 설정
`;

const Cell = styled.div`
  background-color: lightgray;
  border-radius: 1vmin;
`;

export default function Grid() {
  return (
    <Board>
      {new Array(16).fill(0).map((item) => (
        <Cell />
      ))}
    </Board>
  );
}

vmin & vmax

vmin 단위는 처음 사용해보는데, v는 viewport를 의미하며,
vmax은 viewport의 가로, 세로 너비 중 최대 너비, vmin은 viewport의 가로, 세로 너비 중 최소 너비를 말한다.

  • vmax은 viewport의 가로, 세로 너비 중에 큰 너비를 기준으로 하고,
  • vmin은 viewport의 가로, 세로 너비 중에 작은 너비를 기준으로 한다.

나중에 4 * 4가 아닌 다른 크기의 그리드를 만들 수도 있기 때문에 GRID_SIZE를 변수로 선언해주고, cell-sizecell-gap도 반복해서 사용할 것이기 때문에 변수로 선언해준다.

완성된 코드

const GRID_SIZE = 4;

const Board = styled.div`
  --cell-size: 20vmin;
  --cell-gap: 2vmin;
  display: grid;
  grid-template-rows: repeat(${GRID_SIZE}, var(--cell-size));
  grid-template-columns: repeat(${GRID_SIZE}, var(--cell-size));
  gap: var(--cell-gap);
  border-radius: 1vmin;
  padding: var(--cell-gap);
  position: relative;
`;

const Cell = styled.div`
  background-color: lightgray;
  border-radius: 1vmin;
`;

export default function Grid() {
  return (
    <Board>
      {new Array(GRID_SIZE * GRID_SIZE).fill(0).map((_, i) => (
        <Cell key={i} />
      ))}
    </Board>
  );
}

타일 만들기

이제 그리드 위에 올릴 Tile을 만들어주자.
Tile은 매개변수로 xy 값을 받아 해당하는 위치에 렌더링되도록 top, left 속성에 값을 계산해줘야 한다.

먼저 position 속성을 absolute로 설정해주고,
top은 입력받은 y값에 (셀의 너비 + 셀 갭) 만큼 곱해주면 되고,
left는 입력받은 x값에 (셀의 너비 + 셀 갭) 만큼 곱해주면 된다.

하지만 맨 바깥 가장자리도 더해줘야 하기 때문에 마지막에 셀 갭을 한 번 더 더해줘야 한다. ➡️ calc(x * (셀의 너비 + 셀 갭) + 셀 갭)

Tile.js

const Tile = styled.div`
  --x: 1; // 일단 CSS 내에 변수로 x, y 값을 임의로 설정해주었다.
  --y: 2;
  width: var(--cell-size);
  height: var(--cell-size);
  position: absolute;
  top: calc(var(--y) * (var(--cell-size) + var(--cell-gap)) + var(--cell-gap));
  left: calc(var(--x) * (var(--cell-size) + var(--cell-gap)) + var(--cell-gap));
  background-color: pink;
  border-radius: 1vmin;
`;

타일에 애니메이션 효과 주기

@keyframes을 이용해 show라는 애니메이션을 만들고, 어떤 효과를 실행할건지 구체적인 단계를 작성해준다.
그리고 타일의 animation 속성에 만든 show 애니메이션을 설정해준다.

const Tile = styled.div`
  // ...생략
  background-color: hsl(200, 50%, 20%);
  color: hsl(200, 25%, 80%);
  animation: show 200ms ease-in-out; // show라는 애니메이션을 0.2초 동안 ease-in-out하게 보여준다.

  @keyframes show {
    0% { // 투명도 50%, 0의 크기에서 시작해서 진해지며 커지는 효과
      opacity: 0.5;
      transform: scale(0);
    }
  }
`;


hsl()

hsl() 함수는 빨강(R), 녹색(G) 및 파랑(B) 값 대신 색조 (hue), 채도 (saturation)명도(lightness) 값을 받는다.

  • 색조 (Hue): 0 ~ 360 사이의 값
  • 채도 (Saturation): 색상이 얼마나 담겼는지를 나타내는 0 ~ 100% 사이의 값
    여기서 0은 회색 음영으로 표시되고, 100% 는 쨍한 채도로 표현된다.
  • 명도 (Lightness): 색상이 얼마나 밝은지를 나타내는 0 ~ 100% 사이의 값
    여기서 0은 빛이 없고(검은색), 100% 는 완전한 빛(흰색)으로 표현된다.

랜덤한 위치에 타일 생성하기

Grid.js

import { getInitialGrid } from '../util/tile';

function Grid() {
	const [tileList, setTileList] = useState(getInitialGrid);
	// ...생략
}

util/tile.js

게임이 시작하면 랜덤한 위치에 타일 2개가 생성된다.
getInitialGrid()initialTileList라는 빈 배열을 생성하고, makeTile() 함수를 이용해 타일을 생성한 후 initialTileList에 푸쉬해준다.
그러면 타일이 2개가 담긴 타일 리스트가 반환되며 화면에 렌더링된다.

// 첫 타일 2개 생성
export function getInitialGrid() {
	const initialTileList = [];
	const tile1 = makeTile(initialTileList);
	tileList.push(tile1);
	const tile2 = makeTile(initialTileList);
	tileList.push(tile2);

	return initialTileList;
}

타일 생성 함수

makeTile() 함수는 타일을 만들어 반환하는 함수이다.
이미 타일이 있는 곳에 타일을 생성하면 안되므로, 매개변수로 타일리스트를 받아서 새롭게 만든 타일이 중복되지 않는지 체크한다.
x와 y에는 0, 1, 2, 3 중 랜덤한 값이 들어가고, 새롭게 생성된 타일의 value은 2로 지정해준다.

let currentId = 0;

// 새로운 타일 만드는 함수
export function makeTile(tileList) {
	let tile;
	while (!tile || checkCollision(tileList, tile)) {
		tile = {
			id: currentId++,
			x: Math.floor(Math.random() * 3),
			y: Math.floor(Math.random() * 3),
			value: 2,
		};
	}
	return tile;
}

충돌 체크 함수

checkCollision() 함수는 tileList와 타일을 매개변수로 받아, x와 y값이 겹치는 타일이 있는지 확인하고 true나 false를 반환한다.

function checkCollision(tileList, tile) {
	return tileList.some((item) => item.x === tile.x && item.y === tile.y);
}

profile
블로그 이전 -> https://janechun.tistory.com
post-custom-banner

4개의 댓글

comment-user-thumbnail
2023년 6월 28일

우와 2048 게임구현이 이런식으로 가능하군용!
저도 한번 만들어보고 싶네요 ㅎㅎ 완성작 기대됩니다!

답글 달기
comment-user-thumbnail
2023년 7월 1일

ㅎㅎㅎ 참신하고 재밌네요 고생하셨습니다

답글 달기
comment-user-thumbnail
2023년 7월 2일

우왓 재밌네요 ㅋㅋ 고생하셨습니당

답글 달기
comment-user-thumbnail
2023년 7월 2일

우와 먼가 복잡한데 기대됩니다 ><

답글 달기