로아 어빌리티 스톤(돌깎) 세공 시뮬레이터 만들기

j moon·2022년 8월 6일
2

로스트아크

목록 보기
1/1

어빌리티 스톤 시뮬레이터

리액트로 요즘에 즐겨하는 로스트아크 라는 게임의 돌을 깎는 시뮬레이터를 만드려고 한다. 일단 구성은

1. side - 깎은 돌을 저장 했을때 로컬스토리지에 등록후 side에 리스트로 보여줄 예정
2. mainBox - 돌을 깎는 액션을 할 컴포넌트
3. bottom - 돌을 깎으며 누른 순서를 히스토리로 보여줄 예정
side에서 이미 깍은 돌 클릭시 bottom도 같이 보여줌

화면 구성

데이터 구조

채워지는 마름모가 세로로 3칸 가로로 10칸 이기때문에 3 * 10 의 2차원 배열을 만들어 그안에 체크여부와 성공여부의 값을 객체로 넣어줄 예정

let state = [
  [ {result: "", checked: false }, {result: "", checked: false }...],//10개
  [...],//위와 동일
  [...]//위와 동일
]

컴포넌트 구조

MainBox

export default function MainBox(params) {
  //3x10의 2차원 배열 선언 [[{},{},{}...],[{},{},{}...],[{},{},{}...]]
  const [stone, setStone] = useState(Array.from(Array(3), () => new Array(10).fill({ result: "", checked: false })));

  //성공확률 퍼센테이지 초기값 75%
  const [percentage, setPercentage] = useState(75);

  return (
    <div className="MainBox">
      {stone.map((ele, idx) => {
        return (
          <Engraving
            className={idx === 2 ? "decrease" : "increase"} //className에 따라 box-shadow를 다르게 줌
            key={idx}
            Firstele={ele} //stone 2차원 배열의 1차 배열 [{},{},{}...]
            Firstidx={idx} //인덱스 0,1,2
            stone={stone}
            setStone={setStone}
            percentage={percentage}
            setPercentage={setPercentage}
          />
        );
      })}
    </div>
  );
}

stone의 1차 배열만 꺼내서 3개의 Engraving컴포넌트를 만들어준다

Engraving

export default function Engraving({ className, Firstele, Firstidx, stone, setStone, percentage, setPercentage }) {
  const [cnt, setCnt] = useState(0); //채워지는 칸을 위해 Engraving컴포넌트별 상태 만듬
  const copiedStone = [...stone]; //stone을 직접 수정하면 안되기 때문에 복사본 만듬

  const handleBtnOnclik = (index) => {
    //index = 0,1,2
    if (cnt < 10) {
      //10칸 초과면 버튼 작동 안함

      let random = Math.floor(Math.random() * 100); //확률 소숫점 내림

      //랜덤으로 나온 숫자보다 percentage(초기 75%) 가 큰 경우 : 성공 / 아닌경우 그대로 fail
      const isSuccess = percentage > random;

      //성공이면
      if (isSuccess) {
        if (percentage > 25) {
          //확률이 25% 초과일 경우 - 10%
          setPercentage(percentage - 10);
        }
      } else {
        //실패이면
        if (percentage < 75) {
          //확률이 75%미만 일경우 +10%
          setPercentage(percentage + 10);
        }
      }
      copiedStone[index][cnt] = Object.assign({}, { result: isSuccess ? "success" : "fail", checked: isSuccess });
      //stone[index][cnt]의 객체{}를  { result: "success", checked: true }로 변경
      setStone(copiedStone); //stone 상태 업데이트
      setCnt(cnt + 1); //한칸 채워질때마다 다음칸으로 이동
    }
  };
  return (
    <>
      <div>
        {Firstidx === 0 ? "성공확률 " : Firstidx === 2 ? "균열확률 " : ""}
        {Firstidx !== 1 ? percentage + "%" : ""}
      </div>
      <div className={"Engraving " + className}>
        {/* className 으로 box-shadow 변경 */}
        <div className="Engraving__img"></div>
        <div className="Engraving__action">
          <div className="Engraving__action__name"></div>
          <div className="Engraving__action__box">
            {Firstele.map((ele, idx) => {
              return (
                <div key={idx}>
                  <div
                    className={
                      "Engraving__action__box__square " +
                      (ele.result === "success" && Firstidx === 2 ? "success_red" : ele.result)
                      // 클래스 추가로 버튼 클릭시 마름모의 색을 바꿈
                    }
                  />
                </div>
              );
            })}
          </div>
          <div className="Engraving__action__result"></div>
        </div>
        {/* 모코코 망치버튼 */}
        <div
          className="Engraving__btn"
          onClick={() => {
            handleBtnOnclik(Firstidx);
          }}
        >
          <img src={mokoko} alt="mokoko" />
        </div>
      </div>
    </>
  );
}

Engraving 컴포넌트 별 고유의 cnt state를 만들어 1칸씩 채워질때마다 다음칸으로 이동할 수 있게 만들어준다
ex)

순서12345678910버튼
index:0cnt:0cnt:1cnt:2cnt:3cnt:4cnt:5cnt:6cnt:7------버튼
index:1cnt:0cnt:1cnt:2---------------------버튼
index:2cnt:0cnt:1cnt:2cnt:3cnt:4cnt:5------------버튼

버튼 클릭시 성공 여부를 위치에 맞게 배열안의 객체에 하나씩 넣어준다

copiedStone[index][cnt] = Object.assign({}, { result: isSuccess ? "success" : "fail", checked: isSuccess });

바뀐 값에 의해 추가되는 클래스가 달라지기 때문에 색상이 변경된다

{Firstele.map((ele, idx) => {
              return (
                <div key={idx}>
                  <div
                    className={
                      "Engraving__action__box__square " +
                      (ele.result === "success" && Firstidx === 2 ? "success_red" : ele.result)
                      // 클래스 추가로 버튼 클릭시 마름모의 색을 바꿈
                    }
                  />
                </div>
              );
            })}

ex)
success = blue
success_red = red
fail = black


65% 확률로 97돌 실패..

github
https://github.com/moonjh9392/stone-cutting
demo site
https://moonjh9392.github.io/stone-cutting/

profile
자발개보초

0개의 댓글