Skip to content

Commit 3b2bdc9

Browse files
noah-wardlowclaude
andcommitted
docs: simplify controller examples — plain hook first, factory second
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 7fd37b6 commit 3b2bdc9

1 file changed

Lines changed: 20 additions & 24 deletions

File tree

README.md

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -87,43 +87,37 @@ function MyComponent() {
8787

8888
## Writing a Controller
8989

90-
A controller is a hook that uses handle-based access for type-safe actuator and sensor control:
90+
A controller is a hook that reads sensors and writes actuators each physics step:
9191

9292
```tsx
93-
import { createControllerHook, useCtrl, useSensor, useBeforePhysicsStep } from "mujoco-react";
93+
import { useCtrl, useSensor, useBeforePhysicsStep } from "mujoco-react";
9494

95-
export const useMyController = createControllerHook<{ gain: number }, { amplitude: number }>(
96-
{ name: "useMyController", defaultConfig: { gain: 1.0 } },
97-
(config) => {
98-
const shoulder = useCtrl("shoulder");
99-
const elbow = useCtrl("elbow");
100-
const force = useSensor("force_sensor");
101-
102-
useBeforePhysicsStep(() => {
103-
if (!config) return;
104-
shoulder.write(config.gain * Math.sin(Date.now() / 1000));
105-
elbow.write(force.read()[0] * -0.5);
106-
});
107-
108-
return config ? { amplitude: shoulder.read() } : null;
109-
},
110-
);
95+
function useMyController(gain: number) {
96+
const shoulder = useCtrl("shoulder");
97+
const elbow = useCtrl("elbow");
98+
const force = useSensor("force_sensor");
11199

112-
// const result = useMyController({ gain: 2.0 });
113-
// const disabled = useMyController(null); // returns null, no-ops
100+
useBeforePhysicsStep(() => {
101+
shoulder.write(gain * Math.sin(Date.now() / 1000));
102+
elbow.write(force.read()[0] * -0.5);
103+
});
104+
}
114105
```
115106

116-
For controllers that render children (debug overlays, context providers, etc.), use the component factory:
107+
For reusable controllers with typed config, default merging, and children, use the `createController` factory:
117108

118109
```tsx
119-
import { createController, useBeforePhysicsStep } from "mujoco-react";
110+
import { createController, useCtrl, useBeforePhysicsStep } from "mujoco-react";
120111

121112
export const MyController = createController<{ gain: number }>(
122113
{ name: "MyController", defaultConfig: { gain: 1.0 } },
123114
({ config, children }) => {
124-
useBeforePhysicsStep((_model, data) => {
125-
data.ctrl[0] = config.gain * Math.sin(data.time);
115+
const shoulder = useCtrl("shoulder");
116+
117+
useBeforePhysicsStep(() => {
118+
shoulder.write(config.gain * Math.sin(Date.now() / 1000));
126119
});
120+
127121
return <>{children}</>;
128122
},
129123
);
@@ -133,6 +127,8 @@ export const MyController = createController<{ gain: number }>(
133127
// </MyController>
134128
```
135129

130+
A `createControllerHook` factory is also available for the hook equivalent — see the [Building Controllers](https://dadd.mintlify.app/guides/building-controllers) guide.
131+
136132
## Architecture
137133

138134
`<MujocoCanvas>` wraps R3F `<Canvas>` and forwards all Canvas props (`camera`, `shadows`, `gl`, etc.). For full control over the Canvas, use `<MujocoPhysics>` inside your own:

0 commit comments

Comments
 (0)