feat: Implement ColliderCreator and ColliderInstance components, enhance PhysicsSimulator with multiple MaterialSpawner instances
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user