Khundian-ShadowsOfDoubt_UI_Scaler icon

ShadowsOfDoubt UI Scaler

Unifies UI scale via Canvas Scaler for Shadows of Doubt. Config hot-reload, configurable hotkeys (F9 toggle, F11/F10 ±5%), and safe per-canvas application. Keeps fonts/sprites crisp; world-space optional. Requires BepInEx 6 IL2CPP.

Last updated 2 months ago
Total downloads 257
Total rating 1 
Categories Mods
Dependency string Khundian-ShadowsOfDoubt_UI_Scaler-1.0.1
Dependants 0 other packages depend on this package

This mod requires the following mods to function

BepInEx-BepInExPack_IL2CPP-6.0.733 icon
BepInEx-BepInExPack_IL2CPP

BepInEx pack for IL2CPP x64 Unity games. Preconfigured and ready to use.

Preferred version: 6.0.733

README

Shadows of Doubt UI Scaler

Plugin GUID: khundian.sod.uiscaler
Name: Shadows of Doubt UI Scaler
Version: 1.0.1
Description: UI scaler + configurable hotkeys.

Unifies UI scale via Canvas Scaler for Shadows of Doubt. Config hot-reload, configurable hotkeys (F9 toggle, F11/F10 ±5%), and safe per-canvas application. Keeps fonts/sprites crisp; world-space optional. Requires BepInEx 6 IL2CPP.


Features

  • Global UI scaling via Canvas Scaler (Scale With Screen Size) with configurable reference resolution (default 1920×1080), match factor (0–1), and a multiplier (0.10×–5.00×).
  • Configurable hotkeys (defaults: Toggle F9, Scale Up F11, Scale Down F10). Each press adjusts by 5% and saves after a short idle.
  • Hot reload of the config file. Edits on disk are debounced and re-applied safely while the game is running.
  • Scene-change detection by polling (SceneManager.GetActiveScene()), ensuring compatibility with IL2CPP/Roslyn edge-cases (no event subscription required).
  • Batched layout rebuilds: Canvas.ForceUpdateCanvases() is called once per rescan/revert pass to avoid spikes.
  • Efficient discovery of canvases using Object.FindObjectsOfType<Canvas>(true), with filters:
    • ActiveSceneOnly (affect canvases in the active scene)
    • IncludeWorldSpace (optional world-space canvases)
    • Name filters via include/exclude tokens
  • Zero-alloc pruning of destroyed canvases and best‑effort pre‑sizing of internal collections for large scenes.
  • Periodic rescan interval is configurable (Engine:RescanEveryMilliseconds, default 2000 ms). Heavier work runs on a timed tick; only input runs every frame.

Requirements

  • Shadows of Doubt (Unity 2021.3)
  • BepInEx 6 (IL2CPP)

Installation

Manual (zip)

  1. Install BepInEx 6 (IL2CPP) for the game.

  2. Copy the plugin DLL to:

    <GameFolder>\BepInEx\plugins\ShadowsOfDoubt.UIScaler\ShadowsOfDoubt.UIScaler.dll
    
  3. Launch the game once. The config file will be created at:

    <GameFolder>\BepInEx\config\khundian.sod.uiscaler.cfg
    

Thunderstore (optional)

Zip structure:

BepInEx/
  plugins/
    ShadowsOfDoubt.UIScaler/
      ShadowsOfDoubt.UIScaler.dll
manifest.json
README.md
LICENSE

Usage

Defaults

  • Enabled: true
  • Scale: UiScaleMultiplier = 1.00 (vanilla size)
  • Reference Resolution: 1920 × 1080
  • Match: 0.50 (balanced width/height)
  • Rescan: 2000 ms
  • Hotkeys:
    • Toggle: F9 (on/off)
    • ScaleUp: F11 (+5%)
    • ScaleDown: F10 (−5%)

In-game

  • Press F9 to enable/disable (when disabling, original Canvas settings are restored).
  • Press F11 / F10 to adjust the global UI scale by ±5%.
  • A small toast confirms the change (e.g., UI Scale: 1.15x).

Configuration

Location:

<GameFolder>\BepInEx\config\khundian.sod.uiscaler.cfg

The file includes a readable header and per-setting comments. Saving the file hot-reloads and re-applies settings while the game is running—no restart needed.

Key Settings (overview)

[General]

  • Enabled — master switch

[CanvasScaler]

  • UiScaleMultiplier (0.10–5.00) — global UI size multiplier (1.00 = vanilla)
  • ReferenceWidth / ReferenceHeight — reference resolution (default 1920×1080)
  • MatchWidthOrHeight (0..1) — 0 = width, 1 = height, 0.5 = balanced

[Filter]

  • ActiveSceneOnly — consider canvases only from active scene
  • IncludeWorldSpaceCanvases — include World Space canvases (default false)
  • CanvasInclude / CanvasExclude — comma-separated name tokens; exclude wins over include

[Perf]

  • RescanEveryMilliseconds — periodic re-scan to catch newly spawned canvases

[Hotkeys] (names from Unity KeyCode)

  • Toggle (default F9)
  • ScaleUp (default F11)
  • ScaleDown (default F10)

How It Works (Technical)

  • Finds canvases (including inactive) and attaches/updates a CanvasScaler set to Scale With Screen Size.
  • Applies UiScaleMultiplier by adjusting the reference resolution inversely (preserves pixel density).
  • Stores original scaler settings and reverts them when disabling the plugin.
  • Watches the config file; on any save, it reloads and re-applies if still enabled.

Troubleshooting

  • World-space UI breaks
    World-space canvases are excluded by default. Enabling them can affect 3D interaction/depth; use with caution.

Changelog

1.0.1 — 2025-08-23

  • Changed
    • Scene change handling: replaced event subscription attempts with polling in UiDriver.Tick() using SceneManager.GetActiveScene() (compare name/buildIndex) and trigger a single rescan on change.
    • Batched layout rebuilds: Canvas.ForceUpdateCanvases() is now called once per rescan/revert pass (not per-canvas).
    • Discovery path: use Object.FindObjectsOfType<Canvas>(true) and filter by active scene, world-space inclusion, and name tokens.
    • Work cadence: only hotkey reads in Update(); heavier work (rescans, reload debounce, heartbeat) runs on a timed Tick().
    • Logging prefix cleanup: removed manual [{PluginName}] from messages; rely on BepInEx’s [Level: Source] prefix.
  • Added
    • Debounced config reloads: file watcher sets a flag; main thread reloads once after ~200 ms quiet time.
    • Debounced config saves: hotkey changes are persisted once after ~500 ms idle.
    • Name-filter token cache: include/exclude strings are pre-split on (re)load; NameAllowed() uses cached arrays.
    • Stale-entry cleanup: each rescan prunes destroyed canvases from _originalById.
  • Fixed / Compatibility
    • Event interop issues: removed direct subscriptions to SceneManager.activeSceneChanged to avoid IL2CPP/Roslyn edge cases; polling replaces it.
    • Profiler scopes: swapped ProfilerMarker.Auto() for Profiler.BeginSample/EndSample for broad IL2CPP/editor compatibility.
    • Float/double mismatches: consistent float math for Mathf and double timestamps for scheduling.
    • Nullable warnings: initialized static refs and marked optional fields nullable; guarded UI style usage.
  • Optimizations
    • No-alloc pruning: replaced foreach (_originalById.ToArray()) with a reusable buffer to remove dead entries.
    • Best-effort pre-sizing: reflective EnsureCapacity for dictionaries/sets (no-op if unavailable).
    • Quieter logs in steady state: nonessential reload/apply lines moved to Debug (visible only when Verbose=true).

1.0.0

  • Initial release

License

Copyright (c) [2025] [Khundian]. All rights reserved.

You may redistribute this mod in its original, unmodified form.
You may not decompile, reverse engineer, modify, or distribute modified versions of this mod without prior written permission from the author.
This mod may not be used for commercial purposes or included in any paid mod packs or compilations.
The mod is provided "as is," without warranty of any kind. Use at your own risk.