iT邦幫忙

2024 iThome 鐵人賽

0

Day30 要來寫個打地鼠的小遊戲

資料

const MOLE_IMAGE_URL =
  "https://raw.githubusercontent.com/wesbos/JavaScript30/master/30%20-%20Whack%20A%20Mole/mole.svg";
const DIRT_IMAGE_URL =
  "https://raw.githubusercontent.com/wesbos/JavaScript30/refs/heads/master/30%20-%20Whack%20A%20Mole/dirt.svg";
  const [score, setScore] = useState<number>(0);
  const [timeLeft, setTimeLeft] = useState<number>(10);
  const [gameStarted, setGameStarted] = useState<boolean>(false);
  const [activeMole, setActiveMole] = useState<number | null>(null);

  const timerRef = useRef<number | null>(null);
  const moleTimerRef = useRef<number | null>(null);

開始遊戲

  const startGame = useCallback(() => {
    setScore(0);
    setTimeLeft(10);
    setGameStarted(true);
    setActiveMole(null);

    const updateTime = () => {
      setTimeLeft((prevTime: number) => {
        if (prevTime <= 1) {
          if (timerRef.current) {
            clearInterval(timerRef.current);
          }
          if (moleTimerRef.current) {
            clearInterval(moleTimerRef.current);
          }
          setGameStarted(false);
          setActiveMole(null);
          return 0;
        }
        return prevTime - 1;
      });
    };

    const updateMole = () => {
      const newMole = Math.floor(Math.random() * 6);
      setActiveMole(newMole);
    };

    timerRef.current = window.setInterval(updateTime, 1000);
    moleTimerRef.current = window.setInterval(updateMole, 1000);
  }, []);

打地鼠

  const whackMole = useCallback(
    (index: number) => {
      if (index === activeMole) {
        setScore((prevScore: number) => prevScore + 1);
        setActiveMole(null);
      }
    },
    [activeMole]
  );

結束遊戲

  const stopGame = useCallback(() => {
    if (timerRef.current) {
      clearInterval(timerRef.current);
    }
    if (moleTimerRef.current) {
      clearInterval(moleTimerRef.current);
    }
    setGameStarted(false);
    setActiveMole(null);
  }, []);

畫面結構

  return (
    <div className="flex flex-col items-center justify-center min-h-screen bg-green-100">
      <h1 className="text-4xl font-bold mb-4">Whack-A-Mole!</h1>
      <div className="mb-4">
        <span className="font-bold">Score:</span> {score} |{" "}
        <span className="font-bold">Time Left:</span> {timeLeft}s
      </div>

      {!gameStarted ? (
        <button
          type="button"
          className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
          onClick={startGame}
        >
          Start Game
        </button>
      ) : (
        <div className="grid grid-cols-3 gap-4">
          {[...Array(6)].map((_, index) => (
            <div
              key={index}
              className="w-32 h-32 relative cursor-pointer overflow-hidden"
              onClick={() => whackMole(index)}
            >
              <div
                className="absolute inset-0 bg-contain bg-no-repeat bg-bottom z-10"
                style={{ backgroundImage: `url(${DIRT_IMAGE_URL})` }}
              />
              <div
                className={`absolute inset-0 bg-contain bg-no-repeat bg-bottom transition-transform duration-200 ease-out ${
                  activeMole === index ? "translate-y-0" : "translate-y-full"
                }`}
                style={{ backgroundImage: `url(${MOLE_IMAGE_URL})` }}
              />
            </div>
          ))}
        </div>
      )}

      {gameStarted && (
        <button
          type="button"
          className="mt-4 bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded"
          onClick={stopGame}
        >
          Stop Game
        </button>
      )}
    </div>
  );

DEMO

https://codesandbox.io/p/devbox/2mx4cr


上一篇
[Day29]_Countdown-Timer
系列文
React30——用 React 探索 JavaScript30 的魅力30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
brownrice
iT邦新手 4 級 ‧ 2024-10-21 13:06:24

恭喜完賽~

我要留言

立即登入留言