All files / app/(tabs)/scanner/hooks useLoadingGame.ts

64.19% Statements 52/81
40% Branches 10/25
75% Functions 9/12
65% Lines 52/80

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160      3x     3x 3x 3x 3x 3x 3x 3x   3x   6x 6x     6x 6x     6x 6x 6x 6x 6x     6x 5x 1x   5x 1x     5x 5x 5x         6x 6x 2x 1x 1x   2x     4x                                                                                                                     4x   4x 4x 4x         6x 1x 1x 1x       6x         6x 1x 1x     1x 1x     1x 1x 1x 1x     6x                  
import { useEffect, useRef, useState, useCallback } from 'react';
import { Animated, Dimensions, Easing } from 'react-native';
 
const { width: SCREEN_WIDTH } = Dimensions.get('window');
 
// 游戏配置常量
const GRAVITY = 0.8;
const JUMP_FORCE = -12;
const GROUND_HEIGHT = 50;
const CAT_SIZE = 60;
const OBSTACLE_SIZE = 30;
const OBSTACLE_SPEED = 5;
const MIN_OBSTACLE_GAP = 200; // 最小障碍物间隔
 
export const useLoadingGame = (isPlaying: boolean, onGameOver: () => void) => {
  // 游戏状态
  const [score, setScore] = useState(0);
  const [isGameOver, setIsGameOver] = useState(false);
 
  // 动画值
  const catY = useRef(new Animated.Value(0)).current;
  const obstacleX = useRef(new Animated.Value(SCREEN_WIDTH)).current;
 
  // 物理状态引用(用于实时计算)
  const catVelocity = useRef(0);
  const catYValue = useRef(0);
  const obstacleXValue = useRef(SCREEN_WIDTH);
  const gameLoopRef = useRef<number | null>(null);
  const isJumping = useRef(false);
 
  // 初始化监听器
  useEffect(() => {
    const catListener = catY.addListener(({ value }) => {
      catYValue.current = value;
    });
    const obstacleListener = obstacleX.addListener(({ value }) => {
      obstacleXValue.current = value;
    });
 
    return () => {
      catY.removeListener(catListener);
      obstacleX.removeListener(obstacleListener);
    };
  }, []);
 
  // 游戏循环
  useEffect(() => {
    if (!isPlaying || isGameOver) {
      if (gameLoopRef.current) {
        cancelAnimationFrame(gameLoopRef.current);
        gameLoopRef.current = null;
      }
      return;
    }
 
    const loop = () => {
      // 1. 更新猫咪位置(重力)
      if (catYValue.current > 0 || isJumping.current) {
        catVelocity.current += GRAVITY;
        const newY = catYValue.current + catVelocity.current;
 
        if (newY >= 0) {
          // 落地
          catY.setValue(0);
          catYValue.current = 0; // Manually update ref
          catVelocity.current = 0;
          isJumping.current = false;
        } else {
          // 空中
          catY.setValue(newY);
          catYValue.current = newY; // Manually update ref
        }
      }
 
      // 2. 更新障碍物位置
      let newObstacleX = obstacleXValue.current - OBSTACLE_SPEED;
      if (newObstacleX < -OBSTACLE_SIZE) {
        // 重置障碍物
        newObstacleX = SCREEN_WIDTH + Math.random() * 200;
        setScore((s) => s + 1);
      }
      obstacleX.setValue(newObstacleX);
      obstacleXValue.current = newObstacleX; // Manually update ref
 
      // 3. 碰撞检测
      // 猫咪中心点 (50, SCREEN_HEIGHT - GROUND_HEIGHT - catY - CAT_SIZE/2)
      // 障碍物中心点 (obstacleX + OBSTACLE_SIZE/2, SCREEN_HEIGHT - GROUND_HEIGHT - OBSTACLE_SIZE/2)
      // 简化为矩形碰撞
 
      // 猫咪区域:X: 20~20+CAT_SIZE, Y: catYValue.current (注意Y轴向上为负)
      // 障碍物区域:X: obstacleXValue.current, Y: 0 (贴地)
 
      // 这里的坐标系:catY是相对于地面的偏移(负数向上),obstacleX是屏幕横坐标
      const catLeft = 20; // 猫咪固定在左侧
      const catRight = 20 + CAT_SIZE - 20; // 减去一点碰撞宽容度
      const catBottom = catYValue.current; // 0是地面
 
      const obsLeft = obstacleXValue.current + 5;
      const obsRight = obstacleXValue.current + OBSTACLE_SIZE - 5;
      const obsTop = -OBSTACLE_SIZE + 10; // 障碍物高度
 
      // 碰撞条件:水平重叠 且 垂直重叠
      if (
        catRight > obsLeft &&
        catLeft < obsRight &&
        catBottom > obsTop // 猫咪底部 低于 障碍物顶部 (注意都是负数或0)
      ) {
        handleGameOver();
        return;
      }
 
      gameLoopRef.current = requestAnimationFrame(loop);
    };
 
    gameLoopRef.current = requestAnimationFrame(loop);
 
    return () => {
      Eif (gameLoopRef.current) {
        cancelAnimationFrame(gameLoopRef.current);
      }
    };
  }, [isPlaying, isGameOver]);
 
  const jump = () => {
    Eif (!isJumping.current && !isGameOver) {
      isJumping.current = true;
      catVelocity.current = JUMP_FORCE;
    }
  };
 
  const handleGameOver = () => {
    setIsGameOver(true);
    onGameOver();
  };
 
  const resetGame = useCallback(() => {
    setIsGameOver(false);
    setScore(0);
 
    // 重置动画值
    catY.setValue(0);
    obstacleX.setValue(SCREEN_WIDTH);
 
    // 关键:同时重置物理状态引用
    catVelocity.current = 0;
    catYValue.current = 0;
    obstacleXValue.current = SCREEN_WIDTH;
    isJumping.current = false;
  }, []);
 
  return {
    catY,
    obstacleX,
    score,
    isGameOver,
    jump,
    resetGame,
  };
};