OpenXR action map: configure input
Sets, actions, controller profiles, an executed C# resource example and binding diagnostics.
An action map connects input meaning to XR-controller components. Game code checks an action such as select; the map specifies which control triggers it on each device profile. This is separate from ordinary InputMap: adding a Project Settings → Input Map action does not create an OpenXR binding.
Map structure
| Resource | Purpose | Example |
|---|---|---|
| OpenXRActionMap | Root map resource | openxr_action_map.rdata |
| OpenXRActionSet | Actions for one game mode | gameplay, menu |
| OpenXRAction | Named action and value type | select, Bool |
| OpenXRInteractionProfile | Bindings for a controller profile | /interaction_profiles/khr/simple_controller |
| OpenXRIPBinding | One action and one complete input path | /user/hand/right/input/select/click |
ResourceName stores the internal name; LocalizedName is the human-readable label. Prefer short, stable lowercase internal names such as select or move_player. Do not use translated labels as game-code keys. Action names and labels must be unique within their set; the XR runtime checks them when creating actions. OpenXR name and subaction-path requirements.
ToplevelPaths identifies devices for the action, such as left and right hands. BindingPath identifies a specific component on such a device. Two hands require two bindings to the same action, not a comma-separated pair of paths in one string.
Start with one action
A minimal selection map:
- Create set
gameplay, label Gameplay, Priority=0. - Add action
select, label Select, type Bool. - Set ToplevelPaths to
/user/hand/leftand/user/hand/right. - Add profile
/interaction_profiles/khr/simple_controller. - Add two bindings to
select:/user/hand/left/input/select/clickand/user/hand/right/input/select/click. - Save to
asset://openxr_action_map.rdata.
Simple Controller describes a minimal input subset. It provides neither sticks nor every controller-specific feature or guaranteed support on every headset. For more input, add the relevant profiles and paths allowed for those profiles. Application bindings are suggestions to the runtime, not direct access to raw buttons. OpenXR suggested-binding rules.
Create the same map in C#
This helper builds and saves a resource. It is not an attachable Entity script or XR-session startup code.
using Reckfell;
public static class SelectionActionMap
{
// Caller owns the map. This builds resources; it does not start an XR session.
public static OpenXRActionMap Create()
{
var map = new OpenXRActionMap();
using var set = new OpenXRActionSet
{
ResourceName = "gameplay", LocalizedName = "Gameplay", Priority = 0
};
using var select = new OpenXRAction
{
ResourceName = "select", LocalizedName = "Select",
ActionType = OpenXRAction.ActionTypeEnum.Bool,
ToplevelPaths = ["/user/hand/left", "/user/hand/right"]
};
set.AddAction(select);
map.AddActionSet(set);
using var profile = new OpenXRInteractionProfile
{
InteractionProfilePath = "/interaction_profiles/khr/simple_controller"
};
using var left = new OpenXRIPBinding
{
Action = select, BindingPath = "/user/hand/left/input/select/click"
};
using var right = new OpenXRIPBinding
{
Action = select, BindingPath = "/user/hand/right/input/select/click"
};
using var bindings = new Reckfell.Collections.Array { left, right };
profile.Bindings = bindings;
map.AddInteractionProfile(profile);
return map;
}
public static Error Save(string path = "asset://openxr_action_map.rdata")
{
using var map = Create();
return ResourceSaver.Save(map, path);
}
}From a separate resource-preparation tool, call SelectionActionMap.Save() and check for Error.Ok. By default it writes asset://openxr_action_map.rdata in the project executing the call. Prepare the file before starting the XR project. Saving a new map in _Ready() of an already running XR session does not reconnect its current actions.
The editor exposes the same structure in its OpenXR Action Map dock. Enable xr/openxr/enabled and restart the editor when requested. xr/openxr/default_action_map must point to the intended file; its default is asset://openxr_action_map.rdata. This setting matters more than matching the example filename.
If no map can be loaded, native code creates a default map. A tools-enabled build also attempts to save it. Do not rely on this to create your intended game actions in an exported project: keep your own map in the project and check its export inclusion.
Read an action
Select the relevant XRController3D hand tracker: left_hand or right_hand. After the XR session starts successfully and has focus, query actions by internal name:
| Action type | XRController3D C# API | Meaning |
|---|---|---|
| Bool | IsButtonPressed("select") | Current pressed state, not a one-frame event |
| Float | GetFloat("trigger") | Analog value of the configured action |
| Vector2 | GetVector2("move") | Two axes of the configured action |
| Pose | Pose property on the XR object | Selects a named tracked pose, not a button |
First define and bind trigger, move and the required pose; the minimal map above contains only select. It does not replace XROrigin3D, camera or XR startup configuration.
The map editor offers a Haptic type. However, current installed C# OpenXRAction.ActionTypeEnum has no named Haptic member: do not use a nonexistent member or substitute a numeric cast. Configure that action in the map editor. It must target a supported output, such as /user/hand/right/output/haptic, before calling TriggerHapticPulse. That call uses seconds for duration; OpenXRHapticVibration.Duration for modifiers uses nanoseconds. A binding alone does not establish working vibration on a device.
Sets and map changes
Start with one set. Later separate gameplay and menu when one controller component should serve different purposes. OpenXRInterface.SetActionSetActive toggles an existing set by name. Priority resolves conflicts between active sets; avoid leaving conflicting sets active unnecessarily. Adding an OpenXRAction to a second OpenXRActionSet moves it out of the first: independent actions require separate resources.
Editing resource data does not rebuild an attached XR session. OpenXR forbids adding actions and changing suggested bindings after sets are attached to that session. Prepare the map and restart the session. Toggling an existing set’s activity is a separate operation. xrAttachSessionActionSets restrictions.
The current GetActionSets() returns a shared Array. Calling Clear() changes the map without Changed. For ordinary editing, use AddActionSet/RemoveActionSet and the corresponding resource methods; do not clear the returned container as though it were an independent copy.
Add modifiers after checking basic input. xr/openxr/binding_modifiers/analog_threshold and xr/openxr/binding_modifiers/dpad_binding request the corresponding extensions; runtime support is required. Separate analog on/off thresholds to avoid chatter around a boundary. D-pad modifiers require suitable two-dimensional input rather than an arbitrary path. Test each modifier on the target runtime: saving a resource does not establish device-side application.
Diagnose missing input
| Symptom | Check |
|---|---|
| Code always reads false | XR session is running and focused; correct tracker; action set active |
| Only one hand works | Both ToplevelPaths and a separate BindingPath for each hand |
| Saved map has no effect | Correct xr/openxr/default_action_map; session restarted after saving |
| Fails on another controller | Appropriate interaction profile; valid paths for it; runtime accepted bindings |
| Action creation fails | No empty/duplicate names or labels; action type matches the component |
| Vibration does not work | Haptic output action and correct output path; correct action/tracker; duration units |
The exact C# example, structure, two bindings referencing one action, .rdata saving and independent reload were checked in the installed engine without a headset. Action transfer between sets and shared Array behavior were also checked. Physical input, session focus, pose tracking, haptics, modifiers and editor interaction were not tested in this run.