게임 로직 구현에서 게임 진행의 흐름을 action의 값을 바꾸면서 구현하기로 했었는데
이것들을 hook의 형태로 분리해서 hook의 흐름이 필요한 컴포넌트에서 불러다 쓸 수 있도록 했다.
나는 크게 4가지로 분리했다.
useGameAction() //action 관리
useGameTurn() //게임 시작하기, 차례 돌릴때 필요한 작업
usePieceMove() //윷 말을 선택하고 움직이기
useYutThrow() //윷을 던지고 결과 저장, 사용
📌 주의!
각 hook의 구조를 구경하기 전에 주의할점은
react hook이나 recoil hook을 구조만 간단하게 보기 위해서
관련 변수, react hook 같은 부수적인 부분은 모두 지웠다는거 기억해주시길!
action의 값만 바꾸어주면되는데 왜 hook으로까지 적었을까?
//useGameAction.tsx
const useGameAction = () => {
const [action, setAction] = useRecoilState(GameActionState);
const startGame = () => {
setAction("Started");
};
const turnStart = () => {
setAction("TurnStart");
};
//...
}
useGameAction은 단순히 action의 값을 수정하는 기능밖에 없다.
하지만 이를 메소드화한다면 다음과 같은 효과를 얻을 수 있다.
//before
const nextTurn = useCallback(
(userId: string) => {
setNowTurnPlayerId(userId);
startTurn();
setAction("TurnStart");
},
[playerTurnList]
);
//after
const nextTurn = useCallback(
(userId: string) => {
setNowTurnPlayerId(userId);
startTurn();
turnStart();
},
[playerTurnList]
);
before와 after를 비교해봤을때 after가 확실히 코드의 흐름을 읽기가 편해졌다.
이런 효과를 얻고 싶어서 useGameAction을 만들었다
useGameTurn에서는 게임 시작, turn 시작, turn 종료처럼
turn 관련된 작업으로 구성했다.
상세한 코드를 첨부하기에는 꽤 길기 때문에 어떤 메소드가 있는지 확인해보자
const useGameTurn = () => {
// 게임 시작시 관려 변수 셋팅
const initPlayerTurn = (turnInfoList: Array<string>) => {}
// 차례 시작때 필요한 작업
const startTurn = () => {};
// 다음 차례는 누구인지 계산해보기
const getNextPlayerId = () : string => {};
// 다음 턴이 누군지 전역 변수 갱신
const nextTurn = (userId: string) =>{};
// 다음 차례가 내 차례면 소켓으로 broadcast
const ifNextTurnIsMe = () => {};
//action에 따른 흐름 분기
useEffect(() => {
if (action === "None") return;
switch (action) {
case "Started":
case "TurnStart":
case "ThrowYut":
case "TurnEnd":
}
}, [action]);
}
윷 말을 움직이는것과 관련된 기능을 모아놨다.
const usePieceMove = () => {
//말을 업은 경우 움직일 말을 찾아서 셋팅
const pieceMove = (userId: string,
pieceIdList: Array<number>,
movePath: Array<number>,
moveType: PieceMoveType) => {
setMoveInfo()
};
//pieceMove에 필요한 변수를 저장
const setMoveInfo = (userId: string,
pieceIndex: number,
movePath: Array<number>) => {};
//윷 말 움직이기 실행
const doPieceMove = (movePieceIndex: number,
pointIndex: number) => {};
// 도개걸윷모를 int 값으로 변환
const convertThrowResultToInt = (type: ThrowResultType) => {}
//말 선택
const selectPiece = (userId: string, pieceId: number) => {};
//모서리 분기점 선택 (leftTop에서 ⬇로 갈지 ↘ 으로 갈지 등등)
const selectArrow = (arrowType: number, position: number) => {};
//말 동내기
const pieceOver = () => {};
//말 합칠때 필요한 값 계산, 셋팅
const appendPiece = () => {
appendAToB()
};
//a와 b를 합침
const appendAToB = (userId: string,
movePieceIndex: number, //움직여서 합칠 말
targetPieceIndex: number //원래 말 판에 있던 말
) => {};
//윷 말을 잡을때 필요한 값 저장
const saveCatchInfo = (catchedUserId: string,
catchedPieceList: Array<number>) => {}
//상대방 윷 말 잡기
const catchPiece = () => {};
//윷 판의 모서리 비활성화
const clearActiveCornerArrow = () => {};
//서버에 event 카드 요청
const getEvent = () => {};
//윷 말 움직이기 action 흐름 분기
useEffect(() => {
let i = 0;
const timer = setInterval(() => {
if (윷 말을 다 움직였다면) {
clearInterval(timer);
// move가 끝나면 추가 동작
switch (moveType) {
case "Append":
case "Over":
case "Catch":
case "Event":
case "End":
}
if (더 쓸 결과가 없다면) {
turnEnd();
return;
}
//더 쓸 결과가 있으면 윷 말 선택
selectPieceStart();
return;
}
//실제로 윷 말 움직이를 실행함
doPieceMove(movePieceIndex, movePathList[i++]);
}, animationSeconds * 1000);
}, [movePathList]);
};
const useYutThrow = () => {
//내가 윷을 던져도 되는지 계산
const canIThrow = () : boolean=> {};
//사용할 결과가 없는지
const isResultEmpty = () : boolean=> {}
// 윷 던지기
const throwYut = () => {};
// 서버에서 받아온 윷 던지기 결과 저장
const saveThrowResult = () => {};
// 사용할 윷 던지기 결과
const getYutThrowResultForUse = () : ThrowResultType => {};
//
const popYutThrowResultForUse = () => {};
//
const resetThrowResultList = () => {};
//윷 던지기 component controll
useEffect(() => {
if (윷 던지는 타이밍이 아닐때) {
//윷 던지기 비활성화
return;
}
// 윷 던질때이고
if (현재 내 차례일 때는) {
//윷 던지기 활성화
}
}, [action]);
};
로직을 분리해서 윷 던지기 컴포넌트, 윷 말 컴포넌트 등에서 필요한 로직을 가져다 쓸 수 있어서 좋았다.
하지만 메소드명이 중구난방이라서 다른 팀원들이 읽기에는 많이 헷갈렸을거 같다.