Open hichemfantar opened 1 month ago
Related to #29 too
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)
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