Skip to content

Repository files navigation

ba-click-mac

A native macOS version of the Blue Archive click effect + cursor trail, written in Swift + Metal.

碧蓝档案点击特效 + 鼠标光迹 的原生 macOS 版本,使用 Swift + Metal 实现。

It creates a transparent, borderless, click-through overlay covering the main screen. Global mouse events are observed with AppKit's global event monitor and fed into a CPU particle system; Metal renders particles/trail with the original game textures (Circle_01 / Ring3 / Triangle_02_1 / Trail_03) extracted from ba-click-fx, plus a ported MXFinalBloom glow.

它创建一个覆盖主屏幕的透明、无边框、可点击穿透的覆盖层。全局鼠标事件通过 AppKit 的全局事件监听器收集,交给 CPU 粒子系统模拟;Metal 使用从 ba-click-fx 解包出的原始游戏贴图Circle_01 / Ring3 / Triangle_02_1 / Trail_03)渲染粒子与光迹,并移植了 MXFinalBloom 辉光。


Features / 功能

English 中文
Overlay Transparent click-through overlay on the main screen; never steals focus 主屏幕透明可穿透覆盖层;从不抢占焦点
Click effect Center disk → two arcs (弧光) from random sides spread/converge → disk fades → arcs shrink; flying shards 中心圆盘 → 随机两侧弧光相向扩散/汇聚 → 圆盘消失 → 弧光收缩;飞散碎片
Trail Cursor trail with width taper (tail thins, color stays constant) 鼠标光迹,尾部收细(颜色不变)
Glow Ported original MXFinalBloom (multi-level pyramid, prefilter → downsample → upsample → additive) 移植原版 MXFinalBloom(多级金字塔:预过滤 → 降采样 → 升采样 → 叠加)
Fullscreen NSPanel (fullScreenAuxiliary) is carried into fullscreen apps' Spaces automatically — works over QQ / Chrome fullscreen video NSPanel(fullScreenAuxiliary)自动进入全屏应用的 Space —— QQ / Chrome 全屏视频下均正常
Live tuning Edit settings.json, hot-reloaded every 0.5 s; partial files allowed 编辑 settings.json,每 0.5 秒热重载;允许只写要改的键
Power saving Stops rendering when idle; no GPU work behind hidden fullscreen (showInFullscreen=false) 闲置时停止渲染;隐藏全屏时不产生 GPU 开销(showInFullscreen=false
Management panel Apple-native panel via the menu bar icon: effect on/off, launch at login, trail mode/thickness/glow, click size/brightness/opacity, refresh rate 菜单栏图标打开的 Apple 原生管理面板:效果开关、开机自启、尾迹模式/粗细/辉光、点击大小/亮度/透明度、刷新率
i18n Chinese/English UI, auto-detects the system language (zh* → 中文) 中英双语界面,自动检测系统语言(zh* → 中文)
No Dock icon Runs as an .accessory app (menu bar only), so the Dock stays clean .accessory 模式运行(仅菜单栏),Dock 干净

Status / 状态

  • ✅ Transparent click-through overlay / 透明可穿透覆盖层
  • ✅ Global click + mouse-move tracking / 全局点击 + 鼠标移动追踪
  • ✅ Click effect: center disk, rotating dissolve arcs, flying shards / 点击特效:中心圆盘、旋转溶解弧光、飞散碎片
  • ✅ Cursor trail with width taper / 带收细的鼠标光迹
  • ✅ Original game textures + Unity particle curves / 原始游戏贴图 + Unity 粒子曲线
  • ✅ Multi-pass MXFinalBloom (HDR scene → pyramid → additive glow) / 多级 MXFinalBloom 辉光(HDR 场景 → 金字塔 → 叠加辉光)
  • ✅ Works over fullscreen apps (single persistent NSPanel) / 全屏应用之上正常显示(单一常驻 NSPanel)
  • ✅ Manual 60 fps render loop (display-link stalls fixed) / 手动 60fps 渲染循环(修复 display link 停滞)
  • ✅ Idle power saving (render stops when nothing is on screen) / 闲置省电(无内容时停止渲染)
  • ✅ Unit tests (./test.sh) + CI (GitHub Actions) / 单元测试 + CI
  • ✅ Menu bar icon + management panel (no Dock icon) / 菜单栏图标 + 管理面板(无 Dock 图标)
  • ✅ Launch at login / 开机自启
  • ⏳ Multi-monitor (currently only the main screen) / 多显示器(目前仅主屏幕)

App icon / 应用图标: authored in the modern Icon Composer (macOS 26+ / Xcode 26) as icons/icon.icon (an icon.json manifest + layered Assets/*.svg). The format is full-bleed — macOS applies its own squircle mask (a continuous curve, not a plain rounded corner, and it differs across OS versions) in the Dock/Launchpad, so do not bake rounded corners or margins into the artwork. After editing in Icon Composer, regenerate with ./tools/build-icon.sh (renders via the bundled ictool CLI, produces Resources/icon.png + Resources/AppIcon.icns). Requires /Applications/Icon Composer.app. 应用图标:用新版 Icon Composer(macOS 26+ / Xcode 26)制作,源文件为 icons/icon.iconicon.json 清单 + 分层 Assets/*.svg)。该格式是满幅的——macOS 会在 Dock/Launchpad 自动套上自家的 squircle mask(连续曲线,不是普通圆角,且随系统版本不同),所以不要在素材里自己烘焙圆角或边距。在 Icon Composer 里改完用 ./tools/build-icon.sh 重新生成(内部调用自带的 ictool CLI)。需要装有 /Applications/Icon Composer.app

Menu bar icon / 菜单栏图标: rendered at 22 pt from icons/bar_icon.svg. After editing the SVG, regenerate with ./tools/svg2png.sh icons/bar_icon.svg Resources/bar_icon_22.png 22 and ./tools/svg2png.sh icons/bar_icon.svg Resources/bar_icon_44.png 44. 菜单栏图标以 22pt 渲染,源文件为 icons/bar_icon.svg。改完 SVG 后用上面的命令重新生成 PNG。

Requirements / 环境要求

  • macOS 13+ (built with ./build.sh; macOS 14+ enables the vsync render driver, macOS 26+ the native liquid-glass panel)
  • Xcode Command Line Tools or Xcode (Swift toolchain)
  • A Metal-capable Mac (any Apple Silicon, most Intel Macs)
  • 需要 Metal 支持的 Mac(Apple Silicon 或大部分 Intel Mac)
  • build.sh is the single build entry (binary, or --app for the .app bundle); there is no SPM Package.swifttest.sh builds the unit-test harness directly.

Build, run & test / 构建、运行与测试

./build.sh            # compile → .build/ba-click-mac
./run.sh              # build (if needed) then run
./test.sh             # unit tests: BAEval / ParticleSystem / FXSettings

Or build a double-clickable app bundle / 或构建可双击的 .app 包:

./build-app.sh        # == ./build.sh --app → build/BaClickMac.app
open build/BaClickMac.app

The app runs as an .accessory app (no Dock icon, no menu bar) with a menu bar icon. Clicking the icon shows a menu: 打开管理面板 (toggle open/close) and 退出 BA Click. Quit also via the panel's 退出 BA Click button or pkill BaClickMac. 应用以 .accessory 模式运行(无 Dock 图标、无菜单栏),只有菜单栏图标。点击图标弹出菜单:打开管理面板(开关)与退出 BA Click。也可用面板里的退出 BA Click 按钮或 pkill BaClickMac 退出。

Isolated click testing / 单独测试点击特效

BA_CLICK_LOOP=1 ./run.sh   # auto-clicks screen center every 0.9 s

Environment variables / 环境变量

Variable Effect 说明
BA_CLICK_LOOP=1 Auto-click at screen center every 0.9 s (isolated click-effect testing) 每 0.9 秒在屏幕中心自动点击(单独测试点击特效)
BA_SHOW_HUD=1 Show the debug HUD (bloom/particle counts) in the top-left corner 在左上角显示调试 HUD(辉光/粒子数量)
BA_DISABLE_BLOOM=1 Disable bloom entirely (core effect only) 完全关闭辉光(只画核心特效)
BA_BLOOM_DEBUG_VIEW=1 Show only the bloom pyramid (no core) — for verifying the glow itself 只显示辉光金字塔(无核心)—— 用于验证辉光本身

settings.json (live tuning / 实时调参)

The app loads settings.json from the current working directory, the executable's folder, or ~/.ba-click-mac-settings.json (first found wins) and reloads it every 0.5 s — edit and save, no restart needed. The file is optional: the defaults below are the tuned "best" values, so you can run without any settings file. A full template lives in settings.example.json.

应用会从当前工作目录可执行文件所在目录~/.ba-click-mac-settings.json(按顺序取第一个存在的)加载 settings.json,并每 0.5 秒热重载——改完保存即可,无需重启。该文件是可选的:下表默认值就是调好的"最佳"参数,不提供文件也能直接跑。完整模板见 settings.example.json

Key Default Meaning / 含义
diskScale 0.8 Center disk size multiplier / 中心圆盘尺寸倍率
ringScale 0.8 Arc (弧光) radius multiplier / 弧光半径倍率
shardScale 0.8 Shard size/speed multiplier / 碎片大小与速度倍率
trailScale 2.2 Trail width multiplier / 光迹宽度倍率
showInFullscreen true Keep overlay over fullscreen apps; false = hide + stop rendering
clickBloomStrength 0.1 Click glow-source energy / 点击辉光源能量
trailBloomStrength 3.5 Trail glow-source energy / 光迹辉光源能量
bloomStrength 1.7 MXFinalBloom exposure (2^(strength/10)-1 in composite) / 辉光曝光
bloomLevels 16 Max pyramid levels (actual count follows the diffusion formula) / 金字塔最大层数(实际层数由扩散公式决定)
bloomDiffusion 7.0 MXFinalBloom diffusion — drives iteration count + sample scale / 扩散度——决定迭代次数与采样尺度
bloomThreshold 1.0 Brightness threshold (gamma space) for bloom prefilter / 辉光预过滤亮度阈值(伽马空间)
bloomFalloff 0.35 Rational falloff knee a = lum/(lum+k) / 有理式衰减拐点
bloomBoost 1.2 Extra glow overlay brightness / 辉光叠加额外亮度
enabled true Master effect switch / 效果总开关
trailAlwaysVisible true Trail on any mouse move; false = only while left-dragging / 尾迹始终显示;false = 仅左键拖动时
clickBrightness 1.0 Click effect brightness / 点击效果亮度
clickDiskOpacity 1.0 Click disk opacity (higher = more opaque) / 点击圆盘不透明度(越高越实)
triangleOpacity 1.0 Triangle particle opacity (higher = more opaque) / 三角粒子不透明度(越高越实)
refreshRate 60 Render refresh rate (30/60/120/240) / 渲染刷新率

Launch at login / 开机自启 is not persisted in settings.json — it's a system LaunchAgent state toggled by the panel's 开机自启 switch.

Lenient parsing / 宽容解析: a settings.json may contain only the keys you want to override — missing keys keep the defaults. Unknown keys print a warning to stderr (ignored); invalid JSON prints a warning and falls back to defaults. This is intentional, so a partial edit never silently wipes your other settings.

解析规则settings.json 可以只写你要改的键——缺失的键沿用默认值。未知键会在 stderr 打印告警(忽略);JSON 非法会打印告警并回退默认值。这是刻意设计:部分修改不会悄悄丢掉其它设置。

settings.json is git-ignored (personal tuning stays local); commit changes to settings.example.json instead. settings.json 已被 git 忽略(个人调参留在本地);如需提交参数,请改 settings.example.json

Adding a setting / 新增一个设置项

Every runtime setting touches the same places — keep them in sync:

  1. FXSettings (Sources/BaClickMac/FXSettings.swift): add the property, a defaultXxx constant, wire it in init() and init(from:). CodingKeys is CaseIterable, so the "known keys" warning list stays in sync automatically — no manual list edit.
  2. Panel control (Sources/BaClickMac/SettingsPanel.swift): bind it via store.binding(\\.field) (or clickScaleBinding() for the unified size slider).
  3. Apply it: the renderer reads settings.field (synced by Renderer.applySettings); panel-gating logic (e.g. trail mode) lives in AppDelegate.
  4. L10n (Sources/BaClickMac/L10n.swift): add a "key": (zh, en) entry if the label is user-facing.
  5. Docs: settings.example.json + this README table.
  6. Tests: Tests/main.swift — assert the new default + (if relevant) the persist round-trip.

How it works / 工作原理

  • Single persistent NSPanel — borderless, non-activating, level = .floating, collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary, .stationary, .ignoresCycle], ignoresMouseEvents = true. Because it is a fullScreenAuxiliary panel, macOS carries it into every fullscreen app's Space automatically — no window switching or detection needed. 单一常驻 NSPanel——无边框、非激活、level = .floatingcollectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary, .stationary, .ignoresCycle]ignoresMouseEvents = true。因为是 fullScreenAuxiliary 面板,macOS 会自动把它带进每个全屏应用的 Space——无需切换窗口或检测。
  • Manual vsync-synced render loop — the MTKView's internal display link randomly stalls after Space/fullscreen transitions (the effect appeared "sometimes dead"), so we keep isPaused = true and drive MTKView.draw() ourselves via a CADisplayLink (vsync-synced, macOS 14+; Timer fallback on 13). The trail samples the live mouse position every frame, so it stays smooth even when the OS coalesces mouse-moved events. An App Nap activity (beginActivity(.userInitiated)) keeps the background app's driver alive, and a watchdog rebuilds the driver if it stalls. 手动 vsync 同步渲染循环——MTKView 内部 display link 在 Space/全屏切换后会随机停滞(表现为特效"时有时无"),所以我们保持 isPaused = true,用自建 CADisplayLink(vsync 同步,macOS 14+;macOS 13 用 Timer 回退)驱动 MTKView.draw()。尾迹每帧直接采样鼠标实时位置,即使系统合并了 mouse-moved 事件也保持顺滑。beginActivity(.userInitiated) 防止 App Nap 节流后台应用,看门狗会在驱动停滞时重建它。
  • Idle power saving — the render loop stops itself as soon as nothing is on screen; clicks / mouse moves / the click-loop wake it. With showInFullscreen=false, the overlay hides and rendering fully stops over fullscreen apps. 闲置省电——屏幕上没有内容时渲染循环自动停止;点击 / 移动鼠标 / 自动点击循环会唤醒它。showInFullscreen=false 时,全屏应用之上会隐藏覆盖层并完全停止渲染。
  • EventsNSEvent.addGlobalMonitorForEvents observes clicks/moves system-wide. The app must never become frontmost, or the global monitor stops receiving events (we use orderFrontRegardless(), never activate). 事件——NSEvent.addGlobalMonitorForEvents 全局监听点击/移动。应用绝不能变成前台,否则全局监听会收不到事件(我们只用 orderFrontRegardless(),绝不 activate)。
  • Rendering — offscreen HDR scene (rgba16Float) → MXFinalBloom pyramid (prefilter → downsample → upsample) → additive composite over the sharp core. Bloom is skipped entirely when nothing is on screen. 渲染——离屏 HDR 场景(rgba16Float)→ MXFinalBloom 金字塔(预过滤 → 降采样 → 升采样)→ 在锐利核心之上做叠加。屏幕无内容时完全跳过辉光。
  • Management panel — a SwiftUI panel in a titled, non-activating NSPanel with a native Liquid Glass body (NSGlassEffectView, macOS 26+; resolved via NSClassFromString so the code still builds against older SDKs). On older systems it falls back to a classic NSVisualEffectView (.menu material) glass. titlebarAppearsTransparent + fullSizeContentView keep the native traffic lights and title-bar dragging while the window stays transparent so the glass shows through. It becomes key for controls but never activates the app, so the global mouse monitor keeps feeding the overlay while you tune. Clicking the menu bar icon shows a menu (打开管理面板 / 退出 BA Click). All changes apply to the renderer immediately and persist (debounced) to settings.json. 管理面板——SwiftUI 面板,放在带标题栏、非激活 NSPanel 里,主体为原生液态玻璃NSGlassEffectView,macOS 26+;用 NSClassFromString 运行时查找,旧 SDK 也能编译)。老系统自动回退经典 NSVisualEffectView.menu 材质)玻璃。titlebarAppearsTransparent + fullSizeContentView 保留原生红绿灯与标题栏拖动,同时窗口透明让玻璃透出。控件可用但不激活应用,所以调参时全局鼠标监听仍在工作。点击菜单栏图标弹出菜单(打开管理面板 / 退出 BA Click)。所有改动即时生效并(防抖)持久化到 settings.json
  • Trail mode / 尾迹模式 — "始终显示尾迹" on: trail follows any mouse move. Off: trail only appears while the left button is held and dragging. 尾迹模式——开启"始终显示尾迹":尾迹跟随任意鼠标移动;关闭:仅在按下左键并拖动时显示尾迹。
  • Launch at login / 开机自启 — writes a user LaunchAgent plist (~/Library/LaunchAgents/local.ba-click-mac.plist) pointing at the current executable; toggled from the panel. 开机自启——写入用户 LaunchAgent plist(~/Library/LaunchAgents/local.ba-click-mac.plist)指向当前可执行文件;由面板开关控制。

Project layout / 工程结构

Sources/BaClickMac/
  main.swift                 App entry, NSApplication + delegate
  AppDelegate.swift          NSPanel setup, 60 fps render loop, mouse monitor,
                             fullscreen handling, watchdog, HUD
  TransparentMTKView.swift   Non-opaque MTKView
  MouseMonitor.swift         Global mouse event observation
  ParticleSystem.swift       Click particles + trail simulation
  Renderer.swift             Metal pipelines, geometry building, bloom pyramid
  Shaders.swift              Metal Shader Language source (runtime compiled)
  FXSettings.swift           settings.json loading / defaults (lenient decode)
  BAEffectData.swift         Unity keyframes / game-derived values
  DebugLog.swift             stderr logging + bail() helper
  ResourceLocator.swift      Shared bundled-resource lookup
  SettingsStore.swift        ObservableObject settings store + launch-at-login
  SettingsPanel.swift        SwiftUI management panel + non-activating NSPanel
  L10n.swift                 Chinese/English strings, system-language detection
Resources/
  AppIcon.icns               macOS app icon (used by the .app bundle)
  icon.png                   App icon bitmap (Dock icon for the raw binary)
  bar_icon_22/44.png         Menu bar icon (1x / 2x template)
  circle/ring/trail/triangle  Game-derived effect textures
icons/
  icon.icon / bar_icon.svg    Icon sources (Icon Composer package / menu bar SVG)
tools/
  svg2png.sh / svg2png.swift SVG → PNG converter (menu bar icon regeneration)
  build-icon.sh              Regenerate icon.png + AppIcon.icns from icons/icon.icon
  regen-app-icon.sh          Legacy: same from icons/icon.svg (kept for reference)
Tests/
  main.swift                 Unit tests: BAEval / ParticleSystem / FXSettings
.github/workflows/build.yml  CI: build + unit tests on macOS
build.sh                     Build binary, --app for the .app bundle, --release for DMGs
build-app.sh                 Wrapper for ./build.sh --app
test.sh                      Build & run the unit tests
settings.example.json        Template for optional runtime tuning

Permissions / 权限

Global mouse observation via NSEvent.addGlobalMonitorForEvents is generally allowed on macOS — no special permission needed. (A future CGEventTap would require Accessibility.) 通过 NSEvent.addGlobalMonitorForEvents 的全局鼠标监听在 macOS 上一般无需额外权限。(未来若改用 CGEventTap 则需要"辅助功能"权限。)

Troubleshooting / 排障

  • Effect appears randomly / 特效随机消失或时有时无: this used to be the MTKView display link stalling after Space/fullscreen transitions — now fixed by the manual 60 fps render loop. If it ever looks dead again, check the watchdog (a stale frame forces a redraw every 0.5 s). / 这曾是 MTKView display link 在 Space/全屏切换后停滞所致——现已通过手动 60fps 渲染循环修复。若再次看起来"死了",看门狗每 0.5 秒会强制补一帧。
  • App exits immediately with a FATAL: message / 启动即退出并打印 FATAL:: Metal device / shader compile / texture load failed — run from a terminal to see the exact reason (resources must exist in Resources/ with the expected sizes). / Metal 设备 / Shader 编译 / 纹理加载失败——请从终端运行查看具体原因(Resources/ 下资源必须存在且尺寸匹配)。
  • No click response at all / 点击完全无反应: make sure the app is not frontmost (never activate it); the global mouse monitor only receives events while another app is active. / 请确认应用不是前台(绝不 activate);全局鼠标监听只在其他应用为前台时才能收到事件。
  • HUD shows nothing / HUD 不显示: it is off by default — run with BA_SHOW_HUD=1. / 默认关闭——用 BA_SHOW_HUD=1 运行。
  • Settings not applied / 设置没生效: the app reloads every 0.5 s; watch stderr for [settings] WARNING: (invalid JSON → defaults; unknown key → ignored). / 应用每 0.5 秒重载;留意 stderr 的 [settings] WARNING:(JSON 非法 → 回退默认值;未知键 → 忽略)。

Notes / 备注

  • The overlay never steals focus; clicks pass through to the apps below. / 覆盖层从不抢占焦点;点击穿透到下层应用。
  • Coordinates are in AppKit screen points, scaled by screen height (mirroring the web project's 1080p reference). / 坐标基于 AppKit 屏幕点,按屏幕高度缩放(对齐网页版的 1080p 参考高度)。
  • A from-scratch native implementation; visual parameters are ported from the ba-click-fx web project's unpacked Unity data. / 原生从零实现;视觉参数移植自 ba-click-fx 网页项目解包出的 Unity 数据。

About

原生 Swift + Metal 的《蔚蓝档案》点击特效与光标尾迹 —— 全屏/桌面覆盖、菜单栏控制、Liquid Glass 管理面板。Native macOS Blue Archive click effect & cursor trail: fullscreen overlay, menu bar controls, glass settings panel.

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages