Simple web based character controller build on react-three-fiber and react-three-rapier. It provides a playground demo where you can experience the following features:
- Seamless movement over small obstacles
- Enhanced control with floating force incorporating spring and damping forces
- Rigidbody character functionality for interaction with the game environment
- Customizable ground friction for tailored control
- Realistic simulation with applied mass on supporting surfaces
- Smooth integration with moving and rotating platforms
- Incorporate 8 built-in dynamic animations (including 3 for jump actions)
- Flexibility to add and personalize additional animations
- Fine-tune slope angle's impact on jump direction (fully customizable)
- Tailor the rejection velocity for sudden changes in movement direction (fully customizable)
- Collision detection
- Zoom in/out capability
- Expanded movement range
- Improved tracking smoothness
- Character tilts forward/backward while in motion
- Automatically returns to upright position after a hit or attack
- Stability customization: Users can fine-tune the balance sensitivity to match their gameplay style
Live Demo: Floating Capsule Character Controller
Download Node.js. Run this followed commands:
# Install dependencies (only the first time)
npm install
# Run the local server at localhost:8080
npm run dev
# Build for production in the dist/ directory
npm run build
To get started, set up your keyboard map using KeyboardControls. Then, replace <CharacterModel>
with <YourModel>
inside Experience.jsx
:
/**
* Keyboard control preset
*/
const keyboardMap = [
{ name: "forward", keys: ["ArrowUp", "KeyW"] },
{ name: "backward", keys: ["ArrowDown", "KeyS"] },
{ name: "leftward", keys: ["ArrowLeft", "KeyA"] },
{ name: "rightward", keys: ["ArrowRight", "KeyD"] },
{ name: "jump", keys: ["Space"] },
{ name: "run", keys: ["Shift"] },
{ name: "trigger", keys: ["KeyF"] },
];
return (
<>
...
<Physics debug={physics} timeStep="vary">
{/* Keyboard preset */}
<KeyboardControls map={keyboardMap}>
{/* Character Control */}
<CharacterController>
{/* Replace your model here */}
<CharacterModel />
</CharacterController>
</KeyboardControls>
...
</Physics>
</>
);
If you want use your own character animations, customize the animationSet
in CharacterModel.jsx
with your animation names. Also, make sure to adjust the useGLTF
src to your model:
// Change the character src to yours
const character = useGLTF("./Animated Platformer Character.glb");
...
// Rename your character animations here
const animationSet = {
idle: "CharacterArmature|Idle",
walk: "CharacterArmature|Walk",
run: "CharacterArmature|Run",
jump: "CharacterArmature|Jump",
jumpIdle: "CharacterArmature|Jump_Idle",
jumpLand: "CharacterArmature|Jump_Land",
duck: "CharacterArmature|Duck", // This is for falling from high sky
wave: "CharacterArmature|Wave",
};
For advanced animation setups, follow these steps:
- In
CharacterModel.jsx
, expand theanimationSet
with additional animations:
// Rename your character animations here
const animationSet = {
idle: "CharacterArmature|Idle",
walk: "CharacterArmature|Walk",
run: "CharacterArmature|Run",
jump: "CharacterArmature|Jump",
jumpIdle: "CharacterArmature|Jump_Idle",
jumpLand: "CharacterArmature|Jump_Land",
duck: "CharacterArmature|Duck",
wave: "CharacterArmature|Wave",
//additinalAnimation: "additinalAnimationName",
};
- In
useGame.jsx
, create a trigger function for the new animation:
return {
/**
* Character animations state manegement
*/
// Initial animation
curAnimation: null,
animationSet: {},
...
wave: () => {
set((state) => {
if (state.curAnimation === state.animationSet.idle) {
return { curAnimation: state.animationSet.wave };
}
return {};
});
},
/**
* Additional animations
*/
// triggerFunction: ()=>{
// set((state) => {
// return { curAnimation: state.animationSet.additionalAnimation };
// });
// }
};
- In
CharacterController.jsx
, initialize the trigger function and call it when needed:
// Animation change functions
const idleAnimation = useGame((state) => state.idle);
const walkAnimation = useGame((state) => state.walk);
const runAnimation = useGame((state) => state.run);
const jumpAnimation = useGame((state) => state.jump);
const jumpIdleAnimation = useGame((state) => state.jumpIdle);
const jumpLandAnimation = useGame((state) => state.jumpLand);
const duckAnimation = useGame((state) => state.duck);
const waveAnimation = useGame((state) => state.wave);
//const additionalAnimation = useGame((state) => state.triggerFunction);
I appreciate your interest in this project! If you have any feedback, suggestions, or resources related to the controller, please feel free to share.
Thank you!