Skip to content

Commit ee53eb8

Browse files
feat: new autobalance function and forward ref
1 parent b11c40b commit ee53eb8

File tree

2 files changed

+54
-13
lines changed

2 files changed

+54
-13
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ecctrl",
3-
"version": "1.0.36",
3+
"version": "1.0.37",
44
"author": "Erdong Chen",
55
"license": "MIT",
66
"description": "A floating rigibody character controller for R3F",

src/Ecctrl.tsx

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { useKeyboardControls } from "@react-three/drei";
22
import { useFrame } from "@react-three/fiber";
33
import {
4+
quat,
45
RigidBody,
56
CapsuleCollider,
67
useRapier,
78
RapierRigidBody,
89
type RigidBodyProps,
910
} from "@react-three/rapier";
10-
import { useEffect, useRef, useMemo, type ReactNode } from "react";
11+
import { useEffect, useRef, useMemo, type ReactNode, forwardRef } from "react";
1112
import * as THREE from "three";
1213
import { useControls } from "leva";
1314
import { useFollowCam } from "./hooks/useFollowCam";
@@ -20,7 +21,7 @@ import type {
2021

2122
export { EcctrlAnimation } from "./EcctrlAnimation";
2223

23-
export default function Ecctrl({
24+
const Ecctrl = forwardRef<RapierRigidBody, EcctrlProps>(({
2425
children,
2526
debug = false,
2627
capsuleHalfHeight = 0.35,
@@ -68,15 +69,28 @@ export default function Ecctrl({
6869
autoBalance = true,
6970
autoBalanceSpringK = 0.3,
7071
autoBalanceDampingC = 0.03,
72+
autoBalanceSpringOnY = 0.3,
7173
autoBalanceDampingOnY = 0.02,
7274
// Animation temporary setups
7375
animated = false,
7476
// Other rigibody props from parent
7577
...props
76-
}: EcctrlProps) {
77-
const characterRef = useRef<RapierRigidBody>();
78+
}: EcctrlProps, ref) => {
79+
const characterRef = ref || useRef<RapierRigidBody>()
7880
const characterModelRef = useRef<THREE.Group>();
7981

82+
/**
83+
* Body collider setup
84+
*/
85+
const modelFacingVec = useMemo(() => new THREE.Vector3(), []);
86+
const bodyFacingVec = useMemo(() => new THREE.Vector3(), []);
87+
const bodyBalanceVec = useMemo(() => new THREE.Vector3(), []);
88+
const bodyBalanceVecOnX = useMemo(() => new THREE.Vector3(), []);
89+
const bodyFacingVecOnY = useMemo(() => new THREE.Vector3(), []);
90+
const bodyBalanceVecOnZ = useMemo(() => new THREE.Vector3(), []);
91+
const vectorY = useMemo(() => new THREE.Vector3(0, 1, 0), []);
92+
const vectorZ = useMemo(() => new THREE.Vector3(0, 0, 1), []);
93+
8094
// Animation change functions
8195
const idleAnimation = !animated ? null : useGame((state) => state.idle);
8296
const walkAnimation = !animated ? null : useGame((state) => state.walk);
@@ -327,6 +341,12 @@ export default function Ecctrl({
327341
max: 0.1,
328342
step: 0.001,
329343
},
344+
autoBalanceSpringOnY: {
345+
value: autoBalanceSpringOnY,
346+
min: 0,
347+
max: 5,
348+
step: 0.01,
349+
},
330350
autoBalanceDampingOnY: {
331351
value: autoBalanceDampingOnY,
332352
min: 0,
@@ -340,6 +360,7 @@ export default function Ecctrl({
340360
autoBalance = autoBalanceForceDebug.autoBalance;
341361
autoBalanceSpringK = autoBalanceForceDebug.autoBalanceSpringK;
342362
autoBalanceDampingC = autoBalanceForceDebug.autoBalanceDampingC;
363+
autoBalanceSpringOnY = autoBalanceForceDebug.autoBalanceSpringOnY;
343364
autoBalanceDampingOnY = autoBalanceForceDebug.autoBalanceDampingOnY;
344365
}
345366

@@ -562,15 +583,32 @@ export default function Ecctrl({
562583
* Character auto balance function
563584
*/
564585
const autoBalanceCharacter = () => {
586+
bodyFacingVec.set(0, 0, 1).applyQuaternion(quat(characterRef.current.rotation()))
587+
bodyBalanceVec.set(0, 1, 0).applyQuaternion(quat(characterRef.current.rotation()))
588+
589+
bodyBalanceVecOnX.set(0, bodyBalanceVec.y, bodyBalanceVec.z)
590+
bodyFacingVecOnY.set(bodyFacingVec.x, 0, bodyFacingVec.z)
591+
bodyBalanceVecOnZ.set(bodyBalanceVec.x, bodyBalanceVec.y, 0)
592+
593+
characterModelRef.current.getWorldDirection(modelFacingVec)
594+
const crossVecOnX = vectorY.clone().cross(bodyBalanceVecOnX);
595+
const crossVecOnY = vectorZ.clone().cross(bodyFacingVecOnY);
596+
const crossVecOnZ = vectorY.clone().cross(bodyBalanceVecOnZ);
597+
565598
dragAngForce.set(
566-
-autoBalanceSpringK * characterRef.current.rotation().x -
567-
characterRef.current.angvel().x * autoBalanceDampingC,
568-
-autoBalanceSpringK * characterRef.current.rotation().y -
569-
characterRef.current.angvel().y * autoBalanceDampingOnY,
570-
-autoBalanceSpringK * characterRef.current.rotation().z -
571-
characterRef.current.angvel().z * autoBalanceDampingC
599+
(crossVecOnX.x < 0 ? 1 : -1) *
600+
autoBalanceSpringK * (bodyBalanceVecOnX.angleTo(vectorY))
601+
- characterRef.current.angvel().x * autoBalanceDampingC,
602+
(crossVecOnY.y < 0 ? 1 : -1) *
603+
autoBalanceSpringOnY * (bodyFacingVecOnY.angleTo(vectorZ))
604+
- characterRef.current.angvel().y * autoBalanceDampingOnY,
605+
(crossVecOnZ.z < 0 ? 1 : -1) *
606+
autoBalanceSpringK * (bodyBalanceVecOnZ.angleTo(vectorY))
607+
- characterRef.current.angvel().z * autoBalanceDampingC,
572608
);
573-
characterRef.current.applyTorqueImpulse(dragAngForce, false);
609+
610+
// Apply balance torque impulse
611+
characterRef.current.applyTorqueImpulse(dragAngForce, false)
574612
};
575613

576614
useEffect(() => {
@@ -1021,7 +1059,9 @@ export default function Ecctrl({
10211059
</group>
10221060
</RigidBody>
10231061
);
1024-
}
1062+
})
1063+
1064+
export default Ecctrl
10251065

10261066
export interface EcctrlProps extends RigidBodyProps {
10271067
children?: ReactNode;
@@ -1071,6 +1111,7 @@ export interface EcctrlProps extends RigidBodyProps {
10711111
autoBalance?: boolean;
10721112
autoBalanceSpringK?: number;
10731113
autoBalanceDampingC?: number;
1114+
autoBalanceSpringOnY?: number;
10741115
autoBalanceDampingOnY?: number;
10751116
// Animation temporary setups
10761117
animated?: boolean;

0 commit comments

Comments
 (0)