pmndrs / ecctrl

🕹️ A floating rigibody character controller
MIT License
542 stars 64 forks source link

feature request: flying and climbing states #111

Open hichemfantar opened 1 week ago

hichemfantar commented 1 week ago

these 2 states are critical for many use cases

relevant resources: https://youtu.be/jxecc2IGlWA?si=PP8NB8Nu1-I1WJpJ https://www.youtube.com/watch?v=cIZaqeJd28Y

related to #109

elisherer commented 1 week ago

Related to #29 too

elisherer commented 1 day ago

This is a flying POC I created. A stand-in replacement for <Ecctrl /> (replace with <EcctrlWithFlight />)

Press "F" to toggle flying mode. Press "Space" to go up, Press "Shift" to go down. Arrows move on the x/z axis

import {KeyboardEvent, useEffect, useRef, useState } from "react";
import { useKeyboardControls } from "@react-three/drei";
import Ecctrl, { EcctrlProps } from "ecctrl";
import { useFrame } from "@react-three/fiber";
import { RapierRigidBody } from "@react-three/rapier";

const Flying: EcctrlProps = {
  jumpVel: 2,
  jumpForceToGroundMult: 0,
  fallingMaxVel: 0,
  fallingGravityScale: 0,
};

function useIsInsideKeyboardControls() {
  try {
    return !!useKeyboardControls();
  } catch (e) {
    return false;
  }
}

export type EcctrlWithFlightProps = EcctrlProps & {
  domElement?: HTMLElement;
};

export default function EcctrlWithFlight({ domElement, ...props }: EcctrlWithFlightProps) {
  const cref = useRef<RapierRigidBody | undefined>();
  const isInsideKeyboardControls = useIsInsideKeyboardControls();
  const [, getKeys] = isInsideKeyboardControls ? useKeyboardControls() : [null];
  const [flying, setFlying] = useState(false);

  useEffect(() => {
    function toggleFlight(e: KeyboardEvent) {
      if (e.code === "KeyF") {
        setFlying(f => !f);
      }
    }
    const source = domElement || window;
    source.addEventListener("keyup", toggleFlight as any);
    return () => source.removeEventListener("keyup", toggleFlight as any);
  }, [domElement]);

  useFrame(() => {
    if (!getKeys || !flying || !cref.current) return;
    const { jump, run } = isInsideKeyboardControls ? getKeys() : {};

    if (jump) cref.current.applyImpulse({ x: 0, y: 0.05, z: 0 }, true);
    if (run) cref.current.applyImpulse({ x: 0, y: -0.05, z: 0 }, true);
  });
  return <Ecctrl ref={cref as any} {...props} {...(flying ? Flying : null)} />;
}

Sandbox (convert from above TS to JS)