1"use strict";(self.webpackChunkrapier=self.webpackChunkrapier||[]).push([[5143],{3746:(e,t,r)=>{r.r(t),r.d(t,{assets:()=>u,contentTitle:()=>l,default:()=>p,frontMatter:()=>i,metadata:()=>c,toc:()=>d});var n=r(4848),a=r(8453),o=r(1470),s=r(9365);const i={},l=void 0,c={id:"user_guides/rust/character_controller_setup",title:"character_controller_setup",description:"The character controller implementation is exposed as the KinematicCharacterController structure. This structure only",source:"@site/docs/user_guides/rust/character_controller_setup.mdx",sourceDirName:"user_guides/rust",slug:"/user_guides/rust/character_controller_setup",permalink:"/docs/user_guides/rust/character_controller_setup",draft:!1,unlisted:!1,tags:[],version:"current",frontMatter:{}},u={},d=[];function h(e){const t={a:"a",admonition:"admonition",code:"code",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,n.jsxs)(n.Fragment,{children:[(0,n.jsxs)(t.p,{children:["The character controller implementation is exposed as the ",(0,n.jsx)(t.code,{children:"KinematicCharacterController"})," structure. This structure only\ncontains information about the character controller\u2019s behavior. It does not contain any collider-specific or\nrigid-body-specific information like handles, velocities, positions, etc. Therefore, the same instance of ",(0,n.jsx)(t.code,{children:"KinematicCharacterController"}),"\ncan be used to control multiple rigid-bodies/colliders if they rely on the same set of parameters.\nThe ",(0,n.jsx)(t.code,{children:"KinematicCharacterController"})," exposes only two methods:"]}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.code,{children:"move_shape"})," is responsible for calculating the possible movement of a character based on\nthe desired movement, obstacles, and character controller options."]}),"\n",(0,n.jsxs)(t.li,{children:[(0,n.jsx)(t.code,{children:"solve_character_collision_impulses"})," is detailed in the ",(0,n.jsx)(t.a,{href:"/docs/user_guides/rust/character_controller#collisions",children:"collisions section"}),"."]}),"\n"]}),"\n",(0,n.jsxs)(o.A,{groupId:"dim",defaultValue:"2D",values:[{label:"Example 2D",value:"2D"},{label:"Example 3D",value:"3D"}],children:[(0,n.jsx)(s.A,{value:"2D",children:(0,n.jsx)(t.pre,{children:(0,n.jsx)(t.code,{className:"language-rust",children:"// The translation we would like to apply if there were no obstacles.\nlet desired_translation = Vector::new(1.0, -2.0);\n// Create the character controller, here with the default configuration.\nlet character_controller = KinematicCharacterController::default();\n// Init the query pipeline.\nlet filter = QueryFilter::default()\n // Make sure the character we are trying to move isn\u2019t considered an obstacle.\n .exclude_rigid_body(rigid_body_handle);\nlet query_pipeline = broad_phase.as_query_pipeline(\n narrow_phase.query_dispatcher(),\n &bodies,\n &colliders,\n filter,\n);\n// Calculate the possible movement.\nlet corrected_movement = character_controller.move_shape(\n dt, // The timestep length (can be set to SimulationSettings::dt).\n &query_pipeline, // The query pipeline.\n character_shape, // The character\u2019s shape.\n character_pos, // The character\u2019s initial position.\n desired_translation,\n |_| {}, // We don\u2019t care about events in this example.\n);\n// TODO: apply the `corrected_movement.translation` to the rigid-body or collider based on the rules described below.\n"})})}),(0,n.jsx)(s.A,{value:"2D",children:(0,n.jsx)(t.pre,{children:(0,n.jsx)(t.code,{className:"language-rust",children:"// The translation we would like to apply if there were no obstacles.\nlet desired_translation = Vector::new(1.0, -2.0, 3.0);\n// Create the character controller, here with the default configuration.\nlet character_controller = KinematicCharacterController::default();\n// Init the query pipeline.\nlet filter = QueryFilter::default()\n // Make sure the character we are trying to move isn\u2019t considered an obstacle.\n .exclude_rigid_body(rigid_body_handle);\nlet query_pipeline = broad_phase.as_query_pipeline(\n narrow_phase.query_dispatcher(),\n &bodies,\n &c
1olliders,\n filter,\n);\n// Calculate the possible movement.\nlet corrected_movement = character_controller.move_shape(\n dt, // The timestep length (can be set to SimulationSettings::dt).\n &query_pipeline, // The query pipeline.\n character_shape, // The character\u2019s shape.\n character_pos, // The character\u2019s initial position.\n desired_translation,\n |_| {}, // We don\u2019t care about events in this example.\n);\n\n// TODO: apply the `corrected_movement.translation` to the rigid-body or collider based on the rules described below.\n"})})})]}),"\n",(0,n.jsx)(t.p,{children:"The recommended way to update the character\u2019s position depends on its representation:"}),"\n",(0,n.jsxs)(t.ul,{children:["\n",(0,n.jsxs)(t.li,{children:["A ",(0,n.jsx)(t.strong,{children:"collider"})," not attached to any rigid-body: set the collider\u2019s position directly to the corrected movement added to its current position."]}),"\n",(0,n.jsxs)(t.li,{children:["A ",(0,n.jsx)(t.strong,{children:"velocity-based kinematic rigid-body"}),": set its velocity to the computed movement divided by the timestep length."]}),"\n",(0,n.jsxs)(t.li,{children:["A ",(0,n.jsx)(t.strong,{children:"position-based kinematic rigid-body"}),": set its next kinematic position to the corrected movement added to its current position."]}),"\n"]}),"\n",(0,n.jsx)(t.admonition,{type:"info",children:(0,n.jsx)(t.p,{children:"The character\u2019s shape may be any shape supported by Rapier. However, it is recommended to either use a cuboid,\na ball, or a capsule since they involve less computations and less numerical approximations."})}),"\n",(0,n.jsx)(t.admonition,{type:"warning",children:(0,n.jsxs)(t.p,{children:["The built-in character controller ",(0,n.jsx)(t.strong,{children:"does not"})," support rotational movement. It only supports translations."]})})]})}function p(e={}){const{wrapper:t}={...(0,a.R)(),...e.components};return t?(0,n.jsx)(t,{...e,children:(0,n.jsx)(h,{...e})}):h(e)}},9365:(e,t,r)=>{r.d(t,{A:()=>s});r(6540);var n=r(8215);const a={tabItem:"tabItem_Ymn6"};var o=r(4848);function s(e){let{children:t,hidden:r,className:s}=e;return(0,o.jsx)("div",{role:"tabpanel",className:(0,n.A)(a.tabItem,s),hidden:r,children:t})}},1470:(e,t,r)=>{r.d(t,{A:()=>j});var n=r(6540),a=r(8215),o=r(3104),s=r(6347),i=r(205),l=r(7485),c=r(1682),u=r(679);function d(e){return n.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,n.isValidElement)(e)&&function(e){const{props:t}=e;return!!t&&"object"==typeof t&&"value"in t}(e))return e;throw new Error(`Docusaurus error: Bad <Tabs> child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the <Tabs> component should be <TabItem>, and every <TabItem> should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function h(e){const{values:t,children:r}=e;return(0,n.useMemo)((()=>{const e=t??function(e){return d(e).map((e=>{let{props:{value:t,label:r,attributes:n,default:a}}=e;return{value:t,label:r,attributes:n,default:a}}))}(r);return function(e){const t=(0,c.XI)(e,((e,t)=>e.value===t.value));if(t.length>0)throw new Error(`Docusaurus error: Duplicate values "${t.map((e=>e.value)).join(", ")}" found in <Tabs>. Every value needs to be unique.`)}(e),e}),[t,r])}function p(e){let{value:t,tabValues:r}=e;return r.some((e=>e.value===t))}function m(e){let{queryString:t=!1,groupId:r}=e;const a=(0,s.W6)(),o=function(e){let{queryString:t=!1,groupId:r}=e;if("string"==typeof t)return t;if(!1===t)return null;if(!0===t&&!r)throw new Error('Docusaurus error: The <Tabs> component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return r??null}({queryString:t,groupId:r});return[(0,l.aZ)(o),(0,n.useCallback)((e=>{if(!o)return;const t=new URLSearchParams(a.location.search);t.set(o,e),a.replace({...a.location,search:t.toString()})}),[o,a])]}function b(e){const{defaultValue:t,queryString:r=!1,groupId:a}
1=e,o=h(e),[s,l]=(0,n.useState)((()=>function(e){let{defaultValue:t,tabValues:r}=e;if(0===r.length)throw new Error("Docusaurus error: the <Tabs> component requires at least one <TabItem> children component");if(t){if(!p({value:t,tabValues:r}))throw new Error(`Docusaurus error: The <Tabs> has a defaultValue "${t}" but none of its children has the corresponding value. Available values are: ${r.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return t}const n=r.find((e=>e.default))??r[0];if(!n)throw new Error("Unexpected error: 0 tabValues");return n.value}({defaultValue:t,tabValues:o}))),[c,d]=m({queryString:r,groupId:a}),[b,f]=function(e){let{groupId:t}=e;const r=function(e){return e?`docusaurus.tab.${e}`:null}(t),[a,o]=(0,u.Dv)(r);return[a,(0,n.useCallback)((e=>{r&&o.set(e)}),[r,o])]}({groupId:a}),v=(()=>{const e=c??b;return p({value:e,tabValues:o})?e:null})();(0,i.A)((()=>{v&&l(v)}),[v]);return{selectedValue:s,selectValue:(0,n.useCallback)((e=>{if(!p({value:e,tabValues:o}))throw new Error(`Can't select invalid tab value=${e}`);l(e),d(e),f(e)}),[d,f,o]),tabValues:o}}var f=r(2303);const v={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var y=r(4848);function _(e){let{className:t,block:r,selectedValue:n,selectValue:s,tabValues:i}=e;const l=[],{blockElementScrollPositionUntilNextRender:c}=(0,o.a_)(),u=e=>{const t=e.currentTarget,r=l.indexOf(t),a=i[r].value;a!==n&&(c(t),s(a))},d=e=>{let t=null;switch(e.key){case"Enter":u(e);break;case"ArrowRight":{const r=l.indexOf(e.currentTarget)+1;t=l[r]??l[0];break}case"ArrowLeft":{const r=l.indexOf(e.currentTarget)-1;t=l[r]??l[l.length-1];break}}t?.focus()};return(0,y.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,a.A)("tabs",{"tabs--block":r},t),children:i.map((e=>{let{value:t,label:r,attributes:o}=e;return(0,y.jsx)("li",{role:"tab",tabIndex:n===t?0:-1,"aria-selected":n===t,ref:e=>l.push(e),onKeyDown:d,onClick:u,...o,className:(0,a.A)("tabs__item",v.tabItem,o?.className,{"tabs__item--active":n===t}),children:r??t},t)}))})}function g(e){let{lazy:t,children:r,selectedValue:o}=e;const s=(Array.isArray(r)?r:[r]).filter(Boolean);if(t){const e=s.find((e=>e.props.value===o));return e?(0,n.cloneElement)(e,{className:(0,a.A)("margin-top--md",e.props.className)}):null}return(0,y.jsx)("div",{className:"margin-top--md",children:s.map(((e,t)=>(0,n.cloneElement)(e,{key:t,hidden:e.props.value!==o})))})}function x(e){const t=b(e);return(0,y.jsxs)("div",{className:(0,a.A)("tabs-container",v.tabList),
1children:[(0,y.jsx)(_,{...t,...e}),(0,y.jsx)(g,{...t,...e})]})}function j(e){const t=(0,f.A)();return(0,y.jsx)(x,{...e,children:d(e.children)},String(t))}},8453:(e,t,r)=>{r.d(t,{R:()=>s,x:()=>i});var n=r(6540);const a={},o=n.createContext(a);function s(e){const t=n.useContext(o);return n.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function i(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:s(e.components),n.createElement(o.Provider,{value:t},e.children)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.