You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 02:07:34