Touchscreen input and object clicks
Use Input to read real touchscreen contacts and handle mouse clicks or touch taps
on scene primitives in the editor's embedded Play game view. Lua 5.4, Luau,
JavaScript, Python, C, C++, C#, Java, PHP and HTML inline JavaScript use the same
engine-owned input state. New Window and Standalone do not forward these inputs.
Set up a clickable object
- Create a built-in primitive such as a cube. Select it and enable CanTouch in the Inspector. CanCollide can be disabled; clicking does not require physics. Enable Anchored if the object should stay in place.
- Add an active Camera that faces the object. Cameras look along local +Z;
a camera at
[0, 0, 0]can see a cube at[0, 0, 5]. With several active cameras, the highest priority/order wins, matching the rendered game image. - Create a Lua Object Component script in Explorer and attach it to the cube using + Add Component, or drag it onto the selected object in the viewport.
- Paste this complete script, press Play, then click the cube or tap it on a
touchscreen. The Console should print
clickedonce per click or tap.
return {
Start = function()
Input.onClick(rustic.entity_id(), function(id, position, device, pressure)
print('clicked', id, device, position[1], position[2])
end)
Input.onTouchStart(function(id, position, device, pressure)
print('finger down', id, pressure)
end)
Input.onTouchCancel(function(id)
print('finger cancelled', id)
end)
end,
Update = function(dt)
for _, touch in ipairs(Input.touches()) do
-- Normalized coordinates in the displayed game image, top-left is (0, 0).
-- Each contact has its own stable string ID.
local x, y = touch.position[1], touch.position[2]
end
end
}The equivalent JavaScript click subscription belongs inside Start:
globalThis.behavior = {
Start() {
Input.onClick(rustic.entity_id(), (id, position, device, pressure) => {
print('clicked', id, device);
});
},
Update(dt) {
for (const touch of Input.touches()) {
// touch.position is [x, y], touch.id is a string.
}
}
};Calls and returned data
Input.touches()returns currently active touchscreen contacts, including separate contacts for multiple fingers. Mouse input is excluded.Input.touchEvents()returns ordered touchscreen events for the current frame. Read this inUpdate; reads do not consume events, so every script sees them.Input.clicks(object?)returns clicks for the current frame, optionally filtered by entity ID or object path. Each click has{entity, pointer, point}.pointis the world-space hit point andpointeris the release event.Input.onClick(object, callback)subscribes to a specific object'sClickevent.Input.onTouchStart(callback),onTouchMove,onTouchEnd, andonTouchCancelsubscribe to touchscreen events, including fingers that hit no object.
Each touch record contains {id, device, phase, position, pressure, aspect}.
id is a stable string containing the platform device and finger IDs; never parse
it as a JavaScript number. device is touch (or mouse in click records).
phase is start, move, end, or cancel. position is [x, y] relative to
the displayed image, not the entire editor or the letterbox bars. Coordinates can
leave [0, 1] while a captured finger moves outside the image. pressure is
normalized to [0, 1], with 0 when touch pressure is unavailable. aspect is the
displayed image's width divided by height.
Callbacks receive (id, position, device, pressure). The callback's position is
[x, y, 0], using the existing cross-language event vector representation. All
subscriptions return the normal Events connection; call connection.disconnect()
(or the corresponding native method) to remove one. Owner disable, destruction,
Stop and successful script reload clean up subscriptions. Failed reload preserves
the previous script's subscriptions. The event names Click, TouchStart,
TouchMove, TouchEnd, and TouchCancel are engine-reserved and cannot be emitted
from scripts using Events.emit.
Click rules and limits
- Only primary mouse presses and actual touchscreen contacts generate clicks. A touchscreen's synthesized mouse event is suppressed to avoid duplicate clicks.
- Both press and release must pick the same primitive with CanTouch enabled. The closest primitive blocks those behind it, including when its CanTouch is disabled. CanTouch is checked again before delivering queued click callbacks.
- Picking uses primitive world AABBs, including parent transforms, and the active camera's perspective/orthographic projection and zoom. It does not perform triangle-level mesh picking. Imported model meshes need an authored primitive target; meshes alone are not clickable. The near and far camera planes bound picks.
- Input begins only inside the game image. Release outside it, cancellation, focus loss, hiding the game view, pause, Stop and changing scenes clear captures without clicking. Scene changes discard pending input from the previous scene.
- Touch input requires a touchscreen and OS/backend support. A mouse does not emulate a finger. No pinch, swipe or long-press gesture recognizer is included.
- Input is bounded to 64 active pointers and 1024 queued events. Queue overflow cancels active contacts rather than leaving them held.
- Collision contact callbacks remain unavailable.
CanTouchcontrols picking; it does not enable solid collision response orOnCollisionEntercallbacks.
If nothing happens, verify Play is running, the game view is visible, the camera can see the primitive, CanTouch is enabled, and the script is attached and enabled. Check the Console for script/toolchain errors. Clicking a letterbox bar or another editor panel does not target a game object.
Other language bindings
Python imports Input from rustic. C# uses Rustic.Input (or using static Rustic;), Java behaviors inherit Rustic.Input, C++ uses Input::touches() and
Input::onClick(...), and PHP uses Input::touches() and Input::onClick(...).
Lua/Luau and JavaScript/HTML receive Input as a global.
C uses Input.touches() / Input.touchEvents() returning RusticPointerEvents
(count, items) and Input.clicks(entity_or_NULL) returning
RusticObjectClicks. Each click contains entity, pointer, and point;
positions in a C pointer record are position[0] and position[1]. C subscription
callbacks use void callback(const RusticValue *arguments, size_t count) with
four arguments in the order above. Returned C reads live until the next callback.
C++ returns ordinary RusticValue arrays, C# returns JsonElement arrays, and
Java returns lists of maps. Other languages receive their ordinary arrays/lists
and objects/maps. Use the same signatures and the regular Events connection
conventions for your language. Regenerate the script workspace after upgrading
the engine so external scripts compile against the updated SDK.