Binary Journey

[React][Clone] Chess 게임 만들기 - 3. 체스말 움직이기 본문

React

[React][Clone] Chess 게임 만들기 - 3. 체스말 움직이기

binaryJournalist 2021. 2. 10. 12:52
반응형

 

이전글

2021/02/10 - [React] - [React][Clone] Chess 게임 만들기 (single-player) - 2. (2) 체스판 모양 갖추기, require 로 image 파일 가져오기, default 의 필요성

 

[React][Clone] Chess 게임 만들기 (single-player) - 2. (2) 체스판 모양 갖추기, require 로 image 파일 가져오기

이전 글 2021/02/09 - [React] - [React][Clone] Chess 게임 만들기 (single-player) - 2. (1) board settings [React][Clone] Chess 게임 만들기 (single-player) - 2. (1) board settings 이전 글 2021/02/09 -..

binaryjourney.tistory.com

 

참고한 영상

www.youtube.com/watch?v=kBR7pDLcC3A&t=2462s

 

 

사실 영상 목차를 안 봤는데 정말로 다음 차례가 "Moving the pieces" 였다.

 

react-dnd (drag and drop) 라이브러리를 이용하여 체스말을 움직여보자..!

 

 

 

 

 

나는 몰랐지만 react-dnd 에도 hook이 있다...!

 

useDrag 라는 훅을 쓸 것이다.

 

useDrag 가 있으면 useDrop 도 있을까..?

(있음, 궁금해서 확인해 봄)

 

 

 

useDrop

 

 

 

 

 

Piece 에 react-dnd 의 useDrag 를 import 한다.

 

 

// /src/Piece.js

// (중략)
import { useDrag } from 'react-dnd';

function Piece({ piece: { type, color } }) {
    
    const [, drag] = useDrag({
        item: { type: 'piece', id: `${type}_${color}`}
    });

    const pieceImg = require(`./assets/${type}_${color}.png`).default;

    return (
        <div className="piece-container" ref={drag}>
        

 

 

영상에서는 useDrag를 쓸 때 item, type, id는 무조건 (mandatory라고 강조함) useDrag를 쓸 때 필요하다고 말하였다.

이건 나중에 react-dnd 홈페이지 가서 더 알아봐야겠다.

 

 

 

 

여기까지 하면 이미지가 예쁘게? 깔끔하게 떼서 이동되지만 문제는 뒤에 square까지 같이 붙어서 이동한다.

그래서 DragPreviewImage 를 import 하여 사용해야 한다.

 

 

// /src/Piece.js

import { useDrag, DragPreviewImage } from 'react-dnd';

// ...
    const [, drag, preview] = useDrag({
        item: { type: 'piece', id: `${type}_${color}`}
    });
    
// ...

    return (
        <>
            <DragPreviewImage connect={preview} src={pieceImg} />
            <div className="piece-container" ref={drag}>

 

 

useDrag hook을 받는 array변수에 preview를 추가하고

DragPreviewImage 컴포넌트를 배치한 다음 추가한 preview와 pieceImg를 property 로 넣어준다

그리고 <></> fragment로 DragPreviewImage와 <img> 태그를 감싸주었다.

 

 

말을 옮겨보면 뒤에 상자는 없어지고 유령처럼 떠다닌다.

 

 

말이 preview지 뭔가 잔상을 남기는 효과 같다.

 

 

 

말을 옮기면 원래 있던 위치는 텅 비어야 하는데 계속 말이 남아있다. 이를 해결해보겠다.

 

 

 

// /src/Piece.js

// ...

    const [{ isDragging }, drag, preview] = useDrag({
        item: { type: 'piece', id: `${type}_${color}`},
        collect: (monitor) => ({ isDragging: !!monitor.isDragging() })
    });
    
    // ...
    
    return (
		{/*...*/}
            <div className="piece-container" ref={drag} style={{ opacity: isDragging ? 0 : 1}}>

 

useDrag 를 받는 array변수에 { isDragging } 이라는 객체 변수를 추가해주고

useDrag 안에 들어가는 값 중 collect 값을 변경하여 넣어주는 것 같다.

이는 홈페이지에서 hook이 무슨 역할을 하고 어떻게 사용하는지 더 파악해봐야 할 것 같다.

 

그리고 변경사항이 반영되어 나온 isDragging 을 style에 조건부와 함께 넣어준다.

 

 

컴파일된 화면을 보면

 

drag하고 난 자리에 말이 없고 빈자리만 남아있는 걸 볼 수 있다.

 

 

isDragging

 

 

 

이제 말을 들어올리는 데까지 했으니 놓는 것도 진행할 것이다.

 

BoardSquare.js 에 react-dnd 의 useDrop 을 import 한다.

 

// /src/BoardSquare.js

import { useDrop } from 'react-dnd';

 

 

옮기는 위치에 drag한 값 (useDrag에서 넣어준 item 부분)

    const [{ isDragging }, drag, preview] = useDrag({
        item: { type: 'piece', id: `${type}_${color}`},
        collect: (monitor) => ({ isDragging: !!monitor.isDragging() })
    });

 

 

type'piece'id: `${type}_${color}`} << 이 값이 잘 들어오는지 콘솔로 먼저 확인부터 해보겠다.

콘솔로 확인할 부분은 useDrop 에 들어갈 drop 부분에 넣어주면 된다.

 

 

// /src/BoardSquare.js

// ...

function BoardSquare({piece, black}) {

    const [ , drop] = useDrop({
        accept: 'piece',
        drop: (item) => console.log(item)
    });

    return (
        <div className="board-square" ref={drop}>
            <Square black={black} >
                { piece && <Piece piece={piece} /> }

 

 

코드 작성 뒤 말들을 움직여 보며 콘솔창을 확인해보면

 

checkconsole

 

 

(나이트-퀸-킹-폰즈 움직여봄)

 

 

이제 움직인 위치에 말을 놓을텐데 필요한 함수를 만들어야 한다.

 

 

먼저 Board 컴포넌트에 위치를 알려줄 함수를 작성한다. (isBlack 함수 밑에 작성하면 됨)

 

// /src/Board.js

    function getPosition (i) {
        const { x, y } = getXYPosition(i);
        const letter = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'][x];
        return `${letter}${y + 1}`;
    }

 

letter 은 체스판의 가로 자리(x축)를 의미하고

y는 1부터 시작하도록 1을 더해 return 해주는 것 같다.

 

 

만든 getPosition 함수는 BoardSquare에 property로 넘겨준다.

 

<BoardSquare piece={piece} black={isBlack(index)} position={getPosition(index)} />

 

 

그리고 Game.js 에 move 라는 함수를 만들어준다.

 

// /src/Game.js

export function move (from, to) {
    // 자리값을 확인하고 싶다면
    // console.log(from, to)
    const legalMove = chess.move({ from, to });
    if (legalMove) {
        gameSubject.next({ board: chess.board() });
    }
}

 

내 생각에는 chess.js 에 말과 체스 규칙이 다 들어가있는 것 같다.

그래서 저 move 를 통해서 유효한 움직임인지 파악하여 true/false 값을 주고

유효하면 말을 옮기고 그 상황을 App.js 구독한 지점에서 파악하여 업데이트 해주는 것 같다.

 

 

 

만든 move 함수는 BoardSquare에 import 하여 아까 콘솔창에서 값을 확인한 자리에 넣어준다.

 

// /src/BoardSquare.js

import React from 'react';
import Square from './Square';
import Piece from './Piece';
import { useDrop } from 'react-dnd';
import { move } from './Game';

function BoardSquare({piece, black, position}) { // position property 추가

    const [ , drop] = useDrop({
        accept: 'piece',
        drop: (item) => move() // 변경된 자리
    });

    return (
        <div className="board-square" ref={drop}>
            <Square black={black} >
                { piece && <Piece piece={piece} position={position} /> } {/* position 추가 */}
            </Square>
        </div>
    );
}

export default BoardSquare;

 

 

move 함수는 from 과 to 에 위치를 input으로 받아야 하는데 없으므로 Board에서 받은 property position을 Piece 컴포넌트까지 넘겨준다.

 

 

Piece는 useDrag 에 넣어준 id 부분에 position을 추가하면 된다.

 

// /src/Piece.js

        item: { type: 'piece', id: `${position}_${type}_${color}`},

 

 

 

그리고 BoardSquare drop 부분에도 반영해주면 된다.

 

// /src/BoardSquare.js


        drop: (item) => {
            const [ fromPosition ] = item.id.split('_');
            move(fromPosition, position);
        }

 

 

여기까지 한 경우 화면에서는 체스 말이 움직이는 대로 놓이고 흑백 주고 받으며 대결까지 할 수 있다.

 

gaming

반응형