{"id":13773614,"url":"https://github.com/ErdongChen-Andrew/CharacterControl","last_synced_at":"2025-05-11T06:30:30.764Z","repository":{"id":170206120,"uuid":"644124595","full_name":"ErdongChen-Andrew/CharacterControl","owner":"ErdongChen-Andrew","description":"Floating Capsule Character Controller (Rigibody control)","archived":false,"fork":false,"pushed_at":"2024-11-26T02:12:15.000Z","size":49512,"stargazers_count":394,"open_issues_count":7,"forks_count":53,"subscribers_count":8,"default_branch":"main","last_synced_at":"2025-05-04T20:45:05.746Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://character-control.vercel.app","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ErdongChen-Andrew.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["ErdongChen-Andrew"],"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"custom":["https://www.paypal.me/andrewchenerdong"]}},"created_at":"2023-05-22T21:48:29.000Z","updated_at":"2025-04-26T17:15:23.000Z","dependencies_parsed_at":null,"dependency_job_id":"dc2769b8-70d6-40b9-93d7-f9f8904bf5a9","html_url":"https://github.com/ErdongChen-Andrew/CharacterControl","commit_stats":null,"previous_names":["erdongchen-andrew/charactercontrol"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ErdongChen-Andrew%2FCharacterControl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ErdongChen-Andrew%2FCharacterControl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ErdongChen-Andrew%2FCharacterControl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ErdongChen-Andrew%2FCharacterControl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ErdongChen-Andrew","download_url":"https://codeload.github.com/ErdongChen-Andrew/CharacterControl/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253528016,"owners_count":21922574,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-03T17:01:17.956Z","updated_at":"2025-05-11T06:30:30.752Z","avatar_url":"https://github.com/ErdongChen-Andrew.png","language":"TypeScript","funding_links":["https://github.com/sponsors/ErdongChen-Andrew","https://www.paypal.me/andrewchenerdong"],"categories":["TypeScript","Interaction"],"sub_categories":["Desktop Interaction"],"readme":"# Ecctrl Floating Capsule Character Controller\n\n[![screenshot](example/FloatingCharacterControl.png)](https://character-control.vercel.app/)\n\n[Pmndrs/ecctrl](https://github.com/pmndrs/ecctrl) is a simple web based character controller build on [react-three-fiber](https://github.com/pmndrs/react-three-fiber) and [react-three-rapier](https://github.com/pmndrs/react-three-rapier). It provides a playground demo where you can experience the following features:\n\n1. Seamless movement over small obstacles\n2. Enhanced control with floating force incorporating spring and damping forces\n3. Rigidbody character functionality for interaction with the game environment\n4. Customizable ground friction for tailored control\n5. Realistic simulation with applied mass on supporting surfaces\n6. Smooth integration with moving and rotating platforms\n\n## New Features\n\n### (2024-6-24) FixedCamera Mode:\n\n- The “FixedCamera” mode automatically rotates the camera as the character turns (similar to the controls in Coastal World). You can activate it with the following code:\n\n`\u003cEcctrl mode=\"FixedCamera\"\u003e`\n\n![screenshot](example/EcctrlFixedCamera.png)\n\nCheck out the [featurelog.md](/featurelog.md) for details on previous updates and features.\n\n## Project Link\n\nLive Demo: [Floating Capsule Character Controller](https://character-control.vercel.app/)\n\n## Local Setup\n\nDownload [Node.js](https://nodejs.org/en/download). Run this followed commands:\n\n```bash\n# Install dependencies (only the first time)\nnpm install\n\n# Run the local server at localhost:5173\nnpm run dev\n\n# Build for production in the example/exampleDist/ directory\nvite build -c vercelVite.config.js\n```\n\n## How To Use\n\n### Basic Controls ([CodeSandbox Demo](https://codesandbox.io/s/ecctrl-w-o-animations-3k3zxt))\n\n```bash\nnpm install ecctrl\n```\n\n```js\nimport Ecctrl, { EcctrlAnimation } from \"ecctrl\";\n```\n\nTo get started, set up your keyboard map using [KeyboardControls](https://github.com/pmndrs/drei#keyboardcontrols). Then, wrap your character model within `\u003cEcctrl\u003e`:\n\n```js\n/**\n * Keyboard control preset\n */\nconst keyboardMap = [\n  { name: \"forward\", keys: [\"ArrowUp\", \"KeyW\"] },\n  { name: \"backward\", keys: [\"ArrowDown\", \"KeyS\"] },\n  { name: \"leftward\", keys: [\"ArrowLeft\", \"KeyA\"] },\n  { name: \"rightward\", keys: [\"ArrowRight\", \"KeyD\"] },\n  { name: \"jump\", keys: [\"Space\"] },\n  { name: \"run\", keys: [\"Shift\"] },\n  // Optional animation key map\n  { name: \"action1\", keys: [\"1\"] },\n  { name: \"action2\", keys: [\"2\"] },\n  { name: \"action3\", keys: [\"3\"] },\n  { name: \"action4\", keys: [\"KeyF\"] },\n];\n\nreturn (\n  \u003c\u003e\n    ...\n    \u003cPhysics debug={physics} timeStep=\"vary\"\u003e\n      {/* Keyboard preset */}\n      \u003cKeyboardControls map={keyboardMap}\u003e\n        {/* Character Control */}\n        \u003cEcctrl\u003e\n          {/* Replace your model here */}\n          \u003cCharacterModel /\u003e\n        \u003c/Ecctrl\u003e\n      \u003c/KeyboardControls\u003e\n      ...\n    \u003c/Physics\u003e\n  \u003c/\u003e\n);\n```\n\nHere are all the default properties you can play with for `\u003cEcctrl\u003e`:\n\n```js\n// Default properties for Ecctrl\nEcctrlProps: {\n  children, // ReactNode\n  debug: false, // Enable debug mode (require leva package)\n  capsuleHalfHeight: 0.35, // Half-height of the character capsule\n  capsuleRadius: 0.3, // Radius of the character capsule\n  floatHeight: 0.3, // Height of the character when floating\n  characterInitDir: 0, // Character initial facing direction (in rad)\n  followLight: false, // Enable follow light mode (name your light \"followLight\" before turn this on)\n  disableControl: false, // Disable the ecctrl control feature\n  disableFollowCam: false, // Disable follow camera feature\n  disableFollowCamPos: { x: 0, y: 0, z: -5 }, // Camera position when the follow camera feature is disabled\n  disableFollowCamTarget: { x: 0, y: 0, z: 0 }, // Camera lookAt target when the follow camera feature is disabled\n  // Follow camera setups\n  camInitDis: -5, // Initial camera distance\n  camMaxDis: -7, // Maximum camera distance\n  camMinDis: -0.7, // Minimum camera distance\n  camUpLimit: 1.5, // Camera upward limit (in rad)\n  camLowLimit: -1.3, // Camera loward limit (in rad)\n  camInitDir: { x: 0, y: 0 }, // Camera initial rotation direction (in rad)\n  camTargetPos: { x: 0, y: 0, z: 0 }, // Camera target position\n  camMoveSpeed: 1, // Camera moving speed multiplier\n  camZoomSpeed: 1, // Camera zooming speed multiplier\n  camCollision: true, // Camera collision active/deactive\n  camCollisionOffset: 0.7, // Camera collision offset\n  camCollisionSpeedMult: 4, // Camera collision lerping speed multiplier\n  fixedCamRotMult: 1, // Camera rotate speed multiplier (FixedCamera mode)\n  camListenerTarget: \"domElement\", // Camera listener target (\"domElement\" | \"document\")\n   // Follow light setups\n  followLightPos: { x: 20, y: 30, z: 10 }, // Follow light position\n  // Base control setups\n  maxVelLimit: 2.5, // Maximum velocity limit\n  turnVelMultiplier: 0.2, // Turn velocity multiplier\n  turnSpeed: 15, // Turn speed\n  sprintMult: 2, // Sprint speed multiplier\n  jumpVel: 4, // Jump velocity\n  jumpForceToGroundMult: 5, // Jump force to ground object multiplier\n  slopJumpMult: 0.25, // Slope jump affect multiplier\n  sprintJumpMult: 1.2, // Sprint jump multiplier\n  airDragMultiplier: 0.2, // Air drag multiplier\n  dragDampingC: 0.15, // Drag damping coefficient\n  accDeltaTime: 8, // Acceleration delta time\n  rejectVelMult: 4, // Reject velocity multiplier\n  moveImpulsePointY: 0.5, // Move impulse point Y offset\n  camFollowMult: 11, // Camera follow target speed multiplier\n  camLerpMult: 25, // Camera lerp to position speed multiplier\n  fallingGravityScale: 2.5, // Character is falling, apply higher gravity\n  fallingMaxVel: -20, // Limit character max falling velocity\n  wakeUpDelay: 200, // Wake up character delay time after window visibility change to visible (in ms)\n  // Floating Ray setups\n  rayOriginOffest: { x: 0, y: -capsuleHalfHeight, z: 0 }, // Ray origin offset\n  rayHitForgiveness: 0.1, // Ray hit forgiveness\n  rayLength: capsuleRadius + 2, // Ray length\n  rayDir: { x: 0, y: -1, z: 0 }, // Ray direction\n  floatingDis: capsuleRadius + floatHeight, // Floating distance\n  springK: 1.2, // Spring constant\n  dampingC: 0.08, // Damping coefficient\n  // Slope Ray setups\n  showSlopeRayOrigin: false, // Show slope ray origin\n  slopeMaxAngle: 1, // in rad, the max walkable slope angle\n  slopeRayOriginOffest: capsuleRadius - 0.03, // Slope ray origin offset\n  slopeRayLength: capsuleRadius + 3, // Slope ray length\n  slopeRayDir: { x: 0, y: -1, z: 0 }, // Slope ray direction\n  slopeUpExtraForce: 0.1, // Slope up extra force\n  slopeDownExtraForce: 0.2, // Slope down extra force\n  // AutoBalance Force setups\n  autoBalance: true, // Enable auto-balance\n  autoBalanceSpringK: 0.3, // Auto-balance spring constant\n  autoBalanceDampingC: 0.03, // Auto-balance damping coefficient\n  autoBalanceSpringOnY: 0.5, // Auto-balance spring on Y-axis\n  autoBalanceDampingOnY: 0.015, // Auto-balance damping on Y-axis\n  // Animation temporary setups\n  animated: false, // Enable animation\n  // Mode setups\n  mode: null, // Activate different ecctrl modes (\"CameraBasedMovement\" | \"FixedCamera\" | \"PointToMove\")\n  // Customizable controller key setups\n  controllerKeys: { forward: 12, backward: 13, leftward: 14, rightward: 15, jump: 2, action1: 11, action2: 3, action3: 1, action4: 0 },\n  // Point-to-move setups\n  bodySensorSize: [capsuleHalfHeight / 2, capsuleRadius], // cylinder body sensor [halfHeight, radius]\n  bodySensorPosition: { x: 0, y: 0, z: capsuleRadius / 2 },\n  // Other rigibody props from parent\n  // Rigidbody props can be used here,\n  // such as position, friction, gravityScale, etc.\n  ...props\n}\n\n// Simply change the value by doing this\n\u003cEcctrl maxVelLimit={5} jumpVel={4} position={[0,10,0]}\u003e\n  \u003cCharacterModel /\u003e\n\u003c/Ecctrl\u003e\n```\n\n### Apply Character Animations ([CodeSandbox Demo](https://codesandbox.io/s/ecctrl-with-animations-nr4493))\n\nIf you want to apply character animations, prepare the character url and customize the `animationSet` with your own animation names. Change the `Ecctrl` property `animated` to true and wrap your character model inside `\u003cEcctrlAnimation\u003e` tag:\n\n```js\n// Prepare character model url\nconst characterURL = \"./ReplaceWithYourCharacterURL\";\n\n// Prepare and rename your character animations here\n// Note: idle, walk, run, jump, jumpIdle, jumpLand and fall names are essential\n// Missing any of these names might result in an error: \"cannot read properties of undifined (reading 'reset')\"\nconst animationSet = {\n  idle: \"Idle\",\n  walk: \"Walk\",\n  run: \"Run\",\n  jump: \"Jump_Start\",\n  jumpIdle: \"Jump_Idle\",\n  jumpLand: \"Jump_Land\",\n  fall: \"Climbing\", // This is for falling from high sky\n  // Currently support four additional animations\n  action1: \"Wave\",\n  action2: \"Dance\",\n  action3: \"Cheer\",\n  action4: \"Attack(1h)\", // This is special action which can be trigger while walking or running\n};\n\nreturn (\n  \u003c\u003e\n    ...\n    \u003cPhysics debug={physics} timeStep=\"vary\"\u003e\n      {/* Keyboard preset */}\n      \u003cKeyboardControls map={keyboardMap}\u003e\n        {/* Character Control */}\n        \u003cEcctrl animated\u003e\n          {/* Character Animations */}\n          \u003cEcctrlAnimation\n            characterURL={characterURL} // Must have property\n            animationSet={animationSet} // Must have property\n          \u003e\n            {/* Replace your model here */}\n            \u003cCharacterModel /\u003e\n          \u003c/EcctrlAnimation\u003e\n        \u003c/Ecctrl\u003e\n      \u003c/KeyboardControls\u003e\n      ...\n    \u003c/Physics\u003e\n  \u003c/\u003e\n);\n```\n\n### (Advanced) Add and Personalize Additional Animations\n\nFor advanced animation setups, download all files and follow these steps:\n\n1. In `CharacterModel.jsx`, expand the `animationSet` with additional animations:\n\n```js\n// Rename your character animations here\nconst animationSet = {\n  idle: \"Idle\",\n  walk: \"Walk\",\n  run: \"Run\",\n  jump: \"Jump_Start\",\n  jumpIdle: \"Jump_Idle\",\n  jumpLand: \"Jump_Land\",\n  fall: \"Climbing\",\n  action1: \"Wave\",\n  action2: \"Dance\",\n  action3: \"Cheer\",\n  action4: \"Attack(1h)\", // This is special action which can be trigger while walking or running\n  //additinalAnimation: \"additinalAnimationName\",\n};\n```\n\n2. In `useGame.jsx`, create a trigger function for the new animation:\n\n```js\n  return {\n      /**\n       * Character animations state manegement\n       */\n      // Initial animation\n      curAnimation: null,\n      animationSet: {},\n\n      ...\n\n      action1: () =\u003e {\n        set((state) =\u003e {\n          if (state.curAnimation === state.animationSet.idle) {\n            return { curAnimation: state.animationSet.action1 };\n          }\n          return {};\n        });\n      },\n\n      /**\n       * Additional animations\n       */\n      // triggerFunction: ()=\u003e{\n      //    set((state) =\u003e {\n      //        return { curAnimation: state.animationSet.additionalAnimation };\n      //    });\n      // }\n    };\n```\n\n3. In `CharacterController.jsx`, initialize the trigger function and call it when needed:\n\n```js\n// Animation change functions\nconst idleAnimation = useGame((state) =\u003e state.idle);\nconst walkAnimation = useGame((state) =\u003e state.walk);\nconst runAnimation = useGame((state) =\u003e state.run);\nconst jumpAnimation = useGame((state) =\u003e state.jump);\nconst jumpIdleAnimation = useGame((state) =\u003e state.jumpIdle);\nconst jumpLandAnimation = useGame((state) =\u003e state.jumpLand);\nconst fallAnimation = useGame((state) =\u003e state.fall);\nconst action1Animation = useGame((state) =\u003e state.action1);\nconst action2Animation = useGame((state) =\u003e state.action2);\nconst action3Animation = useGame((state) =\u003e state.action3);\nconst action4Animation = useGame((state) =\u003e state.action4);\n//const additionalAnimation = useGame((state) =\u003e state.triggerFunction);\n```\n\n### EcctrlJoystick and Touch buttons\n\nTo get start, simply import `EcctrlJoystick` from `ecctrl`\n\n```js\nimport { EcctrlJoystick } from \"ecctrl\";\n```\n\nPlace `\u003cEcctrlJoystick\u003e` outside of your canvas component, and you're done!\n\n```js\n//...\n  \u003cEcctrlJoystick /\u003e\n  \u003cCanvas\u003e\n    {/* ... */}\n  \u003c/Canvas\u003e\n//...\n```\n\nYou can also add lights or additional meshs like so (note: this will create components twice, once inside the joystick's scene, another inside the buttons' scene, so keep an eye on performance):\n\n```js\n//...\n  \u003cEcctrlJoystick\u003e\n    \u003cambientLight /\u003e\n    \u003cmesh\u003e\n      \u003cboxGeometry args={[1,1,1]} /\u003e\n    \u003c/mesh\u003e\n  \u003c/EcctrlJoystick\u003e\n  \u003cCanvas\u003e\n    {/* ... */}\n  \u003c/Canvas\u003e\n//...\n```\n\nAdditionally, you can change components' material, geometry, or texture as you like:\n\n```js\n//...\n  \u003cEcctrlJoystick\n    joystickBaseProps={{\n      receiveShadow: true,\n      material: new THREE.MeshStandardMaterial({ color: \"grey\" })\n    }}\n  /\u003e\n  \u003cCanvas\u003e\n    {/* ... */}\n  \u003c/Canvas\u003e\n//...\n```\n\nHere are all the properties you can play with for `\u003cEcctrlJoystick\u003e`:\n\n```js\nEcctrlJoystickProps: {\n    // Joystick props\n    children?: ReactNode;\n    joystickRunSensitivity?: number; // Sensitivity for transitioning to the running state. The default value is 0.9 (valid range: 0 \u003c joystickRunSensitivity \u003c 1)\n    joystickPositionLeft?: number; // joystick div container position left\n    joystickPositionBottom?: number; // joystick div container position bottom\n    joystickHeightAndWidth?: number; // joystick div container height and width\n    joystickCamZoom?: number; // camera zoom level for the joystick\n    joystickCamPosition?: [x: number, y: number, z: number]; // camera position for the joystick\n    joystickBaseProps?: ThreeElements['mesh']; // custom properties for the joystick's base mesh\n    joystickStickProps?: ThreeElements['mesh']; // custom properties for the joystick's stick mesh\n    joystickHandleProps?: ThreeElements['mesh']; // custom properties for the joystick's handle mesh\n\n    // Touch buttons props\n    buttonNumber?: number; // Number of buttons (max 5)\n    buttonPositionRight?: number; // buttons div container position right\n    buttonPositionBottom?: number; // buttons div container position bottom\n    buttonHeightAndWidth?: number; // buttons div container height and width\n    buttonCamZoom?: number; // camera zoom level for the buttons\n    buttonCamPosition?: [x: number, y: number, z: number]; // camera position for the buttons\n    buttonGroup1Position?: [x: number, y: number, z: number]; // button 1 posiiton in 3D scene\n    buttonGroup2Position?: [x: number, y: number, z: number]; // button 2 posiiton in 3D scene\n    buttonGroup3Position?: [x: number, y: number, z: number]; // button 3 posiiton in 3D scene\n    buttonGroup4Position?: [x: number, y: number, z: number]; // button 4 posiiton in 3D scene\n    buttonGroup5Position?: [x: number, y: number, z: number]; // button 5 posiiton in 3D scene\n    buttonLargeBaseProps?: ThreeElements['mesh']; // custom properties for the buttons' large base mesh\n    buttonSmallBaseProps?: ThreeElements['mesh']; // custom properties for the buttons' small base mesh\n    buttonTop1Props?: ThreeElements['mesh']; // custom properties for the button 1 top mesh (large button)\n    buttonTop2Props?: ThreeElements['mesh']; // custom properties for the button 2 top mesh (large button)\n    buttonTop3Props?: ThreeElements['mesh']; // custom properties for the button 3 top mesh (small button)\n    buttonTop4Props?: ThreeElements['mesh']; // custom properties for the button 4 top mesh (small button)\n    buttonTop5Props?: ThreeElements['mesh']; // custom properties for the button 5 top mesh (small button)\n};\n```\n\n### Using your own joystick or buttons\n\nIf you prefer to use your custom joystick or buttons, you can leverage the `useJoystickControls` hook from `ecctrl`. Import the hook and call the appropriate functions::\n\n```js\nimport { useJoystickControls } from \"ecctrl\";\n//...\nconst setJoystick = useJoystickControls((state) =\u003e state.setJoystick);\nconst resetJoystick = useJoystickControls((state) =\u003e state.resetJoystick);\nconst pressButton1 = useJoystickControls((state) =\u003e state.pressButton1);\nconst releaseAllButtons = useJoystickControls(\n  (state) =\u003e state.releaseAllButtons\n);\n//...\n// call the proper fuctions\nsetJoystick(joystickDis, joystickAng, runState);\n// or\npressButton1();\n```\n\n### Ecctrl Mode\n\nActivate different modes in Ecctrl by including the desired mode inside Ecctrl component:\n`\u003cEcctrl mode=\"PointToMove\"\u003e`.\n\n#### 1. \"PointToMove\" Mode ([CodeSandbox Demo](https://codesandbox.io/p/sandbox/ecctrl-pointtomove-m9z6xh?file=%2Fsrc%2FMap.js%3A46%2C19))\n\nThis mode doesn't require keyboard controls and is designed for click-to-move or path-following features.\n\n```js\nimport { useGame } from \"ecctrl\";\n// ...\nconst setMoveToPoint = useGame((state) =\u003e state.setMoveToPoint);\n// ...\n// call function setMoveToPoint(), whenever character needs to move\nsetMoveToPoint(point); // \"point\" is a vec3 value\n```\n\n### (Optional) First-person view setup\n\nIf you would like to quickly set up a first-person mode, you can modify these props to achieve that:\n\n```js\n\u003cEcctrl\n  camCollision={false} // disable camera collision detect (useless in FP mode)\n  camInitDis={-0.01} // camera intial position\n  camMinDis={-0.01} // camera zoom in closest position\n  camFollowMult={1000} // give a big number here, so the camera follows the target (character) instantly\n  camLerpMult={1000} // give a big number here, so the camera lerp to the followCam position instantly\n  turnVelMultiplier={1} // Turning speed same as moving speed\n  turnSpeed={100} // give it big turning speed to prevent turning wait time\n  mode=\"CameraBasedMovement\" // character's rotation will follow camera's rotation in this mode\n\u003e\n```\n\n## Contributions\n\nI appreciate your interest in this project! If you have any feedback, suggestions, or resources related to the controller, please feel free to share.\n\nThank you!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FErdongChen-Andrew%2FCharacterControl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FErdongChen-Andrew%2FCharacterControl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FErdongChen-Andrew%2FCharacterControl/lists"}