Search code examples
javascriptreactjsthree.jsreact-three-fiber

Prevent click through objects in React-Fiber Three.js


Here is a cube of planes that I can click

The main issue with this so far is that clicking on a plane clicks through when I would only want to click the plane that my mouse is over. What am I missing on my Three.js Plane?

I have tried searching for something relating to collision in three.js but to no avail so far.

After further research, I think it has something to do with RayCasting?

import React, { useState, useRef } from "react";
import { OrbitControls, Plane } from "@react-three/drei";
import { Canvas, useFrame, useThree, extend } from "@react-three/fiber";
import styles from "../styles/game.module.css";
import { DoubleSide } from "three";

const Cell = (props) => {
  const [hovered, hover] = useState(false);
  const [checked, setChecked] = useState(false);

  const colorStyle = () => {
    if (hovered) return "hotpink";
    if (checked) return "lightblue";
    return "orange";
  };
  return (
    <Plane
      scale={1}
      onClick={() => setChecked(!checked)}
      onPointerEnter={() => hover(true)}
      onPointerLeave={() => hover(false)}
      position={props.position}
      rotation={props.rotation}
    >
      <meshPhongMaterial side={DoubleSide} color={colorStyle()} />
    </Plane>
  );
};

const Cube = () => {
  useFrame((state, delta) => {
  });

  return (
    <>
      {/* Back Face */}
      <Cell position={[-1, 1, -1.5]} rotation={[0, 0, 0]} />
      // other cells here

      {/* Front Face */}
      <Cell position={[-1, 1, 1.5]} rotation={[0, 0, 0]} />
      // other cells here

      {/* Left Face */}
      <Cell position={[-1.5, 1, 1]} rotation={[0, Math.PI / 2, 0]} />
      // other cells here

      {/* Right Face */}
      <Cell position={[1.5, 1, 1]} rotation={[0, Math.PI / 2, 0]} />
      // other cells here
      
      {/* Bottom Face */}
      <Cell position={[1, -1.5, 1]} rotation={[Math.PI / 2, 0, 0]} />
      // other cells here
      
      {/* Top */}
      <Cell position={[1, 1.5, 1]} rotation={[Math.PI / 2, 0, 0]} />
     // other cells here
    </>
  );
};

const SceneItems = () => {
  return (
    <>
      <OrbitControls minDistance={7.5} maxDistance={15} />
      <ambientLight intensity={0.5} />
      <spotLight position={[10, 15, 10]} angle={0.3} />
      <Cube position={[1, 1, 1]} />
    </>
  );
};

const CompleteScene = () => {
  return (
    <div id={styles.scene}>
      <Canvas>
        <SceneItems />
      </Canvas>
    </div>
  );
};

Solution

  • It appears that all I needed to prevent this click-through from happening was to add event.stopPropagation() to my event listeners on my Plane. Now I no longer click through the Plane

    
    const Cell = (props) => {
      const [hovered, hover] = useState(false);
      const [checked, setChecked] = useState(false);
    
      useCursor(hovered);
    
      const colorStyle = () => {
        if (hovered) return "hotpink";
        if (checked) return "lightblue";
        return "orange";
      };
      return (
        <Plane
          scale={1}
          onClick={(e) => {
            e.stopPropagation();
            setChecked(!checked);
          }}
          onPointerEnter={(e) => {
            e.stopPropagation();
            hover(true);
          }}
          onPointerLeave={(e) => {
            e.stopPropagation();
            hover(false);
          }}
          position={props.position}
          rotation={props.rotation}
        >
          <meshPhongMaterial side={DoubleSide} color={colorStyle()} />
        </Plane>
      );
    };
    

    More details here: https://docs.pmnd.rs/react-three-fiber/api/events#pointer-capture