Repository navigation
Closed
Description
Summary
The platformer example already ships working on-screen touch controls: a joypad and a jump button in packages/examples/src/examples/platformer/entities/controls.ts, shown on touch devices from play.ts. Every mobile game needs something like it, but today the only way to get it is to copy that file and adapt it by hand.
This issue proposes turning it into a reusable plugin, @melonjs/gamepad-plugin, alongside debug-plugin and spine-plugin, and filling in what the example leaves out.
What the example already does (the starting point)
Joypad: aUISpriteElementwith a moving thumb, tracking the pointer and releasing when it leaves the padButton: aUISpriteElementthat presses and releases on clickVirtualJoypad: a floating, persistentContainerholding both, repositioned onVIEWPORT_ONRESIZE- input is fed to the game through
input.triggerKeyEvent, so existing keyboard bindings just work
What's missing
Controls
- Full stick: up/down as well as left/right (currently a
TODOincontrols.ts), with 8-way digital output and an analog value (x/yin -1..1) - Deadzone on the stick, matching
input.setGamepadDeadzone - Configurable buttons: count, position, size and which key or action each one triggers (today one button, hard-wired to
SPACE) - Optional second stick for twin-stick games
- Floating stick option: the stick appears where the thumb lands instead of at a fixed spot
- Buttons that follow the game: a button can be relabelled, dimmed or hidden at runtime, so a game can light its use button only when there is something in reach
Integration
- Report through the existing gamepad API, so code that reads a real pad (
bindGamepad/GAMEPADconstants) works unchanged, with key events kept as an option - Multi-touch: hold the stick and press a button at the same time, each touch tracked by its pointer id
- Pointers that start on the controls are consumed and don't reach the game world underneath
- A key bound with
lock(input.bindKey(key, action, true)) fires once per tap, as it does on a keyboard, however long the button is held - Drawn over the whole game: above every other floating renderable whatever its
z(a game can keep a full-screen overlay at a very highzand its HUD above that), and only by the main camera, never inside a second camera such as a minimap - Across stages: the controls survive a stage change and can be shown or hidden at runtime, so a stage that only needs a tap, like an end screen, can hide them
- A game can tell when the controls are showing (
enabled, readable at runtime), so its prompts can say "tap" instead of naming a key - Hide when a real gamepad is connected (option, on by default): the virtual controls hide on
GAMEPAD_CONNECTEDand come back onGAMEPAD_DISCONNECTED, or when the screen is touched again. Browsers only report a pad once one of its buttons is pressed, so in practice they hide as soon as the player picks up the pad. The readableenabledstate follows, so prompts stay right
Look and setup
- Draws itself with renderer primitives by default (circles and rounded rects, tinted and semi-transparent), so it needs no texture atlas; sprite regions remain an option for custom skins
- One-line setup, e.g.
plugin.register(GamepadPlugin, { buttons: 2 }), with anenabledflag that defaults todevice.touch - Layout from the viewport, with safe margins, kept on resize and orientation change
Also worth considering
- Pinch to zoom reported the same way as the mouse wheel, so one handler covers desktop and touch
Done when
- the plugin is published as its own package, with README, CHANGELOG and tests
- the platformer example uses the plugin and
entities/controls.tsis removed - the isometric dungeon example gets its touch controls from it: an 8-way stick and its use button, alongside its minimap camera and its darkness overlay
melonjs-inputskill documents it, with a symptom table entry for "touch controls do nothing"- works on the Canvas and GPU backends
Metadata
Metadata
Assignees
Labels
No labels
Activity
gamepad-plugin: on-screen touch controls, reported as a gamepad (#1719)
Gamepad plugin: on-screen touch controls, reported as a gamepad (#1799)
obiot commentedon Oct 30, 2026
Done in #1799 (the
@melonjs/gamepad-pluginpackage and the engine's virtual gamepad slot) and #1800 (the platformer moved onto the plugin, plus fixes from trying it on a phone: no selection on a long press, the stick answering at half way, a light vibration on button presses).Custom skins from a texture atlas moved to #1802.
Generated by Claude Code