Controller instances can be used as a component in a custom character implementation.
The Class.ControllerManager instance manages simulated motion control for its assigned Class.ControllerManager.RootPart|RootPart. Along with Class.ControllerPartSensor|ControllerPartSensors, it can be used to build a physics‑based character controller.
Core setup
Class.ControllerManager requires a Class.BasePart to use as its root. Movement forces and part sensing will be on this part.
Choose a Class.Part or Class.MeshPart and name it RootPart.
Group the part as a Class.Model instance for organization along with the other components.
Add a Class.ControllerManager instance to the model. If ControllerManager doesn't initially appear in the object insertion menu, uncheck "Show only recommended objects" in the menu's insertion settings.
Sensor setup
A Class.ControllerPartSensor detects parts with the same code the Class.Humanoid uses for detecting floors and ladders.
Insert a Class.ControllerPartSensor as a child of RootPart and rename it GroundSensor for easier identification of its purpose. Then, in the Properties window, set its Class.ControllerPartSensor.SearchDistance|SearchDistance property to 2 but leave its Class.ControllerPartSensor.SensorMode|SensorMode as Floor.
Insert another Class.ControllerPartSensor as a child of RootPart and rename it ClimbSensor. Then, in the Properties window, set its Class.ControllerPartSensor.SearchDistance|SearchDistance property to 1 and its Class.ControllerPartSensor.SensorMode|SensorMode to Ladder.
Controller setup
Controller instances like Class.GroundController and Class.ClimbController tell the managed part how to interact with the world, working alongside the sensors you configured during the sensor setup.
Insert both a Class.GroundController and Class.ClimbController as children of the Class.ControllerManager.
Select the new Class.GroundController instance and then, in the Properties window, set its Class.GroundController.GroundOffset|GroundOffset property to a value at which the managed part should "levitate" above the ground. It's important that this value is less than the value of Class.ControllerPartSensor.SearchDistance|SearchDistance for the GroundSensor, since that sensor will deactivate if it loses sense of the ground and effectively stop its forces on the part.
Link references
To complete the core setup, you'll need to link various properties of the Class.ControllerManager instance to objects within the main Class.Model.
Select the Class.ControllerManager instance.
In the Properties window, click each of the following properties and then, back in the Explorer window, click the respective instance to complete the link.
Link the Class.ControllerManager.ActiveController|ActiveController property to the GroundController instance.
Link the Class.ControllerManager.RootPart|RootPart property to the part you named RootPart.
Link the Class.ControllerManager.ClimbSensor|ClimbSensor property to the ClimbSensor instance.
Link the Class.ControllerManager.GroundSensor|GroundSensor property to the GroundSensor instance.
Initiate a playtest using the Run mode (F8) since you don't need to insert your avatar character in this scenario.
RootPart should levitate above the ground at the Class.GroundController.GroundOffset value you set in Adding Controllers. It should also rotate to align with the Class.ControllerManager.FacingDirection vector.
Experiment with different movement and facing directions by changing the Class.ControllerManager.MovingDirection|MovingDirection and Class.ControllerManager.FacingDirection|FacingDirection vectors of the Class.ControllerManager during runtime. Also experiment with different properties of the Class.GroundController instance such as Class.GroundController.AccelerationTime|AccelerationTime, Class.GroundController.DecelerationTime|DecelerationTime, and Class.GroundController.GroundOffset|GroundOffset.
ControllerManagerGroundController
Remember that `Class.GroundController.GroundOffset|GroundOffset` must be **less** than the value of `Class.ControllerPartSensor.SearchDistance|SearchDistance` for the [GroundSensor](#sensor-setup), since that sensor will deactivate if it loses sense of the ground and effectively stop its forces on the part. This will cause a bouncing effect as gravity and the `Class.GroundController` repeatedly swap control of the part.
Custom sensors
The Class.ControllerPartSensor.SensorMode options of Enum.SensorMode|Floor and Enum.SensorMode|Ladder run the exact Class.Humanoid sensor code, letting you use them for backwards compatibility. However, you can also customize how and when walkable and climbable parts are detected, ultimately changing when the managed part walks/climbs.
Switch the Class.ControllerPartSensor.UpdateType from OnRead to Manual.
OnRead triggers the output properties to update corresponding to your Class.ControllerPartSensor.SensorMode|SensorMode every time you read them. It essentially puts the sensor in a "read‑only" mode where anything you write to these properties becomes overwritten by its internal sensing behavior.
Manual turns off the internal sensing behavior. The output properties are free for you to write however you like and the Class.ControllerPartSensor.SensorMode|SensorMode does nothing.
Create a script that performs your own sensing logic and writes the outputs to your sensor output properties. Typically you'll use a spatial query such as Class.WorldRoot:Raycast() or Class.WorldRoot:Blockcast() which returns a Datatype.RaycastResult. Your script can then take the result properties and set them on the sensor equivalents: