如何在React Native&Expo中用matter.js定时持续渲染物理物体?
问题描述
我正在用React Native、matter.js和react-native-game-engine开发一款小游戏,想要每隔1秒生成并渲染随机形状。用循环一次性创建有限数量的形状时,物体能正常显示;但用setInterval定时创建时,日志显示形状已创建,却无法在屏幕上渲染。
问题原因
直接在entities函数里用setInterval修改shapes对象不会触发React状态更新,GameEngine组件依赖的gameEntities状态没有变化,所以不会重新渲染新添加的形状。另外,每次调用entities()都会创建新的Matter引擎实例,这也会导致状态不同步。
解决方案
把定时生成形状的逻辑移到GameScreen组件中,通过更新React状态让GameEngine感知到新实体,同时复用同一个Matter引擎实例:
步骤1:重构entities函数
修改entities函数,返回初始实体、引擎/世界实例,以及创建形状的方法,方便后续复用:
import Matter from 'matter-js'; import { Dimensions } from 'react-native'; import Box from '../components/Box'; import Circle from '../components/Circle'; import Triangle from "../components/Triangle"; import Rectangle from '../components/Rectangle'; import EllipseComponent from "../components/Ellipse"; import Pentagon from "../components/Pentagon"; import Hexagon from "../components/Hexagon"; export default () => { const engine = Matter.Engine.create(); const world = engine.world; const { width, height } = Dimensions.get('window'); const shapes = {}; // 创建随机形状的方法,接收world参数 const createRandomShape = (world) => { const randomShapeIndex = Math.floor(Math.random() * 7); let shape; switch (randomShapeIndex) { case 0: shape = Box( world, 'red', { x: Math.random() * width, y: 120 }, { width: 40, height: 40 }, { isStatic: false, label: 'Square' } ); console.log('Square created:'); break; case 1: shape = Circle( world, 'blue', { x: Math.random() * width, y: 120 }, 20, { isStatic: false, label: 'Circle' } ); console.log('Circle created:'); break; case 2: shape = Triangle( world, 'green', { x: Math.random() * width, y: 120 }, { width: 50, height: 50 }, { isStatic: false, label: 'Triangle' } ); console.log('Triangle created:'); break; case 3: shape = Rectangle( world, 'purple', { x: Math.random() * width, y: 120 }, { width: 60, height: 40 }, { isStatic: false, label: 'Rectangle' } ); console.log('Rectangle created:'); break; case 4: shape = EllipseComponent( world, 'orange', { x: Math.random() * width, y: 120 }, { width: 60, height: 40 }, { isStatic: false, label: 'Ellipse' } ); console.log('Ellipse created:'); break; case 5: shape = Pentagon( world, 'pink', { x: Math.random() * width, y: 120 }, { width: 40, height: 40 }, { isStatic: false, label: 'Pentagon' } ); console.log('Pentagon created:'); break; case 6: shape = Hexagon( world, 'yellow', { x: Math.random() * width, y: 140 }, { width: 60, height: 40 }, { isStatic: false, label: 'Hexagon' } ); console.log('Hexagon created:'); break; default: break; } return shape; }; // 生成初始5个形状 for (let i = 0; i < 5; i++) { const newShape = createRandomShape(world); shapes[`shape${i}`] = newShape; } return { initialEntities: { physics: { engine, world }, ...shapes, }, engine, world, createRandomShape // 暴露创建方法给组件使用 }; };
步骤2:修改GameScreen组件
在组件中初始化实体,设置定时器定时添加新形状,并通过setGameEntities更新状态:
import React, { useState, useEffect } from 'react'; import { View, Text, Button, StatusBar } from 'react-native'; import { GameEngine } from 'react-native-game-engine'; import entities from '../entities/index'; import Physics from '../Physics'; const GameScreen = (props) => { const [gameEntities, setGameEntities] = useState(null); const [shapeIdCounter, setShapeIdCounter] = useState(5); // 初始已有5个形状,从5开始计数 const [world, setWorld] = useState(null); const [createRandomShape, setCreateRandomShape] = useState(null); useEffect(() => { const { initialEntities, world: matterWorld, createRandomShape: shapeCreator } = entities(); setGameEntities(initialEntities); setWorld(matterWorld); setCreateRandomShape(shapeCreator); }, []); useEffect(() => { if (!world || !createRandomShape) return; // 设置定时器,每隔1秒创建一个新形状 const intervalId = setInterval(() => { const newShape = createRandomShape(world); const newId = `shape${shapeIdCounter}`; // 更新gameEntities状态,添加新形状 setGameEntities(prevEntities => ({ ...prevEntities, [newId]: newShape })); setShapeIdCounter(prev => prev + 1); }, 1000); // 组件卸载时清理定时器,避免内存泄漏 return () => clearInterval(intervalId); }, [world, createRandomShape, shapeIdCounter]); if (!gameEntities) { return <Text>Loading...</Text>; } return ( <View> <Text>Game Screen</Text> <Text>Click on the button to navigate to the next screen</Text> <Button title="Go to Main Screen" onPress={() => props.navigation.navigate('ScreenOne')} /> <GameEngine systems={[Physics]} entities={gameEntities} > <StatusBar style="auto" hidden={true} /> </GameEngine> </View> ); } export default GameScreen;
步骤3:验证形状组件
你的Box等形状组件逻辑没问题,不需要修改,它们会在GameEngine更新时自动渲染新的形状。
关键说明
- 必须通过
setGameEntities更新状态,这样React才会重新渲染GameEngine组件,进而渲染新添加的形状 - 复用同一个Matter引擎和世界实例,避免状态混乱
- 组件卸载时清理定时器,防止内存泄漏
内容的提问来源于stack exchange,提问作者Alistair Alva
相关产品推荐
相关产品推荐

