Skip to content

Commit ad652df

Browse files
author
ledouxm
committed
feat: add typescript
1 parent 503d614 commit ad652df

27 files changed

+588
-264
lines changed

.vscode/settings.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"workbench.colorCustomizations": {
3+
"commandCenter.border": "#e7e7e799",
4+
"sash.hoverBorder": "#4452a4",
5+
"titleBar.activeBackground": "#354080",
6+
"titleBar.activeForeground": "#e7e7e7",
7+
"titleBar.inactiveBackground": "#35408099",
8+
"titleBar.inactiveForeground": "#e7e7e799"
9+
},
10+
"peacock.color": "#354080"
11+
}

ecctrlNPMPackage/Ecctrl.jsx renamed to ecctrlNPMPackage/Ecctrl.tsx

Lines changed: 122 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,23 @@
11
import { useKeyboardControls } from "@react-three/drei";
22
import { useFrame } from "@react-three/fiber";
3-
import { RigidBody, CapsuleCollider, useRapier } from "@react-three/rapier";
4-
import { useEffect, useRef, useMemo } from "react";
3+
import {
4+
RigidBody,
5+
CapsuleCollider,
6+
useRapier,
7+
RapierRigidBody,
8+
} from "@react-three/rapier";
9+
import { useEffect, useRef, useMemo, type ReactNode } from "react";
510
import * as THREE from "three";
611
import { useControls } from "leva";
7-
import useFollowCam from "./hooks/useFollowCam";
8-
import useGame from "./stores/useGame";
12+
import { useFollowCam } from "./hooks/useFollowCam";
13+
import { useGame } from "./stores/useGame";
14+
import type {
15+
Collider,
16+
RayColliderToi,
17+
Vector,
18+
} from "@dimforge/rapier3d-compat";
919

10-
export {EcctrlAnimation} from "./EcctrlAnimation"
20+
export { EcctrlAnimation } from "./EcctrlAnimation";
1121

1222
export default function Ecctrl({
1323
children,
@@ -57,10 +67,9 @@ export default function Ecctrl({
5767
autoBalanceDampingOnY = 0.02,
5868
// Animation temporary setups
5969
animated = false,
60-
...props
61-
}) {
62-
const characterRef = useRef();
63-
const characterModelRef = useRef();
70+
}: EcctrlProps) {
71+
const characterRef = useRef<RapierRigidBody>();
72+
const characterModelRef = useRef<THREE.Group>();
6473

6574
// Animation change functions
6675
const idleAnimation = !animated ? null : useGame((state) => state.idle);
@@ -345,7 +354,7 @@ export default function Ecctrl({
345354
/**
346355
* Initial light setup
347356
*/
348-
let dirLight = null;
357+
let dirLight: THREE.DirectionalLight = null;
349358

350359
/**
351360
* Follow camera initial setups from props
@@ -384,28 +393,33 @@ export default function Ecctrl({
384393
const characterMassForce = useMemo(() => new THREE.Vector3(), []);
385394
const rayOrigin = useMemo(() => new THREE.Vector3(), []);
386395
const rayCast = new rapier.Ray(rayOrigin, rayDir);
387-
let rayHit = null;
396+
let rayHit: RayColliderToi = null;
388397

389398
/**Test shape ray */
390399
// const shape = new rapier.Capsule(0.2,0.1)
391400

392401
/**
393402
* Slope detection ray setup
394403
*/
395-
let slopeAngle = null;
396-
let actualSlopeNormal = null;
397-
let actualSlopeAngle = null;
404+
let slopeAngle: number = null;
405+
let actualSlopeNormal: Vector = null;
406+
let actualSlopeAngle: number = null;
398407
const actualSlopeNormalVec = useMemo(() => new THREE.Vector3(), []);
399408
const floorNormal = useMemo(() => new THREE.Vector3(0, 1, 0), []);
400-
const slopeRayOriginRef = useRef();
409+
const slopeRayOriginRef = useRef<THREE.Mesh>();
401410
const slopeRayorigin = useMemo(() => new THREE.Vector3(), []);
402411
const slopeRayCast = new rapier.Ray(slopeRayorigin, slopeRayDir);
403-
let slopeRayHit = null;
412+
let slopeRayHit: RayColliderToi = null;
404413

405414
/**
406415
* Character moving function
407416
*/
408-
const moveCharacter = (delta, run, slopeAngle, movingObjectVelocity) => {
417+
const moveCharacter = (
418+
_: number,
419+
run: boolean,
420+
slopeAngle: number,
421+
movingObjectVelocity: THREE.Vector3
422+
) => {
409423
/**
410424
* Setup moving direction
411425
*/
@@ -520,11 +534,15 @@ export default function Ecctrl({
520534
}
521535

522536
// Move character at proper direction and impulse
523-
characterRef.current.applyImpulseAtPoint(moveImpulse, {
524-
x: currentPos.x,
525-
y: currentPos.y + moveImpulsePointY,
526-
z: currentPos.z,
527-
});
537+
characterRef.current.applyImpulseAtPoint(
538+
moveImpulse,
539+
{
540+
x: currentPos.x,
541+
y: currentPos.y + moveImpulsePointY,
542+
z: currentPos.z,
543+
},
544+
false
545+
);
528546
};
529547

530548
/**
@@ -539,7 +557,7 @@ export default function Ecctrl({
539557
-autoBalanceSpringK * characterRef.current.rotation().z -
540558
characterRef.current.angvel().z * autoBalanceDampingC
541559
);
542-
characterRef.current.applyTorqueImpulse(dragAngForce);
560+
characterRef.current.applyTorqueImpulse(dragAngForce, false);
543561
};
544562

545563
useEffect(() => {
@@ -549,7 +567,7 @@ export default function Ecctrl({
549567
(item) => {
550568
return item.type === "DirectionalLight";
551569
}
552-
);
570+
) as THREE.DirectionalLight;
553571
}
554572

555573
// Action 1 key subscribe for special animation
@@ -605,14 +623,15 @@ export default function Ecctrl({
605623
characterRef.current.setEnabledRotations(
606624
autoBalance ? true : false,
607625
autoBalance ? true : false,
608-
autoBalance ? true : false
626+
autoBalance ? true : false,
627+
false
609628
);
610629
}, [autoBalance]);
611630

612631
useFrame((state, delta) => {
613632
// Character current position
614633
if (characterRef.current) {
615-
currentPos.copy(characterRef.current.translation());
634+
currentPos.copy(characterRef.current.translation() as THREE.Vector3);
616635
}
617636

618637
/**
@@ -664,7 +683,7 @@ export default function Ecctrl({
664683

665684
// Character current velocity
666685
if (characterRef.current) {
667-
currentVel.copy(characterRef.current.linvel());
686+
currentVel.copy(characterRef.current.linvel() as THREE.Vector3);
668687
}
669688

670689
// Jump impulse
@@ -680,7 +699,8 @@ export default function Ecctrl({
680699
jumpDirection
681700
.set(0, (run ? sprintJumpMult * jumpVel : jumpVel) * slopJumpMult, 0)
682701
.projectOnVector(actualSlopeNormalVec)
683-
.add(jumpVelocityVec)
702+
.add(jumpVelocityVec),
703+
false
684704
);
685705
// Apply jump force downward to the standing platform
686706
characterMassForce.y *= jumpForceToGroundMult;
@@ -710,14 +730,15 @@ export default function Ecctrl({
710730
/**
711731
* Ray casting detect if on ground
712732
*/
713-
rayOrigin.addVectors(currentPos, rayOriginOffest);
733+
rayOrigin.addVectors(currentPos, rayOriginOffest as THREE.Vector3);
714734
rayHit = world.castRay(
715735
rayCast,
716736
rayLength,
717737
false,
718738
null,
719739
null,
720-
characterRef.current
740+
// I have no idea
741+
characterRef.current as unknown as Collider
721742
);
722743
/**Test shape ray */
723744
// rayHit = world.castShape(
@@ -765,11 +786,15 @@ export default function Ecctrl({
765786
// Calculate distance between character and moving object
766787
distanceFromCharacterToObject
767788
.copy(currentPos)
768-
.sub(rayHit.collider.parent().translation());
789+
.sub(rayHit.collider.parent().translation() as THREE.Vector3);
769790
// Moving object linear velocity
770-
const movingObjectLinvel = rayHit.collider.parent().linvel();
791+
const movingObjectLinvel = rayHit.collider
792+
.parent()
793+
.linvel() as THREE.Vector3;
771794
// Moving object angular velocity
772-
const movingObjectAngvel = rayHit.collider.parent().angvel();
795+
const movingObjectAngvel = rayHit.collider
796+
.parent()
797+
.angvel() as THREE.Vector3;
773798
// Combine object linear velocity and angular velocity to movingObjectVelocity
774799
movingObjectVelocity.set(
775800
movingObjectLinvel.x +
@@ -823,14 +848,16 @@ export default function Ecctrl({
823848
false,
824849
null,
825850
null,
826-
characterRef.current
851+
// Still no idea
852+
characterRef.current as unknown as Collider
827853
);
828854

829855
// Calculate slope angle
830856
if (slopeRayHit) {
831857
actualSlopeNormal = slopeRayHit.collider.castRayAndGetNormal(
832858
slopeRayCast,
833-
slopeRayLength
859+
slopeRayLength,
860+
false
834861
)?.normal;
835862
if (actualSlopeNormal) {
836863
actualSlopeNormalVec?.set(
@@ -843,9 +870,12 @@ export default function Ecctrl({
843870
}
844871
if (slopeRayHit && rayHit && slopeRayHit.toi < floatingDis + 0.5) {
845872
if (canJump) {
846-
slopeAngle = Math.atan(
847-
(rayHit.toi - slopeRayHit.toi) / slopeRayOriginOffest
848-
).toFixed(2);
873+
// Round the slope angle to 2 decimal places
874+
slopeAngle = Number(
875+
Math.atan(
876+
(rayHit.toi - slopeRayHit.toi) / slopeRayOriginOffest
877+
).toFixed(2)
878+
);
849879
} else {
850880
slopeAngle = null;
851881
}
@@ -862,7 +892,8 @@ export default function Ecctrl({
862892
springK * (floatingDis - rayHit.toi) -
863893
characterRef.current.linvel().y * dampingC;
864894
characterRef.current.applyImpulse(
865-
springDirVec.set(0, floatingForce, 0)
895+
springDirVec.set(0, floatingForce, 0),
896+
false
866897
);
867898

868899
// Apply opposite force to standing object (gravity g in rapier is 0.11 ?_?)
@@ -884,7 +915,7 @@ export default function Ecctrl({
884915
0,
885916
-currentVel.z * dragDampingC
886917
);
887-
characterRef.current.applyImpulse(dragForce);
918+
characterRef.current.applyImpulse(dragForce, false);
888919
}
889920
// on a moving object
890921
else {
@@ -893,7 +924,7 @@ export default function Ecctrl({
893924
0,
894925
(movingObjectVelocity.z - currentVel.z) * dragDampingC * 2
895926
);
896-
characterRef.current.applyImpulse(dragForce);
927+
characterRef.current.applyImpulse(dragForce, false);
897928
}
898929
}
899930

@@ -966,3 +997,53 @@ export default function Ecctrl({
966997
</RigidBody>
967998
);
968999
}
1000+
1001+
export type EcctrlProps = {
1002+
children?: ReactNode;
1003+
debug?: boolean;
1004+
capsuleHalfHeight?: number;
1005+
capsuleRadius?: number;
1006+
floatHeight?: number;
1007+
followLight?: boolean;
1008+
// Follow camera setups
1009+
camInitDis?: number;
1010+
camMaxDis?: number;
1011+
camMinDis?: number;
1012+
// Base control setups
1013+
maxVelLimit?: number;
1014+
turnVelMultiplier?: number;
1015+
turnSpeed?: number;
1016+
sprintMult?: number;
1017+
jumpVel?: number;
1018+
jumpForceToGroundMult?: number;
1019+
slopJumpMult?: number;
1020+
sprintJumpMult?: number;
1021+
airDragMultiplier?: number;
1022+
dragDampingC?: number;
1023+
accDeltaTime?: number;
1024+
rejectVelMult?: number;
1025+
moveImpulsePointY?: number;
1026+
camFollowMult?: number;
1027+
// Floating Ray setups
1028+
rayOriginOffest?: { x: number; y: number; z: number };
1029+
rayHitForgiveness?: number;
1030+
rayLength?: number;
1031+
rayDir?: { x: number; y: number; z: number };
1032+
floatingDis?: number;
1033+
springK?: number;
1034+
dampingC?: number;
1035+
// Slope Ray setups
1036+
showSlopeRayOrigin?: boolean;
1037+
slopeRayOriginOffest?: number;
1038+
slopeRayLength?: number;
1039+
slopeRayDir?: { x: number; y: number; z: number };
1040+
slopeUpExtraForce?: number;
1041+
slopeDownExtraForce?: number;
1042+
// AutoBalance Force setups
1043+
autoBalance?: boolean;
1044+
autoBalanceSpringK?: number;
1045+
autoBalanceDampingC?: number;
1046+
autoBalanceDampingOnY?: number;
1047+
// Animation temporary setups
1048+
animated?: boolean;
1049+
};

ecctrlNPMPackage/EcctrlAnimation.jsx renamed to ecctrlNPMPackage/EcctrlAnimation.tsx

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { useGLTF, useAnimations } from "@react-three/drei";
22
import { useEffect, useRef, Suspense } from "react";
33
import * as THREE from "three";
4-
import useGame from "./stores/useGame";
4+
import { useGame, type AnimationSet } from "./stores/useGame";
5+
import type { GLTF } from "three/examples/jsm/loaders/GLTFLoader";
56

6-
export function EcctrlAnimation(props) {
7+
export function EcctrlAnimation(props: EcctrlAnimationProps) {
78
// Change the character src to yours
89
const group = useRef();
9-
const { animations } = useGLTF(props.characterURL);
10+
const { animations } = useGLTF(props.characterURL) as GLTF;
1011
const { actions } = useAnimations(animations, group);
1112

1213
/**
@@ -37,34 +38,39 @@ export function EcctrlAnimation(props) {
3738
curAnimation === props.animationSet.action3 ||
3839
curAnimation === props.animationSet.action4
3940
) {
40-
action.reset().fadeIn(0.2).setLoop(THREE.LoopOnce).play();
41+
action.reset().fadeIn(0.2).setLoop(THREE.LoopOnce, 0).play();
4142
action.clampWhenFinished = true;
4243
} else {
4344
action.reset().fadeIn(0.2).play();
4445
}
4546

4647
// When any action is clamp and finished reset animation
47-
action._mixer.addEventListener("finished", () => resetAnimation());
48+
(action as any)._mixer.addEventListener("finished", () => resetAnimation());
4849

4950
return () => {
5051
// Fade out previous action
5152
action.fadeOut(0.2);
5253

5354
// Clean up mixer listener, and empty the _listeners array
54-
action._mixer.removeEventListener("finished", () => resetAnimation());
55-
action._mixer._listeners = [];
55+
(action as any)._mixer.removeEventListener("finished", () =>
56+
resetAnimation()
57+
);
58+
(action as any)._mixer._listeners = [];
5659
};
5760
}, [curAnimation]);
5861

5962
return (
6063
<Suspense fallback={null}>
61-
<group
62-
ref={group}
63-
dispose={null}
64-
>
64+
<group ref={group} dispose={null}>
6565
{/* Replace character model here */}
6666
{props.children}
6767
</group>
6868
</Suspense>
6969
);
7070
}
71+
72+
export type EcctrlAnimationProps = {
73+
characterURL: string;
74+
animationSet: AnimationSet;
75+
children: React.ReactNode;
76+
};

0 commit comments

Comments
 (0)