ecctrl
Version:
A floating rigibody character controller for R3F
1,327 lines • 79.2 kB
JavaScript
"use strict";
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
const drei = require("@react-three/drei");
const fiber = require("@react-three/fiber");
const rapier = require("@react-three/rapier");
const React = require("react");
const THREE = require("three");
const leva = require("leva");
const zustand = require("zustand");
const middleware = require("zustand/middleware");
const rapier3dCompat = require("@dimforge/rapier3d-compat");
const three = require("@react-spring/three");
function _interopNamespaceDefault(e) {
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
if (e) {
for (const k in e) {
if (k !== "default") {
const d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: () => e[k]
});
}
}
}
n.default = e;
return Object.freeze(n);
}
const THREE__namespace = /* @__PURE__ */ _interopNamespaceDefault(THREE);
const useFollowCam = function({
disableFollowCam = false,
disableFollowCamPos = null,
disableFollowCamTarget = null,
camInitDis = -5,
camMaxDis = -7,
camMinDis = -0.7,
camUpLimit = 1.5,
// in rad
camLowLimit = -1.3,
// in rad
camInitDir = { x: 0, y: 0 },
// in rad
camMoveSpeed = 1,
camZoomSpeed = 1,
camCollisionOffset = 0.7,
// percentage
camCollisionSpeedMult = 4,
camListenerTarget = "domElement",
...props
} = {}) {
const { scene, camera, gl } = fiber.useThree();
let isMouseDown = false;
let previousTouch1 = null;
let previousTouch2 = null;
const originZDis = React.useRef(camInitDis != null ? camInitDis : -5);
const pivot = React.useMemo(() => new THREE__namespace.Object3D(), []);
const followCam = React.useMemo(() => {
const origin = new THREE__namespace.Object3D();
origin.position.set(
0,
originZDis.current * Math.sin(-camInitDir.x),
originZDis.current * Math.cos(-camInitDir.x)
);
return origin;
}, []);
let smallestDistance = null;
let cameraDistance = null;
let intersects = null;
const intersectObjects = React.useRef([]);
const cameraRayDir = React.useMemo(() => new THREE__namespace.Vector3(), []);
const cameraRayOrigin = React.useMemo(() => new THREE__namespace.Vector3(), []);
const cameraPosition = React.useMemo(() => new THREE__namespace.Vector3(), []);
const camLerpingPoint = React.useMemo(() => new THREE__namespace.Vector3(), []);
const camRayCast = new THREE__namespace.Raycaster(
cameraRayOrigin,
cameraRayDir,
0,
-camMaxDis
);
const onDocumentMouseMove = (e) => {
if (document.pointerLockElement || isMouseDown) {
pivot.rotation.y -= e.movementX * 2e-3 * camMoveSpeed;
const vy = followCam.rotation.x + e.movementY * 2e-3 * camMoveSpeed;
cameraDistance = followCam.position.length();
if (vy >= camLowLimit && vy <= camUpLimit) {
followCam.rotation.x = vy;
followCam.position.y = -cameraDistance * Math.sin(-vy);
followCam.position.z = -cameraDistance * Math.cos(-vy);
}
}
return false;
};
const onDocumentMouseWheel = (e) => {
const vz = originZDis.current - e.deltaY * 2e-3 * camZoomSpeed;
const vy = followCam.rotation.x;
if (vz >= camMaxDis && vz <= camMinDis) {
originZDis.current = vz;
followCam.position.z = originZDis.current * Math.cos(-vy);
followCam.position.y = originZDis.current * Math.sin(-vy);
}
return false;
};
const onTouchEnd = (e) => {
previousTouch1 = null;
previousTouch2 = null;
};
const onTouchMove = (e) => {
e.preventDefault();
e.stopImmediatePropagation();
const touch1 = e.targetTouches[0];
const touch2 = e.targetTouches[1];
if (previousTouch1 && !previousTouch2) {
const touch1MovementX = touch1.pageX - previousTouch1.pageX;
const touch1MovementY = touch1.pageY - previousTouch1.pageY;
pivot.rotation.y -= touch1MovementX * 5e-3 * camMoveSpeed;
const vy = followCam.rotation.x + touch1MovementY * 5e-3 * camMoveSpeed;
cameraDistance = followCam.position.length();
if (vy >= camLowLimit && vy <= camUpLimit) {
followCam.rotation.x = vy;
followCam.position.y = -cameraDistance * Math.sin(-vy);
followCam.position.z = -cameraDistance * Math.cos(-vy);
}
}
if (previousTouch1 && previousTouch2) {
const prePinchDis = Math.hypot(
previousTouch1.pageX - previousTouch2.pageX,
previousTouch1.pageY - previousTouch2.pageY
);
const pinchDis = Math.hypot(
e.touches[0].pageX - e.touches[1].pageX,
e.touches[0].pageY - e.touches[1].pageY
);
const vz = originZDis.current - (prePinchDis - pinchDis) * 0.01 * camZoomSpeed;
const vy = followCam.rotation.x;
if (vz >= camMaxDis && vz <= camMinDis) {
originZDis.current = vz;
followCam.position.z = originZDis.current * Math.cos(-vy);
followCam.position.y = originZDis.current * Math.sin(-vy);
}
}
previousTouch1 = touch1;
previousTouch2 = touch2;
};
const joystickCamMove = (movementX, movementY) => {
pivot.rotation.y -= movementX * 5e-3 * camMoveSpeed * 5;
const vy = followCam.rotation.x + movementY * 5e-3 * camMoveSpeed * 5;
cameraDistance = followCam.position.length();
if (vy >= camLowLimit && vy <= camUpLimit) {
followCam.rotation.x = vy;
followCam.position.y = -cameraDistance * Math.sin(-vy);
followCam.position.z = -cameraDistance * Math.cos(vy);
}
};
function customTraverseAdd(object) {
if (object.userData && object.userData.camExcludeCollision === true) {
return;
}
if (object.isMesh && object.visible) {
intersectObjects.current.push(object);
}
object.children.forEach((child) => {
customTraverseAdd(child);
});
}
function customTraverseRemove(object) {
intersectObjects.current = intersectObjects.current.filter(
(item) => item.uuid !== object.uuid
// Keep all items except the one to remove
);
object.children.forEach((child) => {
customTraverseRemove(child);
});
}
const cameraCollisionDetect = (delta) => {
cameraRayOrigin.copy(pivot.position);
camera.getWorldPosition(cameraPosition);
cameraRayDir.subVectors(cameraPosition, pivot.position);
intersects = camRayCast.intersectObjects(intersectObjects.current);
if (intersects.length && intersects[0].distance <= -originZDis.current) {
smallestDistance = Math.min(-intersects[0].distance * camCollisionOffset, camMinDis);
} else {
smallestDistance = originZDis.current;
}
camLerpingPoint.set(
followCam.position.x,
smallestDistance * Math.sin(-followCam.rotation.x),
smallestDistance * Math.cos(-followCam.rotation.x)
);
followCam.position.lerp(camLerpingPoint, 1 - Math.exp(-camCollisionSpeedMult * delta));
};
React.useEffect(() => {
pivot.rotation.y = camInitDir.y;
followCam.rotation.x = camInitDir.x;
scene.children.forEach((child) => customTraverseAdd(child));
pivot.add(followCam);
scene.add(pivot);
if (camListenerTarget === "domElement") {
gl.domElement.addEventListener("mousedown", () => {
isMouseDown = true;
});
gl.domElement.addEventListener("mouseup", () => {
isMouseDown = false;
});
gl.domElement.addEventListener("mousemove", onDocumentMouseMove);
gl.domElement.addEventListener("mousewheel", onDocumentMouseWheel);
gl.domElement.addEventListener("touchend", onTouchEnd);
gl.domElement.addEventListener("touchmove", onTouchMove, { passive: false });
} else if (camListenerTarget === "document") {
document.addEventListener("mousedown", () => {
isMouseDown = true;
});
document.addEventListener("mouseup", () => {
isMouseDown = false;
});
document.addEventListener("mousemove", onDocumentMouseMove);
document.addEventListener("mousewheel", onDocumentMouseWheel);
document.addEventListener("touchend", onTouchEnd);
document.addEventListener("touchmove", onTouchMove, { passive: false });
}
return () => {
if (camListenerTarget === "domElement") {
gl.domElement.removeEventListener("mousedown", () => {
isMouseDown = true;
});
gl.domElement.removeEventListener("mouseup", () => {
isMouseDown = false;
});
gl.domElement.removeEventListener("mousemove", onDocumentMouseMove);
gl.domElement.removeEventListener("mousewheel", onDocumentMouseWheel);
gl.domElement.removeEventListener("touchend", onTouchEnd);
gl.domElement.removeEventListener("touchmove", onTouchMove);
} else if (camListenerTarget === "document") {
document.removeEventListener("mousedown", () => {
isMouseDown = true;
});
document.removeEventListener("mouseup", () => {
isMouseDown = false;
});
document.removeEventListener("mousemove", onDocumentMouseMove);
document.removeEventListener("mousewheel", onDocumentMouseWheel);
document.removeEventListener("touchend", onTouchEnd);
document.removeEventListener("touchmove", onTouchMove);
}
};
}, []);
React.useEffect(() => {
if (disableFollowCam) {
if (disableFollowCamPos)
camera.position.set(disableFollowCamPos.x, disableFollowCamPos.y, disableFollowCamPos.z);
if (disableFollowCamTarget)
camera.lookAt(new THREE__namespace.Vector3(disableFollowCamTarget.x, disableFollowCamTarget.y, disableFollowCamTarget.z));
}
}, [disableFollowCam]);
React.useEffect(() => {
const onObjectAdded = (e) => customTraverseAdd(e.child);
const onObjectRemoved = (e) => customTraverseRemove(e.child);
scene.addEventListener("childadded", onObjectAdded);
scene.addEventListener("childremoved", onObjectRemoved);
return () => {
scene.removeEventListener("childadded", onObjectAdded);
scene.removeEventListener("childremoved", onObjectRemoved);
};
}, [scene]);
return { pivot, followCam, cameraCollisionDetect, joystickCamMove };
};
const useGame = /* @__PURE__ */ zustand.create(
/* @__PURE__ */ middleware.subscribeWithSelector((set, get) => {
return {
/**
* Point to move point
*/
moveToPoint: null,
/**
* Character animations state manegement
*/
// Initial animation
curAnimation: null,
animationSet: {},
initializeAnimationSet: (animationSet) => {
set((state) => {
if (Object.keys(state.animationSet).length === 0) {
return { animationSet };
}
return {};
});
},
reset: () => {
set((state) => {
return { curAnimation: state.animationSet.idle };
});
},
idle: () => {
set((state) => {
if (state.curAnimation === state.animationSet.jumpIdle) {
return { curAnimation: state.animationSet.jumpLand };
} else if (state.curAnimation !== state.animationSet.action1 && state.curAnimation !== state.animationSet.action2 && state.curAnimation !== state.animationSet.action3 && state.curAnimation !== state.animationSet.action4) {
return { curAnimation: state.animationSet.idle };
}
return {};
});
},
walk: () => {
set((state) => {
if (state.curAnimation !== state.animationSet.action4) {
return { curAnimation: state.animationSet.walk };
}
return {};
});
},
run: () => {
set((state) => {
if (state.curAnimation !== state.animationSet.action4) {
return { curAnimation: state.animationSet.run };
}
return {};
});
},
jump: () => {
set((state) => {
return { curAnimation: state.animationSet.jump };
});
},
jumpIdle: () => {
set((state) => {
if (state.curAnimation === state.animationSet.jump) {
return { curAnimation: state.animationSet.jumpIdle };
}
return {};
});
},
jumpLand: () => {
set((state) => {
if (state.curAnimation === state.animationSet.jumpIdle) {
return { curAnimation: state.animationSet.jumpLand };
}
return {};
});
},
fall: () => {
set((state) => {
return { curAnimation: state.animationSet.fall };
});
},
action1: () => {
set((state) => {
if (state.curAnimation === state.animationSet.idle) {
return { curAnimation: state.animationSet.action1 };
}
return {};
});
},
action2: () => {
set((state) => {
if (state.curAnimation === state.animationSet.idle) {
return { curAnimation: state.animationSet.action2 };
}
return {};
});
},
action3: () => {
set((state) => {
if (state.curAnimation === state.animationSet.idle) {
return { curAnimation: state.animationSet.action3 };
}
return {};
});
},
action4: () => {
set((state) => {
if (state.curAnimation === state.animationSet.idle || state.curAnimation === state.animationSet.walk || state.curAnimation === state.animationSet.run) {
return { curAnimation: state.animationSet.action4 };
}
return {};
});
},
/**
* Additional animations
*/
// triggerFunction: ()=>{
// set((state) => {
// return { curAnimation: state.animationSet.additionalAnimation };
// });
// }
/**
* Set/get point to move point
*/
setMoveToPoint: (point) => {
set(() => {
return { moveToPoint: point };
});
},
getMoveToPoint: () => {
return {
moveToPoint: get().moveToPoint
};
}
};
})
);
const useJoystickControls = /* @__PURE__ */ zustand.create(
/* @__PURE__ */ middleware.subscribeWithSelector((set, get) => {
return {
/**
* Joystick state manegement
*/
// Initial joystick/button state
curJoystickDis: 0,
curJoystickAng: 0,
curRunState: false,
curButton1Pressed: false,
curButton2Pressed: false,
curButton3Pressed: false,
curButton4Pressed: false,
curButton5Pressed: false,
setJoystick: (joystickDis, joystickAng, runState) => {
set(() => {
return {
curJoystickDis: joystickDis,
curJoystickAng: joystickAng,
curRunState: runState
};
});
},
resetJoystick: () => {
set((state) => {
if (state.curJoystickDis !== 0 || state.curJoystickAng !== 0) {
return {
curJoystickDis: 0,
curJoystickAng: 0,
curRunState: false
};
}
return {};
});
},
pressButton1: () => {
set((state) => {
if (!state.curButton1Pressed) {
return {
curButton1Pressed: true
};
}
return {};
});
},
pressButton2: () => {
set((state) => {
if (!state.curButton2Pressed) {
return {
curButton2Pressed: true
};
}
return {};
});
},
pressButton3: () => {
set((state) => {
if (!state.curButton3Pressed) {
return {
curButton3Pressed: true
};
}
return {};
});
},
pressButton4: () => {
set((state) => {
if (!state.curButton4Pressed) {
return {
curButton4Pressed: true
};
}
return {};
});
},
pressButton5: () => {
set((state) => {
if (!state.curButton5Pressed) {
return {
curButton5Pressed: true
};
}
return {};
});
},
releaseAllButtons: () => {
set((state) => {
if (state.curButton1Pressed) {
return {
curButton1Pressed: false
};
}
if (state.curButton2Pressed) {
return {
curButton2Pressed: false
};
}
if (state.curButton3Pressed) {
return {
curButton3Pressed: false
};
}
if (state.curButton4Pressed) {
return {
curButton4Pressed: false
};
}
if (state.curButton5Pressed) {
return {
curButton5Pressed: false
};
}
return {};
});
},
getJoystickValues: () => {
return {
joystickDis: get().curJoystickDis,
joystickAng: get().curJoystickAng,
runState: get().curRunState,
button1Pressed: get().curButton1Pressed,
button2Pressed: get().curButton2Pressed,
button3Pressed: get().curButton3Pressed,
button4Pressed: get().curButton4Pressed,
button5Pressed: get().curButton5Pressed
};
}
};
})
);
function EcctrlAnimation(props) {
const group = React.useRef(null);
const { animations } = drei.useGLTF(props.characterURL);
const { actions } = drei.useAnimations(animations, group);
const curAnimation = useGame((state) => state.curAnimation);
const resetAnimation = useGame((state) => state.reset);
const initializeAnimationSet = useGame(
(state) => state.initializeAnimationSet
);
React.useEffect(() => {
initializeAnimationSet(props.animationSet);
}, []);
React.useEffect(() => {
const key = curAnimation != null ? curAnimation : props.animationSet.jumpIdle;
const action = key ? actions[key] : null;
if (action === null)
return;
if (curAnimation === props.animationSet.jump || curAnimation === props.animationSet.jumpLand || curAnimation === props.animationSet.action1 || curAnimation === props.animationSet.action2 || curAnimation === props.animationSet.action3 || curAnimation === props.animationSet.action4) {
action.reset().fadeIn(0.2).setLoop(THREE__namespace.LoopOnce, 0).play();
action.clampWhenFinished = true;
} else {
action.reset().fadeIn(0.2).play();
}
action._mixer.addEventListener("finished", () => resetAnimation());
return () => {
action.fadeOut(0.2);
action._mixer.removeEventListener(
"finished",
() => resetAnimation()
);
action._mixer._listeners = [];
};
}, [curAnimation]);
return /* @__PURE__ */ React.createElement(React.Suspense, { fallback: null }, /* @__PURE__ */ React.createElement("group", { ref: group, dispose: null, userData: { camExcludeCollision: true } }, props.children));
}
const JoystickComponents = (props) => {
let joystickCenterX = 0;
let joystickCenterY = 0;
let joystickHalfWidth = 0;
let joystickHalfHeight = 0;
let joystickMaxDis = 0;
let joystickDis = 0;
let joystickAng = 0;
const touch1MovementVec2 = React.useMemo(() => new THREE__namespace.Vector2(), []);
const joystickMovementVec2 = React.useMemo(() => new THREE__namespace.Vector2(), []);
const [windowSize, setWindowSize] = React.useState({ innerHeight, innerWidth });
const joystickDiv = document.querySelector("#ecctrl-joystick");
const [springs, api] = three.useSpring(
() => ({
topRotationX: 0,
topRotationY: 0,
basePositionX: 0,
basePositionY: 0,
config: {
tension: 600
}
})
);
const joystickBaseGeo = React.useMemo(() => new THREE__namespace.CylinderGeometry(2.3, 2.1, 0.3, 16), []);
const joystickStickGeo = React.useMemo(() => new THREE__namespace.CylinderGeometry(0.3, 0.3, 3, 6), []);
const joystickHandleGeo = React.useMemo(() => new THREE__namespace.SphereGeometry(1.4, 8, 8), []);
const joystickBaseMaterial = React.useMemo(() => new THREE__namespace.MeshNormalMaterial({ transparent: true, opacity: 0.3 }), []);
const joystickStickMaterial = React.useMemo(() => new THREE__namespace.MeshNormalMaterial({ transparent: true, opacity: 0.3 }), []);
const joystickHandleMaterial = React.useMemo(() => new THREE__namespace.MeshNormalMaterial({ transparent: true, opacity: 0.7 }), []);
const setJoystick = useJoystickControls((state) => state.setJoystick);
const resetJoystick = useJoystickControls((state) => state.resetJoystick);
const onTouchMove = React.useCallback((e) => {
var _a;
e.preventDefault();
e.stopImmediatePropagation();
const touch1 = e.targetTouches[0];
const touch1MovementX = touch1.pageX - joystickCenterX;
const touch1MovementY = -(touch1.pageY - joystickCenterY);
touch1MovementVec2.set(touch1MovementX, touch1MovementY);
joystickDis = Math.min(Math.sqrt(Math.pow(touch1MovementX, 2) + Math.pow(touch1MovementY, 2)), joystickMaxDis);
joystickAng = touch1MovementVec2.angle();
joystickMovementVec2.set(joystickDis * Math.cos(joystickAng), joystickDis * Math.sin(joystickAng));
const runState = joystickDis > joystickMaxDis * ((_a = props.joystickRunSensitivity) != null ? _a : 0.9);
api.start({
topRotationX: -joystickMovementVec2.y / joystickHalfHeight,
topRotationY: joystickMovementVec2.x / joystickHalfWidth,
basePositionX: joystickMovementVec2.x * 2e-3,
basePositionY: joystickMovementVec2.y * 2e-3
});
setJoystick(joystickDis, joystickAng, runState);
}, [api, windowSize]);
const onTouchEnd = (e) => {
api.start({
topRotationX: 0,
topRotationY: 0,
basePositionX: 0,
basePositionY: 0
});
resetJoystick();
};
const onWindowResize = () => {
setWindowSize({ innerHeight: window.innerHeight, innerWidth: window.innerWidth });
};
React.useEffect(() => {
var _a;
if (!joystickDiv)
return;
const joystickPositionX = joystickDiv.getBoundingClientRect().x;
const joystickPositionY = joystickDiv.getBoundingClientRect().y;
joystickHalfWidth = joystickDiv.getBoundingClientRect().width / 2;
joystickHalfHeight = joystickDiv.getBoundingClientRect().height / 2;
joystickMaxDis = joystickHalfWidth * 0.65;
joystickCenterX = joystickPositionX + joystickHalfWidth;
joystickCenterY = joystickPositionY + joystickHalfHeight;
joystickDiv.addEventListener("touchmove", onTouchMove, { passive: false });
joystickDiv.addEventListener("touchend", onTouchEnd);
(_a = window.visualViewport) == null ? void 0 : _a.addEventListener("resize", onWindowResize);
return () => {
var _a2;
joystickDiv.removeEventListener("touchmove", onTouchMove);
joystickDiv.removeEventListener("touchend", onTouchEnd);
(_a2 = window.visualViewport) == null ? void 0 : _a2.removeEventListener("resize", onWindowResize);
};
});
return /* @__PURE__ */ React.createElement(React.Suspense, { fallback: "null" }, /* @__PURE__ */ React.createElement(three.animated.group, { "position-x": springs.basePositionX, "position-y": springs.basePositionY }, /* @__PURE__ */ React.createElement("mesh", { geometry: joystickBaseGeo, material: joystickBaseMaterial, rotation: [-Math.PI / 2, 0, 0], ...props.joystickBaseProps })), /* @__PURE__ */ React.createElement(three.animated.group, { "rotation-x": springs.topRotationX, "rotation-y": springs.topRotationY }, /* @__PURE__ */ React.createElement("mesh", { geometry: joystickStickGeo, material: joystickStickMaterial, rotation: [-Math.PI / 2, 0, 0], position: [0, 0, 1.5], ...props.joystickStickProps }), /* @__PURE__ */ React.createElement("mesh", { geometry: joystickHandleGeo, material: joystickHandleMaterial, position: [0, 0, 4], ...props.joystickHandleProps })));
};
const ButtonComponents = ({ buttonNumber = 1, ...props }) => {
const buttonLargeBaseGeo = React.useMemo(() => new THREE__namespace.CylinderGeometry(1.1, 1, 0.3, 16), []);
const buttonSmallBaseGeo = React.useMemo(() => new THREE__namespace.CylinderGeometry(0.9, 0.8, 0.3, 16), []);
const buttonTop1Geo = React.useMemo(() => new THREE__namespace.CylinderGeometry(0.9, 0.9, 0.5, 16), []);
const buttonTop2Geo = React.useMemo(() => new THREE__namespace.CylinderGeometry(0.9, 0.9, 0.5, 16), []);
const buttonTop3Geo = React.useMemo(() => new THREE__namespace.CylinderGeometry(0.7, 0.7, 0.5, 16), []);
const buttonTop4Geo = React.useMemo(() => new THREE__namespace.CylinderGeometry(0.7, 0.7, 0.5, 16), []);
const buttonTop5Geo = React.useMemo(() => new THREE__namespace.CylinderGeometry(0.7, 0.7, 0.5, 16), []);
const buttonBaseMaterial = React.useMemo(() => new THREE__namespace.MeshNormalMaterial({ transparent: true, opacity: 0.3 }), []);
const buttonTop1Material = React.useMemo(() => new THREE__namespace.MeshNormalMaterial({ transparent: true, opacity: 0.5 }), []);
const buttonTop2Material = React.useMemo(() => new THREE__namespace.MeshNormalMaterial({ transparent: true, opacity: 0.5 }), []);
const buttonTop3Material = React.useMemo(() => new THREE__namespace.MeshNormalMaterial({ transparent: true, opacity: 0.5 }), []);
const buttonTop4Material = React.useMemo(() => new THREE__namespace.MeshNormalMaterial({ transparent: true, opacity: 0.5 }), []);
const buttonTop5Material = React.useMemo(() => new THREE__namespace.MeshNormalMaterial({ transparent: true, opacity: 0.5 }), []);
const buttonDiv = document.querySelector("#ecctrl-button");
const [springs, api] = three.useSpring(
() => ({
buttonTop1BaseScaleY: 1,
buttonTop1BaseScaleXAndZ: 1,
buttonTop2BaseScaleY: 1,
buttonTop2BaseScaleXAndZ: 1,
buttonTop3BaseScaleY: 1,
buttonTop3BaseScaleXAndZ: 1,
buttonTop4BaseScaleY: 1,
buttonTop4BaseScaleXAndZ: 1,
buttonTop5BaseScaleY: 1,
buttonTop5BaseScaleXAndZ: 1,
config: {
tension: 600
}
})
);
const pressButton1 = useJoystickControls((state) => state.pressButton1);
const pressButton2 = useJoystickControls((state) => state.pressButton2);
const pressButton3 = useJoystickControls((state) => state.pressButton3);
const pressButton4 = useJoystickControls((state) => state.pressButton4);
const pressButton5 = useJoystickControls((state) => state.pressButton5);
const releaseAllButtons = useJoystickControls((state) => state.releaseAllButtons);
const onPointerDown = (number) => {
switch (number) {
case 1:
pressButton1();
api.start({
buttonTop1BaseScaleY: 0.5,
buttonTop1BaseScaleXAndZ: 1.15
});
break;
case 2:
pressButton2();
api.start({
buttonTop2BaseScaleY: 0.5,
buttonTop2BaseScaleXAndZ: 1.15
});
break;
case 3:
pressButton3();
api.start({
buttonTop3BaseScaleY: 0.5,
buttonTop3BaseScaleXAndZ: 1.15
});
break;
case 4:
pressButton4();
api.start({
buttonTop4BaseScaleY: 0.5,
buttonTop4BaseScaleXAndZ: 1.15
});
break;
case 5:
pressButton5();
api.start({
buttonTop5BaseScaleY: 0.5,
buttonTop5BaseScaleXAndZ: 1.15
});
break;
}
};
const onPointerUp = () => {
releaseAllButtons();
api.start({
buttonTop1BaseScaleY: 1,
buttonTop1BaseScaleXAndZ: 1,
buttonTop2BaseScaleY: 1,
buttonTop2BaseScaleXAndZ: 1,
buttonTop3BaseScaleY: 1,
buttonTop3BaseScaleXAndZ: 1,
buttonTop4BaseScaleY: 1,
buttonTop4BaseScaleXAndZ: 1,
buttonTop5BaseScaleY: 1,
buttonTop5BaseScaleXAndZ: 1
});
};
React.useEffect(() => {
if (!buttonDiv)
return;
buttonDiv.addEventListener("pointerup", onPointerUp);
return () => {
buttonDiv.removeEventListener("pointerup", onPointerUp);
};
});
return /* @__PURE__ */ React.createElement(React.Suspense, { fallback: "null" }, buttonNumber > 0 && /* @__PURE__ */ React.createElement(
three.animated.group,
{
"scale-x": springs.buttonTop1BaseScaleXAndZ,
"scale-y": springs.buttonTop1BaseScaleY,
"scale-z": springs.buttonTop1BaseScaleXAndZ,
rotation: [-Math.PI / 2, 0, 0],
position: props.buttonGroup1Position || (buttonNumber === 1 ? [0, 0, 0] : [2, 1, 0])
},
/* @__PURE__ */ React.createElement("mesh", { geometry: buttonLargeBaseGeo, material: buttonBaseMaterial, ...props.buttonLargeBaseProps, onPointerDown: () => onPointerDown(1) }),
/* @__PURE__ */ React.createElement("mesh", { geometry: buttonTop1Geo, material: buttonTop1Material, position: [0, -0.3, 0], ...props.buttonTop1Props })
), buttonNumber > 1 && /* @__PURE__ */ React.createElement(
three.animated.group,
{
"scale-x": springs.buttonTop2BaseScaleXAndZ,
"scale-y": springs.buttonTop2BaseScaleY,
"scale-z": springs.buttonTop2BaseScaleXAndZ,
rotation: [-Math.PI / 2, 0, 0],
position: props.buttonGroup2Position || [0.5, -1.3, 0]
},
/* @__PURE__ */ React.createElement("mesh", { geometry: buttonLargeBaseGeo, material: buttonBaseMaterial, ...props.buttonLargeBaseProps, onPointerDown: () => onPointerDown(2) }),
/* @__PURE__ */ React.createElement("mesh", { geometry: buttonTop2Geo, material: buttonTop2Material, position: [0, -0.3, 0], ...props.buttonTop2Props })
), buttonNumber > 2 && /* @__PURE__ */ React.createElement(
three.animated.group,
{
"scale-x": springs.buttonTop3BaseScaleXAndZ,
"scale-y": springs.buttonTop3BaseScaleY,
"scale-z": springs.buttonTop3BaseScaleXAndZ,
rotation: [-Math.PI / 2, 0, 0],
position: props.buttonGroup3Position || [-1, 1, 0]
},
/* @__PURE__ */ React.createElement("mesh", { geometry: buttonSmallBaseGeo, material: buttonBaseMaterial, ...props.buttonSmallBaseProps, onPointerDown: () => onPointerDown(3) }),
/* @__PURE__ */ React.createElement("mesh", { geometry: buttonTop3Geo, material: buttonTop3Material, position: [0, -0.3, 0], ...props.buttonTop3Props })
), buttonNumber > 3 && /* @__PURE__ */ React.createElement(
three.animated.group,
{
"scale-x": springs.buttonTop4BaseScaleXAndZ,
"scale-y": springs.buttonTop4BaseScaleY,
"scale-z": springs.buttonTop4BaseScaleXAndZ,
rotation: [-Math.PI / 2, 0, 0],
position: props.buttonGroup4Position || [-2, -1.3, 0]
},
/* @__PURE__ */ React.createElement("mesh", { geometry: buttonSmallBaseGeo, material: buttonBaseMaterial, ...props.buttonSmallBaseProps, onPointerDown: () => onPointerDown(4) }),
/* @__PURE__ */ React.createElement("mesh", { geometry: buttonTop4Geo, material: buttonTop4Material, position: [0, -0.3, 0], ...props.buttonTop4Props })
), buttonNumber > 4 && /* @__PURE__ */ React.createElement(
three.animated.group,
{
"scale-x": springs.buttonTop5BaseScaleXAndZ,
"scale-y": springs.buttonTop5BaseScaleY,
"scale-z": springs.buttonTop5BaseScaleXAndZ,
rotation: [-Math.PI / 2, 0, 0],
position: props.buttonGroup5Position || [0.4, 2.9, 0]
},
/* @__PURE__ */ React.createElement("mesh", { geometry: buttonSmallBaseGeo, material: buttonBaseMaterial, ...props.buttonSmallBaseProps, onPointerDown: () => onPointerDown(5) }),
/* @__PURE__ */ React.createElement("mesh", { geometry: buttonTop5Geo, material: buttonTop5Material, position: [0, -0.3, 0], ...props.buttonTop5Props })
));
};
const EcctrlJoystick = React.forwardRef((props, ref) => {
const joystickWrapperStyle = {
userSelect: "none",
MozUserSelect: "none",
WebkitUserSelect: "none",
msUserSelect: "none",
touchAction: "none",
pointerEvents: "none",
overscrollBehavior: "none",
position: "fixed",
zIndex: "9999",
height: props.joystickHeightAndWidth || "200px",
width: props.joystickHeightAndWidth || "200px",
left: props.joystickPositionLeft || "0",
bottom: props.joystickPositionBottom || "0"
};
const buttonWrapperStyle = {
userSelect: "none",
MozUserSelect: "none",
WebkitUserSelect: "none",
msUserSelect: "none",
touchAction: "none",
pointerEvents: "none",
overscrollBehavior: "none",
position: "fixed",
zIndex: "9999",
height: props.buttonHeightAndWidth || "200px",
width: props.buttonHeightAndWidth || "200px",
right: props.buttonPositionRight || "0",
bottom: props.buttonPositionBottom || "0"
};
return /* @__PURE__ */ React.createElement("div", { ref }, /* @__PURE__ */ React.createElement("div", { id: "ecctrl-joystick", style: joystickWrapperStyle, onContextMenu: (e) => e.preventDefault() }, /* @__PURE__ */ React.createElement(
fiber.Canvas,
{
shadows: true,
orthographic: true,
camera: {
zoom: props.joystickCamZoom || 26,
position: props.joystickCamPosition || [0, 0, 50]
}
},
/* @__PURE__ */ React.createElement(JoystickComponents, { ...props }),
props.children
)), props.buttonNumber !== 0 && /* @__PURE__ */ React.createElement("div", { id: "ecctrl-button", style: buttonWrapperStyle, onContextMenu: (e) => e.preventDefault() }, /* @__PURE__ */ React.createElement(
fiber.Canvas,
{
shadows: true,
orthographic: true,
camera: {
zoom: props.buttonCamZoom || 26,
position: props.buttonCamPosition || [0, 0, 50]
}
},
/* @__PURE__ */ React.createElement(ButtonComponents, { ...props }),
props.children
)));
});
const getMovingDirection = (forward, backward, leftward, rightward, pivot) => {
if (!forward && !backward && !leftward && !rightward)
return null;
if (forward && leftward)
return pivot.rotation.y + Math.PI / 4;
if (forward && rightward)
return pivot.rotation.y - Math.PI / 4;
if (backward && leftward)
return pivot.rotation.y - Math.PI / 4 + Math.PI;
if (backward && rightward)
return pivot.rotation.y + Math.PI / 4 + Math.PI;
if (backward)
return pivot.rotation.y + Math.PI;
if (leftward)
return pivot.rotation.y + Math.PI / 2;
if (rightward)
return pivot.rotation.y - Math.PI / 2;
if (forward)
return pivot.rotation.y;
return null;
};
const Ecctrl = ({
children,
debug = false,
capsuleHalfHeight = 0.35,
capsuleRadius = 0.3,
floatHeight = 0.3,
characterInitDir = 0,
// in rad
followLight = false,
disableControl = false,
disableFollowCam = false,
disableFollowCamPos = null,
disableFollowCamTarget = null,
// Follow camera setups
camInitDis = -5,
camMaxDis = -7,
camMinDis = -0.7,
camUpLimit = 1.5,
// in rad
camLowLimit = -1.3,
// in rad
camInitDir = { x: 0, y: 0 },
// in rad
camTargetPos = { x: 0, y: 0, z: 0 },
camMoveSpeed = 1,
camZoomSpeed = 1,
camCollision = true,
camCollisionOffset = 0.7,
camCollisionSpeedMult = 4,
fixedCamRotMult = 1,
camListenerTarget = "domElement",
// document or domElement
// Follow light setups
followLightPos = { x: 20, y: 30, z: 10 },
// Base control setups
maxVelLimit = 2.5,
turnVelMultiplier = 0.2,
turnSpeed = 15,
sprintMult = 2,
jumpVel = 4,
jumpForceToGroundMult = 5,
slopJumpMult = 0.25,
sprintJumpMult = 1.2,
airDragMultiplier = 0.2,
dragDampingC = 0.15,
accDeltaTime = 8,
rejectVelMult = 4,
moveImpulsePointY = 0.5,
camFollowMult = 11,
camLerpMult = 25,
fallingGravityScale = 2.5,
fallingMaxVel = -20,
wakeUpDelay = 200,
// Floating Ray setups
rayOriginOffest = { x: 0, y: -capsuleHalfHeight, z: 0 },
rayHitForgiveness = 0.1,
rayLength = capsuleRadius + 2,
rayDir = { x: 0, y: -1, z: 0 },
floatingDis = capsuleRadius + floatHeight,
springK = 1.2,
dampingC = 0.08,
// Slope Ray setups
showSlopeRayOrigin = false,
slopeMaxAngle = 1,
// in rad
slopeRayOriginOffest = capsuleRadius - 0.03,
slopeRayLength = capsuleRadius + 3,
slopeRayDir = { x: 0, y: -1, z: 0 },
slopeUpExtraForce = 0.1,
slopeDownExtraForce = 0.2,
// AutoBalance Force setups
autoBalance = true,
autoBalanceSpringK = 0.3,
autoBalanceDampingC = 0.03,
autoBalanceSpringOnY = 0.5,
autoBalanceDampingOnY = 0.015,
// Animation temporary setups
animated = false,
// Mode setups
mode = null,
// Controller setups
controllerKeys = { forward: 12, backward: 13, leftward: 14, rightward: 15, jump: 2, action1: 11, action2: 3, action3: 1, action4: 0 },
// Point-to-move setups
bodySensorSize = [capsuleHalfHeight / 2, capsuleRadius],
bodySensorPosition = { x: 0, y: 0, z: capsuleRadius / 2 },
// Other rigibody props from parent
...props
}, ref) => {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G;
const characterRef = React.useRef(null);
const characterModelRef = React.useRef(null);
const characterModelIndicator = React.useMemo(() => new THREE__namespace.Object3D(), []);
const defaultControllerKeys = { forward: 12, backward: 13, leftward: 14, rightward: 15, jump: 2, action1: 11, action2: 3, action3: 1, action4: 0 };
React.useImperativeHandle(ref, () => ({
get group() {
return characterRef.current;
},
rotateCamera,
rotateCharacterOnY
}), []);
let isModePointToMove = false;
let functionKeyDown = false;
let isModeFixedCamera = false;
let isModeCameraBased = false;
const setMoveToPoint = useGame((state) => state.setMoveToPoint);
const findMode = (mode2, modes) => modes.split(" ").some((m) => m === mode2);
if (mode) {
if (findMode("PointToMove", mode))
isModePointToMove = true;
if (findMode("FixedCamera", mode))
isModeFixedCamera = true;
if (findMode("CameraBasedMovement", mode))
isModeCameraBased = true;
}
const modelFacingVec = React.useMemo(() => new THREE__namespace.Vector3(), []);
const bodyFacingVec = React.useMemo(() => new THREE__namespace.Vector3(), []);
const bodyBalanceVec = React.useMemo(() => new THREE__namespace.Vector3(), []);
const bodyBalanceVecOnX = React.useMemo(() => new THREE__namespace.Vector3(), []);
const bodyFacingVecOnY = React.useMemo(() => new THREE__namespace.Vector3(), []);
const bodyBalanceVecOnZ = React.useMemo(() => new THREE__namespace.Vector3(), []);
const vectorY = React.useMemo(() => new THREE__namespace.Vector3(0, 1, 0), []);
const vectorZ = React.useMemo(() => new THREE__namespace.Vector3(0, 0, 1), []);
const crossVecOnX = React.useMemo(() => new THREE__namespace.Vector3(), []);
const crossVecOnY = React.useMemo(() => new THREE__namespace.Vector3(), []);
const crossVecOnZ = React.useMemo(() => new THREE__namespace.Vector3(), []);
const bodyContactForce = React.useMemo(() => new THREE__namespace.Vector3(), []);
const slopeRayOriginUpdatePosition = React.useMemo(() => new THREE__namespace.Vector3(), []);
const camBasedMoveCrossVecOnY = React.useMemo(() => new THREE__namespace.Vector3(), []);
const idleAnimation = !animated ? null : useGame((state) => state.idle);
const walkAnimation = !animated ? null : useGame((state) => state.walk);
const runAnimation = !animated ? null : useGame((state) => state.run);
const jumpAnimation = !animated ? null : useGame((state) => state.jump);
const jumpIdleAnimation = !animated ? null : useGame((state) => state.jumpIdle);
const fallAnimation = !animated ? null : useGame((state) => state.fall);
const action1Animation = !animated ? null : useGame((state) => state.action1);
const action2Animation = !animated ? null : useGame((state) => state.action2);
const action3Animation = !animated ? null : useGame((state) => state.action3);
const action4Animation = !animated ? null : useGame((state) => state.action4);
let characterControlsDebug = null;
let floatingRayDebug = null;
let slopeRayDebug = null;
let autoBalanceForceDebug = null;
characterControlsDebug = leva.useControls(
"Character Controls",
debug ? {
maxVelLimit: {
value: maxVelLimit,
min: 0,
max: 10,
step: 0.01
},
turnVelMultiplier: {
value: turnVelMultiplier,
min: 0,
max: 1,
step: 0.01
},
turnSpeed: {
value: turnSpeed,
min: 5,
max: 30,
step: 0.1
},
sprintMult: {
value: sprintMult,
min: 1,
max: 5,
step: 0.01
},
jumpVel: {
value: jumpVel,
min: 0,
max: 10,
step: 0.01
},
jumpForceToGroundMult: {
value: jumpForceToGroundMult,
min: 0,
max: 80,
step: 0.1
},
slopJumpMult: {
value: slopJumpMult,
min: 0,
max: 1,
step: 0.01
},
sprintJumpMult: {
value: sprintJumpMult,
min: 1,
max: 3,
step: 0.01
},
airDragMultiplier: {
value: airDragMultiplier,
min: 0,
max: 1,
step: 0.01
},
dragDampingC: {
value: dragDampingC,
min: 0,
max: 0.5,
step: 0.01
},
accDeltaTime: {
value: accDeltaTime,
min: 0,
max: 50,
step: 1
},
rejectVelMult: {
value: rejectVelMult,
min: 0,
max: 10,
step: 0.1
},
moveImpulsePointY: {
value: moveImpulsePointY,
min: 0,
max: 3,
step: 0.1
},
camFollowMult: {
value: camFollowMult,
min: 0,
max: 15,
step: 0.1
}
} : {},
{ collapsed: true }
);
if (debug) {
maxVelLimit = (_a = characterControlsDebug.maxVelLimit) != null ? _a : maxVelLimit;
turnVelMultiplier = (_b = characterControlsDebug.turnVelMultiplier) != null ? _b : turnVelMultiplier;
turnSpeed = (_c = characterControlsDebug.turnSpeed) != null ? _c : turnSpeed;
sprintMult = (_d = characterControlsDebug.sprintMult) != null ? _d : sprintMult;
jumpVel = (_e = characterControlsDebug.jumpVel) != null ? _e : jumpVel;
jumpForceToGroundMult = (_f = characterControlsDebug.jumpForceToGroundMult) != null ? _f : jumpForceToGroundMult;
slopJumpMult = (_g = characterControlsDebug.slopJumpMult) != null ? _g : slopJumpMult;
sprintJumpMult = (_h = characterControlsDebug.sprintJumpMult) != null ? _h : sprintJumpMult;
airDragMultiplier = (_i = characterControlsDebug.airDragMultiplier) != null ? _i : airDragMultiplier;
dragDampingC = (_j = characterControlsDebug.dragDampingC) != null ? _j : dragDampingC;
accDeltaTime = (_k = characterControlsDebug.accDeltaTime) != null ? _k : accDeltaTime;
rejectVelMult = (_l = characterControlsDebug.rejectVelMult) != null ? _l : rejectVelMult;
moveImpulsePointY = (_m = characterControlsDebug.moveImpulsePointY) != null ? _m : moveImpulsePointY;
camFollowMult = (_n = characterControlsDebug.camFollowMult) != null ? _n : camFollowMult;
}
floatingRayDebug = leva.useControls(
"Floating Ray",
debug ? {
rayOriginOffest: {
x: 0,
y: -capsuleHalfHeight,
z: 0
},
rayHitForgiveness: {
value: rayHitForgiveness,
min: 0,
max: 0.5,
step: 0.01
},
rayLength: {
value: capsuleRadius + 2,
min: 0,
max: capsuleRadius + 10,
step: 0.01
},
rayDir: { x: 0, y: -1, z: 0 },
floatingDis: {
value: capsuleRadius + floatHeight,
min: 0,
max: capsuleRadius + 2,
step: 0.01
},
springK: {
value: springK,
min: 0,
max: 5,
step: 0.01
},
dampingC: {
value: dampingC,
min: 0,
max: 3,
step: 0.01
}
} : {},
{ collapsed: true }
);
if (debug) {
rayOriginOffest = (_o = floatingRayDebug.rayOriginOffest) != null ? _o : rayOriginOffest;
rayHitForgiveness = (_p = floatingRayDebug.rayHitForgiveness) != null ? _p : rayHitForgiveness;
rayLength = (_q = floatingRayDebug.rayLength) != null ? _q : rayLength;
rayDir = (_r = floatingRayDebug.rayDir) != null ? _r : rayDir;
floatingDis = (_s = floatingRayDebug.floatingDis) != null ? _s : floatingDis;
springK = (_t = floatingRayDebug.springK) != null ? _t : springK;
dampingC = (_u = floatingRayDebug.dampingC) != null ? _u : dampingC;
}
slopeRayDebug = leva.useControls(
"Slope Ray",
debug ? {
showSlopeRayOrigin: false,
slopeMaxAngle: {
value: slopeMaxAngle,
min: 0,
max: 1.57,
step: 0.01
},
slopeRayOriginOffest: {
value: capsuleRadius,
min: 0,
max: capsuleRadius + 3,
step: 0.01
},
slopeRayLength: {
value: capsuleRadius + 3,
min: 0,
max: capsuleRadius + 13,
step: 0.01
},
slopeRayDir: { x: 0, y: -1, z: 0 },
slopeUpExtraForce: {
value: slopeUpExtraForce,
min: 0,
max: 5,
step: 0.01
},
slopeDownExtraForce: {
value: slopeDownExtraForce,
min: 0,
max: 5,
step: 0.01
}
} : {},
{ collapsed: true }
);
if (debug) {
showSlopeRayOrigin = (_v = slopeRayDebug.showSlopeRayOrigin) != null ? _v : showSlopeRayOrigin;
slopeMaxAngle = (_w = slopeRayDebug.slopeMaxAngle) != null ? _w : slopeMaxAngle;
slopeRayOriginOffest = (_x = slopeRayDebug.slopeRayOriginOffest) != null ? _x : slopeRayOriginOffest;
slopeRayLength = (_y = slopeRayDebug.slopeRayLength) != null ? _y : slopeRayLength;
slopeRayDir = (_z = slopeRayDebug.slopeRayDir) != null ? _z : slopeRayDir;
slopeUpExtraForce = (_A = slopeRayDebug.slopeUpExtraForce) != null ? _A : slopeUpExtraForce;
slopeDownExtraForce = (_B = slopeRayDebug.slopeDownExtraForce) != null ? _B : slopeDownExtraForce;
}
autoBalanceForceDebug = leva.useControls(
"AutoBalance Force",
debug ? {
autoBalance: {
value: true
},
autoBalanceSpringK: {
value: autoBalanceSpringK,
min: 0,
max: 5,
step: 0.01
},
autoBalanceDampingC: {
value: autoBalanceDampingC,
min: 0,
max: 0.1,
step: 1e-3
},
autoBalanceSpringOnY: {
value: autoBalanceSpringOnY,
min: 0,
max: 5,
step: 0.01
},
autoBalanceDampingOnY: {
value: autoBalanceDampingOnY,
min: 0,
max: 0.1,
step: 1e-3
}
} : {},
{ collapsed: true }
);
if (debug) {
autoBalance = (_C = autoBalanceForceDebug.autoBalance) != null ? _C : autoBalance;
autoBalanceSpringK = (_D = autoBalanceForceDebug.autoBalanceSpringK) != null ? _D : autoBalanceSpringK;
autoBalanceDampingC = (_E = autoBalanceForceDebug.autoBalanceDampingC) != null ? _E : autoBalanceDampingC;
autoBalanceSpringOnY = (_F = autoBalanceForceDebug.autoBalanceSpringOnY) != null ? _F : autoBalanceSpringOnY;
autoBalanceDampingOnY = (_G = autoBalanceForceDebug.autoBalanceDampingOnY) != null ? _G : autoBalanceDampingOnY;
}
function useIsInsideKeyboardControls() {
try {
return !!drei.useKeyboardControls();
} catch (e) {
return false;
}
}
const isInsideKeyboardControls = useIsInsideKeyboardControls();
const [subscribeKeys, getKeys] = isInsideKeyboardControls ? drei.useKeyboardControls() : [null];
const presetKeys = { forward: false, backward: false, leftward: false, rightward: false, jump: false, run: false };
const { rapier: rapier$1, world } = rapier.useRapier();
const getJoystickValues = useJoystickControls((state) => state.getJoystickValues);
const pressButton1 = useJoystickControls((state) => state.pressButton1);
const pressButton2 = useJoystickControls((state) => state.pressButton2);
const pressButton3 = useJoystickControls((state) => state.pressButton3);
const pressButton4 = useJoystickControls((state) => state.pressButton4);
const pressButton5 = useJoystickControls((state) => state.pressButton5);
const releaseAllButtons = useJoystickControls((state) => state.releaseAllButtons);
const setJoystick = useJoystickControls((state) => state.setJoystick);
const resetJoystick = useJoystickControls((state) => state.resetJoystick);
const [controllerIndex, setControllerIndex] = React.useState(null);
const gamepadKeys = { forward: false, backward: false, leftward: false, rightward: false };
const gamepadJoystickVec2 = React.useMemo(() => new THREE__namespace.Vector2(), []);
let gamepadJoystickDis = 0;
let gamepadJoystickAng = 0;
const gamepadConnect = (e) => {
setControllerIndex(e.gamepad.index);
};
const gamepadDisconnect = () => {
setControllerIndex(null);
};
const mergedKeys = React.useMemo(() => Object.assign({}, defaultControllerKeys, controllerKeys), [controllerKeys]);
const handleButtons = (buttons) => {
gamepadKeys.forward = buttons[mergedKeys.forward].pressed;
gamepadKeys.backward = buttons[mergedKeys.backward].pressed;
gamepadKeys.leftward = buttons[mergedKeys.leftward].pressed;
gamepadKeys.rightward = buttons[mergedKeys.rightward].pressed;
if (buttons[mergedKeys.action4].pressed) {
pressButton2();
} else if (buttons[mergedKeys.action3].pressed) {
pressButton4();
} else if (buttons[mergedKeys.jump].pressed) {
pressButton1();
} else if (buttons[mergedKeys.action2].pressed) {
pressButton3();
} else if (buttons[mergedKeys.action1].pressed) {
pressButton5();
} else {
releaseAllButtons();
}
};
const handleSticks = (axes) => {
if (Math.abs(axes[0]) > 0 || Math.abs(axes[1]) > 0) {
gamepadJoystickVec2.set(axes[0], -axes[1]);
gamepadJoystickDis = Math.min(Math.sqrt(Math.pow(gamepadJoystickVec2.x, 2