Skip to content

Commit 41ca7be

Browse files
fix(android): 简化 Android 虚拟按键设置
1 parent 9c2f0e2 commit 41ca7be

6 files changed

Lines changed: 34 additions & 31 deletions

File tree

‎android/runtime/MOD_TOUCH.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ best-effort, with the existing **按键 / 触屏** fallback.
4444
still be configured on the launcher's custom **按键** page. This change does not
4545
implement a touch version of the gamepad emote wheel or player-list row actions.
4646

47-
Default gameplay shortcuts require gameplay buttons enabled and a usable keyboard
48-
binding; both touch preferences off remain off. Direct chat/map menus also work
49-
with joystick-only controls. Custom semantic buttons can target unbound Mod actions.
47+
Default gameplay shortcuts require virtual keys enabled and a usable keyboard
48+
binding. Direct chat/map menus also work with the virtual-key switch enabled.
49+
Custom semantic buttons can target unbound Mod actions.
5050

5151
## Verification
5252

‎android/runtime/TOUCH_CONTROLS.md‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ one-tap resume.
3939
startup. If the native text keyboard is open, Back dismisses it first. During
4040
startup it retains the cancel-launch confirmation.
4141
- The top-left edit button and adjacent exit icon are fixed. Exit **always asks for confirmation** and is
42-
available even when both touch options are off. It does not save the game for you.
43-
- Enabling only the stick still provides complete menu navigation and confirmation.
44-
Disabling both options leaves only edit/exit; other touches pass through.
42+
available even when virtual keys are off. It does not save the game for you.
43+
- The **显示虚拟按键** switch controls the complete touch overlay, including gameplay actions and
44+
direction controls. When it is off, only edit/exit remain and other touches pass through.
4545
- Bindings are read from `Celeste.Input` and `Settings`, including menu directions,
4646
gameplay movement, interaction and Pause. Supported keyboard bindings use their
4747
first Android-representable key. Only missing metadata (e.g. an unavailable hook)
@@ -126,7 +126,7 @@ one-tap resume.
126126
- In the gameplay layout editor, tap the stick or any direction button to choose
127127
**固定摇杆 / 浮动摇杆 / 四键按钮 / 八键按钮**. The eight-button layout adds four explicit diagonal
128128
buttons around an empty center. Menus retain cardinal-only navigation. Existing
129-
launcher enable/disable switches are respected; an unset mode follows the old
129+
launcher virtual-key switch is respected; an unset mode follows the old
130130
joystick preference. Switching modes preserves the hidden controls' positions
131131
and styles so switching back is non-destructive.
132132
- **浮动摇杆** starts anywhere in the left half of the overlay, below the
@@ -307,7 +307,7 @@ verify play icon, D-pad and bottom-right confirm; open options and verify back i
307307
resume via both play icon and Android Back; hold climb while entering Talk range;
308308
hold movement while opening pause and verify no stuck selection; background/return;
309309
test journal, naming, dialogue, two-finger gameplay, fast taps, controller-opened
310-
menus, and both touch preference combinations. Exit only through the confirmed
310+
menus, and with virtual keys both enabled and disabled. Exit only through the confirmed
311311
manager button or normal game exit. Use test saves for any progress-changing checks.
312312
For rebinding: move jump/dash/grab/talk/pause and menu confirm/cancel/directions to
313313
distinct keys; verify both direct touch and virtual navigation. Clear a common
@@ -346,7 +346,7 @@ invalidation, and propagation of original game exceptions.
346346

347347
- Manager settings → Android 游戏运行时 → **跟随游戏震动** (`gameRumble`, default
348348
false, applied on the next game launch). Existing settings files remain off;
349-
partial setting updates preserve all other switches.
349+
partial setting updates preserve the virtual-key switch.
350350
- A managed detour observes `FNA GamePad.SetVibration(PlayerIndex, float, float)`
351351
after the original call, including when it returns false because no physical
352352
controller is attached. Celeste's own rumble off/half/full setting, timing,
@@ -374,6 +374,6 @@ invalidation, and propagation of original game exceptions.
374374
last 20 ms, and the motor must receive distinct amplitudes rather than different
375375
durations. Actual perceptual separation must be checked on the device.
376376
Device smoke checks: enable
377-
with both touch controls off; trigger dash/landing rumble without a gamepad; check
377+
with virtual keys off; trigger dash/landing rumble without a gamepad; check
378378
half/off in the game's settings; combine touch pulses; background and return;
379379
disable and relaunch. Physical controller rumble should remain unchanged.

‎android/runtime/src/com/celemod/runtime/RuntimePlugin.kt‎

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,12 @@ class RuntimePlugin(private val activity: Activity) : Plugin(activity) {
7575

7676
@Command fun info(invoke: Invoke) {
7777
val result = JSObject()
78+
// The gameplay buttons and direction controls are one virtual-keyboard
79+
// switch. Keep accepting the old settings file format and treat either
80+
// legacy flag as enabling the overlay.
81+
val touchEnabled = settings().optBoolean("buttons", false) || settings().optBoolean("joystick", false)
7882
result.put("gameRoot", root().absolutePath)
79-
result.put("buttons", settings().optBoolean("buttons", false))
80-
result.put("joystick", settings().optBoolean("joystick", false))
83+
result.put("joystick", touchEnabled)
8184
result.put("gameRumble", settings().optBoolean("gameRumble", false))
8285
result.put("runtime", "RotatingArtLauncher 2.1.1 / CoreCLR 10.0.4")
8386
invoke.resolve(result)
@@ -87,9 +90,13 @@ class RuntimePlugin(private val activity: Activity) : Plugin(activity) {
8790
try {
8891
val args = invoke.getArgs()
8992
val value = settings()
90-
for (key in arrayOf("buttons", "joystick", "gameRumble")) {
91-
if (args.has(key) && !args.isNull(key)) value.put(key, args.getBoolean(key))
93+
if (args.has("joystick") && !args.isNull("joystick")) {
94+
val enabled = args.getBoolean("joystick")
95+
value.put("joystick", enabled)
96+
// Keep the legacy field in sync for older game processes.
97+
value.put("buttons", enabled)
9298
}
99+
if (args.has("gameRumble") && !args.isNull("gameRumble")) value.put("gameRumble", args.getBoolean("gameRumble"))
93100
val temp = File(activity.filesDir, "android-controls.json.tmp")
94101
temp.writeText(value.toString())
95102
check(temp.renameTo(settingsFile())) { "Cannot save control settings" }
@@ -174,12 +181,13 @@ class RuntimePlugin(private val activity: Activity) : Plugin(activity) {
174181
SteamBridge.beforeLaunch(activity.applicationContext, path)
175182
SteamBridge.launchStage = "preparing"
176183
RuntimeHost.prepare(activity)
184+
val touchEnabled = settings().optBoolean("buttons", false) || settings().optBoolean("joystick", false)
177185
val intent = Intent(activity, GameActivity::class.java)
178186
.putExtra("path", path.absolutePath)
179187
.putExtra("origin", args.optBoolean("origin"))
180188
.putExtra("legacyLoader", args.optBoolean("legacyLoader"))
181-
.putExtra("buttons", settings().optBoolean("buttons"))
182-
.putExtra("joystick", settings().optBoolean("joystick"))
189+
.putExtra("buttons", touchEnabled)
190+
.putExtra("joystick", touchEnabled)
183191
.putExtra("gameRumble", settings().optBoolean("gameRumble", false))
184192
SteamBridge.launchStage = "starting"
185193
activity.runOnUiThread {

‎src-tauri/src/android.rs‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,10 @@ pub fn steam(request: serde_json::Value) -> anyhow::Result<serde_json::Value> {
6060
.run_mobile_plugin("steam", request)?)
6161
}
6262

63-
pub fn settings(buttons: Option<bool>, joystick: Option<bool>, game_rumble: Option<bool>) -> anyhow::Result<serde_json::Value> {
63+
pub fn settings(joystick: Option<bool>, game_rumble: Option<bool>) -> anyhow::Result<serde_json::Value> {
6464
let handle = RUNTIME.get().ok_or_else(|| anyhow::anyhow!("Android runtime is not initialized"))?;
65-
Ok(if buttons.is_some() || joystick.is_some() || game_rumble.is_some() {
66-
handle.run_mobile_plugin("configure", serde_json::json!({"buttons": buttons, "joystick": joystick, "gameRumble": game_rumble}))?
65+
Ok(if joystick.is_some() || game_rumble.is_some() {
66+
handle.run_mobile_plugin("configure", serde_json::json!({"joystick": joystick, "gameRumble": game_rumble}))?
6767
} else { handle.run_mobile_plugin("info", ())? })
6868
}
6969

‎src-tauri/src/backend.rs‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4332,12 +4332,12 @@ async fn android_steam(request: serde_json::Value) -> Result<serde_json::Value,
43324332
}
43334333

43344334
#[tauri::command]
4335-
async fn android_runtime_settings(buttons: Option<bool>, joystick: Option<bool>, game_rumble: Option<bool>) -> Result<serde_json::Value, String> {
4335+
async fn android_runtime_settings(joystick: Option<bool>, game_rumble: Option<bool>) -> Result<serde_json::Value, String> {
43364336
#[cfg(target_os = "android")]
4337-
return tauri::async_runtime::spawn_blocking(move || crate::android::settings(buttons, joystick, game_rumble)).await
4337+
return tauri::async_runtime::spawn_blocking(move || crate::android::settings(joystick, game_rumble)).await
43384338
.map_err(|e| e.to_string())?.map_err(|e| format!("{e:#}"));
43394339
#[cfg(not(target_os = "android"))]
4340-
{ let _ = (buttons, joystick, game_rumble); Err("Android only".into()) }
4340+
{ let _ = (joystick, game_rumble); Err("Android only".into()) }
43414341
}
43424342

43434343
#[tauri::command]

‎src/celemod-ui/src/components/AndroidRuntime.tsx‎

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { Icon } from "./Icon";
77
import { SteamAccount } from "./SteamAccount";
88
import { AndroidLogs } from "./AndroidLogs";
99

10-
type RuntimeSettings = { gameRoot: string; runtime: string; buttons: boolean; joystick: boolean; gameRumble: boolean };
10+
type RuntimeSettings = { gameRoot: string; runtime: string; joystick: boolean; gameRumble: boolean };
1111

1212
export function AndroidRuntime() {
1313
const [gamePath] = useGamePath();
@@ -19,7 +19,7 @@ export function AndroidRuntime() {
1919
useEffect(() => {
2020
invoke<RuntimeSettings>("android_runtime_settings").then(setSettings).catch(e => setError(String(e)));
2121
}, []);
22-
async function update(key: "buttons" | "joystick" | "gameRumble", value: boolean) {
22+
async function update(key: "joystick" | "gameRumble", value: boolean) {
2323
if (!settings) return;
2424
setSaving(true); setError("");
2525
try {
@@ -54,17 +54,12 @@ export function AndroidRuntime() {
5454
<button disabled={!gamePath || saving} onClick={() => void importPackage()}>导入 Mod / Everest ZIP</button>
5555
</div>
5656
<label className="setting-toggle-row">
57-
<span><strong>智能屏幕按键</strong><small>游玩时显示跳跃 / 冲刺 / 抓取,菜单自动切换确定 / 返回;跟随游戏键位,默认关闭。</small></span>
58-
<input type="checkbox" checked={settings?.buttons ?? false} disabled={!settings || saving}
59-
onChange={e => void update("buttons", e.target.checked)} />
60-
</label>
61-
<label className="setting-toggle-row">
62-
<span><strong>屏幕方向控制</strong><small>默认使用固定八向摇杆;游戏内「编辑」可切换固定 / 浮动摇杆、四键 / 八键按钮,选择八分圆环显示。四键 / 八键可选择独立按钮或合并成方向盘整体排版,并调整死区、震动和大小(最高 300%)。菜单使用四向方向键,默认关闭。</small></span>
57+
<span><strong>显示虚拟按键</strong></span>
6358
<input type="checkbox" checked={settings?.joystick ?? false} disabled={!settings || saving}
6459
onChange={e => void update("joystick", e.target.checked)} />
6560
</label>
6661
<div className="theme-setting"><div className="setting-description">
67-
<small>设置在下次启动游戏时生效。屏幕按键和方向控制均关闭时可使用实体手柄或键盘。游戏内安卓返回键 / 返回手势等同 Esc;左上角退出图标可返回管理器,启动过程中返回键可取消启动。</small>
62+
<small>设置在下次启动游戏时生效。关闭虚拟按键时可使用实体手柄或键盘。游戏内安卓返回键 / 返回手势等同 Esc;左上角退出图标可返回管理器,启动过程中返回键可取消启动。</small>
6863
<small>游戏内点左上角「编辑」,再轻点摇杆 / 方向键切换模式,轻点各按钮设置进入 / 离开震动、独立震动强度和不透明度(Opacity);保存后生效,横竖屏分别记忆。</small>
6964
{error && <p role="alert">{error}</p>}
7065
</div></div>

0 commit comments

Comments
 (0)