feat: Implement ColliderCreator and ColliderInstance components, enhance PhysicsSimulator with multiple MaterialSpawner instances

This commit is contained in:
2025-07-23 14:03:00 +05:30
parent d4d66d9d32
commit ec4a4247b2
5 changed files with 319 additions and 24 deletions

View File

@@ -15,12 +15,19 @@ type MaterialSpawnerProps = {
function MaterialSpawner({ position, spawnInterval, spawnCount }: MaterialSpawnerProps) {
const { loadingProgress } = useLoadingProgress();
const [spawned, setSpawned] = useState<{ id: string; position: [number, number, number]; ref: React.RefObject<RapierRigidBody> }[]>([]);
const [spawned, setSpawned] = useState<{
id: string;
position: [number, number, number];
ref: React.RefObject<RapierRigidBody>;
materialType: string;
}[]>([]);
const [spawningPaused, setSpawningPaused] = useState(false);
const spawnedCount = useRef(0);
const { gl, camera, pointer, controls } = useThree();
const [draggedId, setDraggedId] = useState<string | null>(null);
const dragOffset = useRef<THREE.Vector3>(new THREE.Vector3());
const initialDepth = useRef<number>(0);
const materialTypes = ['Default material', 'Material 1', 'Material 2', 'Material 3'];
useEffect(() => {
if (loadingProgress !== 0) return;
@@ -30,6 +37,7 @@ function MaterialSpawner({ position, spawnInterval, spawnCount }: MaterialSpawne
const startSpawning = () => {
if (!interval) {
interval = setInterval(() => {
if (spawningPaused) return;
setSpawned(prev => {
if (spawnCount !== undefined && spawnedCount.current >= spawnCount) {
clearInterval(interval!);
@@ -37,15 +45,20 @@ function MaterialSpawner({ position, spawnInterval, spawnCount }: MaterialSpawne
return prev;
}
spawnedCount.current++;
const randomMaterialType = materialTypes[Math.floor(Math.random() * materialTypes.length)];
return [
...prev,
{
id: generateUniqueId(),
position,
ref: React.createRef<RapierRigidBody>(),
materialType: randomMaterialType,
}
];
});
}, spawnInterval);
}
};
@@ -72,16 +85,14 @@ function MaterialSpawner({ position, spawnInterval, spawnCount }: MaterialSpawne
stopSpawning();
document.removeEventListener('visibilitychange', handleVisibility);
};
}, [loadingProgress, spawnInterval, spawnCount, position]);
}, [loadingProgress, spawnInterval, spawnCount, position, spawningPaused]);
const handleSleep = (id: string) => {
setSpawned(prev => prev.filter(obj => obj.id !== id));
};
const handlePointerDown = (id: string) => {
if (controls) {
(controls as CameraControls).enabled = false;
}
if (controls) (controls as CameraControls).enabled = false;
setDraggedId(id);
const obj = spawned.find(o => o.id === id);
@@ -128,7 +139,6 @@ function MaterialSpawner({ position, spawnInterval, spawnCount }: MaterialSpawne
const finalPosition = camera.position.clone().add(direction.multiplyScalar(initialDepth.current));
const currentPosition = obj.ref.current.translation();
const moveDirection = new THREE.Vector3().subVectors(finalPosition, currentPosition);
obj.ref.current.setLinvel({
@@ -139,9 +149,7 @@ function MaterialSpawner({ position, spawnInterval, spawnCount }: MaterialSpawne
};
const handlePointerUp = () => {
if (controls) {
(controls as CameraControls).enabled = true;
}
if (controls) (controls as CameraControls).enabled = true;
if (!draggedId) return;
const obj = spawned.find(o => o.id === draggedId);
@@ -165,9 +173,21 @@ function MaterialSpawner({ position, spawnInterval, spawnCount }: MaterialSpawne
};
}, [draggedId, spawned, controls, camera, gl]);
const handleBoxClick = () => {
setSpawningPaused(prev => !prev);
};
return (
<>
{spawned.map(({ id, position, ref }) => (
<mesh
position={position}
onClick={handleBoxClick}
>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color={spawningPaused ? "red" : "white"} transparent opacity={0.2} />
</mesh>
{spawned.map(({ id, position, materialType, ref }) => (
<RigidBody
key={id}
ref={ref}
@@ -176,11 +196,12 @@ function MaterialSpawner({ position, spawnInterval, spawnCount }: MaterialSpawne
angularDamping={0.5}
linearDamping={0.5}
restitution={0.1}
// onSleep={() => handleSleep(id)}
userData={{ materialType, materialUuid: id }}
// onSleep={() => handleSleep(id)}
>
<MaterialModel
materialId={id}
materialType="Default material"
materialType={materialType}
onPointerDown={(e) => {
e.stopPropagation();
handlePointerDown(id);