Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature request: flying and climbing states #111

Open
hichemfantar opened this issue Oct 6, 2024 · 2 comments
Open

feature request: flying and climbing states #111

hichemfantar opened this issue Oct 6, 2024 · 2 comments

Comments

@hichemfantar
Copy link

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

@hichemfantar hichemfantar changed the title feature request: isFlying and isClimbing states feature request: flying and climbing states Oct 6, 2024
@elisherer
Copy link

Related to #29 too

@elisherer
Copy link

elisherer commented Oct 14, 2024

This is a flying POC I created.
A drop-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 { forwardRef, KeyboardEvent, useEffect, useImperativeHandle, 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 & {
  forceFlying?: boolean;
  domElement?: HTMLElement;
};

export default forwardRef(function EcctrlWithFlight(
  { forceFlying, domElement, ...props }: EcctrlWithFlightProps,
  fref,
) {
  const cref = useRef<RapierRigidBody | undefined>();
  useImperativeHandle(fref, () => cref.current);

  const lastImpulseTime = useRef<number | null>(null);
  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(state => {
    if (!getKeys || !(forceFlying ?? flying) || !cref.current || !isInsideKeyboardControls) return;
    if (lastImpulseTime.current !== null && state.clock.getElapsedTime() - lastImpulseTime.current < 0.1) return;
    const { jump, run } = getKeys();
    if (lastImpulseTime.current === null && (jump || run)) {
      // just started ascending or descending
      lastImpulseTime.current = state.clock.getElapsedTime();
    }
    if (jump) {
      cref.current.applyImpulse({ x: 0, y: 0.02, z: 0 }, true);
    } else if (run) {
      cref.current.applyImpulse({ x: 0, y: -0.02, z: 0 }, true);
    } else {
      lastImpulseTime.current = null;
    }
  });
  return <Ecctrl ref={cref as any} {...props} {...((forceFlying ?? flying) ? Flying : null)} />;
});

Sandbox
(convert from above TS to JS)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants