Lethal Company
Install with App

Details

Latest version
1.0.4
Last Updated
First Uploaded
Downloads
264
Likes
0
Size
28MB
Dependants

LyricDisplay

English

LyricDisplay is a BepInEx plugin for Lethal Company that renders the lyrics of the music currently playing on a boombox as a HUD element right above the inventory bar. It works with the vanilla boombox and with custom tracks added by CustomBoomboxMusic (CBM), and it shows the lyrics of several boomboxes at once when they are in range.

The lyrics come from .lrc files that sit next to the music. Nothing is fetched from the network and no audio is analysed: the plugin looks up the lyric file whose base file name matches the track and walks it with the boombox playback clock.

At a glance

Item Value
Version 1.0.4
Game Lethal Company
Loader BepInEx 5.4.2305 (BepInExPack)
Required mods CustomBoomboxMusic 2.4.2 or later, Lyriclib 1.0.0
Config file BepInEx/config/LyricDisplay.cfg (written on first launch)
Lyric folders BepInEx/plugins/LyricDisplay/Lyrics (a folder named Lyric is accepted as well), plus every folder named CustomBoomboxMusic, Lyrics or Lyric under BepInEx/plugins
HUD toggle key End (ToggleDisplayKey)
Default renderer the game's own HUD text (UseNativeHudLyrics = true)

What's new in 1.0.4

  • The LethalConfig Reload button now actually appears. Its reflective registration only accepted a button item whose callback parameter was a System.Action; LethalConfig 1.4.x passes a delegate of its own (GenericButtonOptions.GenericButtonHandler, void()) instead, so no constructor matched, the bridge logged a warning and skipped the button - the entry never showed up on this mod's page. The registration now accepts that delegate shape, and adds the button through the AddConfigItem(BaseConfigItem, Assembly) overload with this plugin's assembly, so the entry lands on this mod's page rather than depending on Assembly.GetCallingAssembly() behind a reflection invoke.

  • The button is diagnosable. If a later LethalConfig version changes the shape again, the warning in BepInEx/LogOutput.log now prints the constructor signatures the bridge found instead of only saying that nothing usable was there.

  • A Reload button on the LethalConfig page. With LethalConfig installed, this mod's page carries a button that re-reads the lyric and the music folders inside the running game: songs and .lrc files copied in while the game is up are indexed on the spot, with no restart. It goes through the same code path the mod already uses to catch up with a folder whose contents changed, so the result is identical to what the next boombox playback would have produced.

  • Optional music-folder preload (PreloadMusicFolders, off by default). Switched on, the lyric and music folders are read while the game is still on its way to the main menu - before the online / LAN mode is chosen - instead of waiting for the first boombox to play. It exists for troubleshooting a folder or index problem; every session with the switch on logs the warning again, because the folders are read earlier than the game itself would and the cost (loading time and memory) is paid even by a session that never plays a boombox: Do not use this feature unless you know what you are doing. 除非你知道你在做什么,否则不要使用该功能。

  • The version numbers are in sync again. Plugin.PLUGIN_VERSION, the csproj <Version> and manifest.json all report 1.0.4; the project file used to still say 1.0.3.

Earlier release highlights (1.0.3):

  • The game's own HUD draws the lyrics now. UseNativeHudLyrics (on by default) is the new renderer and the default one: the lyrics are mounted inside the HUD the game builds - next to the weight counter - and drawn with the HUD's own font, layer, camera and scaling, so they look like the rest of the HUD. Chinese lyrics come out with the Chinese glyphs the game itself uses, so an installed Chinese localisation or font pack is picked up automatically, with nothing to configure.
  • The mod's own overlay became an option. UseUiLyrics (off) brings back this mod's own screen space overlay canvas for anyone who wants the lyrics outside the game's HUD hierarchy. With both switches off nothing is drawn; the IMGUI overlay stays the last resort.
  • The renderer configuration options have been consolidated into the two aforementioned toggles. UseNativeHudLyrics both draws using the game's native HUD text and, through the same code path, borrows glyphs already available at runtime in the game.
  • A folder named Lyric (singular) is indexed as well. Lyric files kept in Lyric instead of Lyrics are found again, both next to the plugin and anywhere under BepInEx/plugins, so a collection created with an earlier package layout keeps working after the update.
  • Steady frames got cheaper. While nothing is on screen the boombox scan drops to every sixth frame, the composed panel and the karaoke memo are cached per renderer / per track, a line whose glyphs did not change reuses the size it was measured at instead of being measured again for every gradient step, and the HUD labels stop marking the canvas dirty when nothing moved - an unchanged frame is recognised by a single reference check.

Features

Tracking

  • Live lyric HUD: the current line (and optionally the next one, or the translation) is drawn above the inventory bar, and only in gameplay scenes - never on the main menu or the loading screens.

  • Vanilla + CustomBoomboxMusic support: track names are resolved through CBM's public AudioManager catalog when the mod is installed, with an automatic fallback to the audio clip name, so the vanilla boombox works too.

  • Distance filtering and several boomboxes at once: nearby boomboxes are sorted by distance and the closest ones (up to MaxSimultaneousSources, playing sources first) are stacked, each with its own header line. Boomboxes farther away than MaxAudioDistance are ignored.

  • Spectating support: lyrics stay visible while you spectate after death, controlled by ShowInSpectate. The spectate state is detected reflectively (death flag plus the active spectate UI), so the plugin does not hard-depend on the game assembly.

Lyric content

  • Bilingual lyrics: when an LRC file carries a translation line that shares the same timestamp as the original line, the HUD shows the original line with its translation below it. Identical duplicate lines are treated as "no translation".

  • Second line resolution: when there is no translation, the upcoming lyric line is shown below the current one (the ShowNextLine behaviour).

  • Karaoke word-level gradient: enhanced LRC exports (QQ Music / NetEase <start,duration>word tags) render a per-word red/white karaoke gradient; the word currently being sung blends smoothly from white toward red. CJK / Hangul / Kana character-per-tag lines stay gapless, while Latin word boundaries are preserved exactly as in the plain text.

  • Timeline-driven highlight: the karaoke highlight edge is derived purely from the lyric word timings, and the karaoke rich text is rebuilt once per Update from the current timeline position, so the gradient always matches the live playback without audio processing or any energy modulation.

  • Track header line: Title - Artist - Album above the lyrics, read from ID3v2 tags (TIT2 / TPE1 / TALB) or from the FLAC VORBIS_COMMENT block (TITLE / ARTIST / ALBUM), with an Artist - Title (Album) file-name fallback; missing fields are shown as unknown.

Rendering

  • Native HUD text by default: UseNativeHudLyrics (on) draws the lyrics with the game's own HUD - the text is mounted inside the HUD the game builds, next to the weight counter, with the HUD's own font, layer and camera - and with the Chinese glyphs the session already has loaded, so installed Chinese localisation / font packs render the lyrics too. The bundled font is only used while the switch is off.

  • Optional UI overlay: UseUiLyrics (off by default) draws the lyrics on this mod's own screen space overlay canvas instead, and the IMGUI overlay stays the last resort when the enabled renderers are unavailable. With both switched off nothing is drawn.

  • Text alignment and offsets: Left / Center / Right alignment inside the panel plus vertical and horizontal screen offsets, so the panel can be moved away from the vanilla HUD layout.

  • Low-battery pitch fix (opt-in): FixLowBatteryPitchAfterRecharge restores the vanilla boombox pitch to normal once the battery recharges above 5%, so the music is not stuck slowed until the track is restarted.

Performance

  • Rendering work is skipped when nothing changes: OnGUI is filtered to Repaint events only, per-frame layout and render caches skip repeated font measuring - the measured size is keyed to the panel's glyphs, so a karaoke repaint that only advances the gradient reuses the size instead of measuring the text again - and the composed lyric panel is memoized per renderer - it is rebuilt only when the track strings, the font size, the header switch or the karaoke progress actually changed, so a frame that reproduces the previous one allocates nothing and reuses the panel string it already has.

  • Idle frames stay cheap: while nothing is on screen the boombox scan drops to every sixth frame and returns to every frame the moment a track is visible; a hidden HUD (toggled off, in a menu scene, or spectating with ShowInSpectate off) skips the whole per-frame rebuild; the player position and Camera.main are only resolved while a boombox is actually tracked.

  • Text work is throttled: the HUD and overlay labels write their anchored position only when it moved instead of dirtying the canvas every frame, config values are re-read only after a setting actually changed, and colour / toggle-key strings are parsed only on change. Outlined text is drawn with two GUI passes instead of an 8-direction outline loop.

  • Diagnostics are quiet: log lines are state-driven - a line is printed when the reported state changes, not on a timer - and the optional verbose heartbeat is capped at one line per topic every 30 seconds.

Requirements

Mod Version Role
BepInExPack 5.4.2305 or later the loader this plugin runs on
CustomBoomboxMusic (CBM) 2.4.2 or later the custom track catalog the plugin resolves names through, and the mod that loads the non-flac audio formats
Lyriclib (BiliBiliMOXL-Lyriclib, GUID Lyriclib) 1.0.0 decodes .flac files below a folder named CustomBoomboxMusic and registers them with the CBM catalog

All three are declared as hard dependencies in manifest.json, so r2modman / Thunderstore Mod Manager installs them together with this mod.

LethalConfig is optional and is deliberately not declared as a dependency. When it is installed, this mod's page carries the Reload button described above; without it every setting still works through BepInEx/config/LyricDisplay.cfg and nothing else changes.

The code still tolerates their absence - without CBM the HUD falls back to the vanilla boombox, without Lyriclib flac files simply stay unloaded (which is CBM's stock behaviour) - but the supported setup is both installed.

Track metadata (Title - Artist - Album) is read from ID3v2 tags and, for FLAC files, from the native VORBIS_COMMENT block. Apart from flac, audio decoding is none of this mod's business: it stays with the game and with CBM / LethalModUtils, whose loader maps .ogg / .mp3 / .wav / .m4a / .aiff only.

FLAC playback is delegated to Lyriclib (BepInEx/plugins/BiliBiliMOXL-Lyriclib): it decodes the .flac files below a folder named CustomBoomboxMusic and registers them with the CBM catalog itself, so they behave like any other track. LyricDisplay does not decode audio any more - it only writes one log line telling you whether Lyriclib was found.

Installation

  1. Install BepInExPack (BepInEx 5.4.2305 or later) for Lethal Company.

  2. Install CustomBoomboxMusic (2.4.2 or later) and Lyriclib. With a mod manager this happens automatically, because manifest.json lists both as required dependencies; when installing by hand, drop all three plugin folders into BepInEx/plugins/.

  3. Copy the LyricDisplay folder from this package into BepInEx/plugins/ so the layout looks like:

    BepInEx/plugins/LyricDisplay/LyricDisplay.dll
    BepInEx/plugins/LyricDisplay/Fonts/...
    BepInEx/plugins/LyricDisplay/source/... (optional, for reference)
    
  4. Start the game once. The plugin writes BepInEx/config/LyricDisplay.cfg; adjust it there (or in any BepInEx config editor) and restart if needed.

  5. Optional check: the BepInEx console / BepInEx/LogOutput.log reports the load lines, for example LyricDisplay v1.0.4 loaded; N lyric file(s) indexed from <folder>, the two Harmony hooks and, once resolved, whether the companion library was found.

Adding Lyrics

  • Create a Lyrics folder next to the plugin (BepInEx/plugins/LyricDisplay/Lyrics) and place .lrc files there. If that folder does not exist, a folder named Lyric (singular) is used instead, so a lyric collection that was created with an older package layout keeps working after an update.

  • A lyric file must share the exact base file name of its audio track. For example, a CBM track named Artist - Title (Album) needs Artist - Title (Album).lrc.

  • Lyric files are also discovered automatically in any folder named CustomBoomboxMusic, Lyrics or Lyric anywhere under BepInEx/plugins (recursive, any nesting depth). Lyrics found in CBM music folders take priority over the local Lyrics folder. Matching is attempted by track name first, then by audio file base name.

  • Supported LRC syntax:

    • Time tags [mm:ss], [mm:ss.xx] and [mm:ss.xxx].
    • Multiple timestamps sharing one lyric line.
    • Global [offset:±ms] metadata tag, applied to every timestamp in the file.
    • Karaoke word tags <start,duration>word (QQ Music / NetEase enhanced LRC exports); offsets are relative to the start of the line and are given in milliseconds.
    • Bilingual LRC: a second line sharing the same timestamp as the original is treated as the translation.
    • Files are read as UTF-8 first, with an ANSI/GBK fallback for legacy files.
    • When the same track exists in several lyric folders, the copy that carries karaoke word tags wins over a plain copy; among equally tagged copies the newest one wins, and folders whose names contain backup, bak or old are skipped during discovery.
  • A minimal example file (Artist - Title (Album).lrc):

    [offset:0]
    [00:12.50]First line of the song
    [00:12.50]第一句歌词(与原句同一时间戳,作为译文)
    [00:16.20]<0,220>Ka<220,240>ra<460,260>o<520,300>ke
    

    The first pair of lines demonstrates the bilingual layout (original plus translation), the last line demonstrates word-level karaoke tags.

Configuration

All options live in BepInEx/config/LyricDisplay.cfg after the first launch. Every entry is described in the file itself as well; the tables below repeat the same defaults and add a little context.

With LethalConfig installed the same entries also appear in the in-game config menu, under this mod's own page, and that page carries one extra control that is not a setting:

  • Reload button: re-reads the lyric and the music folders inside the running game. It is what to press after copying songs or .lrc files into a folder while the game is up - the new files are indexed immediately, without restarting. The button only exists while LethalConfig is installed; it is not written to LyricDisplay.cfg and has no counterpart in the console.

General

Setting Default Description
Enabled true Master switch of the mod. false stops LyricDisplay from loading and running at all - no Harmony hooks, no lyric tracking, no HUD text - so it stays completely inactive until it is re-enabled and the game is restarted.
ToggleDisplayKey End Key to show/hide the HUD. Accepts Unity KeyCode names, e.g. F9 or End. The key is read through the IMGUI event stream, because the game uses the Input System package.
PreloadMusicFolders false Read the lyric and the music folders as soon as the plugin is up - while the game is on its way to the main menu, so before the online / LAN mode is chosen - instead of waiting for the first boombox to play. Kept for troubleshooting a folder or index problem. Every session with the switch on logs the warning again. Do not use this feature unless you know what you are doing. 除非你知道你在做什么,否则不要使用该功能。

Display

Setting Default Description
VerticalOffset 0.26 Height of the lyrics from the bottom of the screen (0–1). 0.13 sits right above the inventory bar.
HorizontalOffset 0 Shift the panel left/right as a fraction of screen width. Negative = left, positive = right.
FontSize 22 Font size of the lyrics.
ShowNextLine true Show the upcoming lyric line below the current one (when there is no translation).
BackgroundAlpha 0.55 Background darkness (0 = transparent, 1 = solid).
HoldAfterEnd 4 How long the last line stays after the music stops (seconds).
TextAlign Center Align text inside the panel: Left, Center or Right.
ShowHeader true Legacy alias of the TrackDetails master switch (Enabled). Kept so an existing config keeps working.
ShowInSpectate true Show lyrics while spectating after death. Set false to hide them.
UseNativeHudLyrics true Draw the lyrics with the game's own HUD: the text is mounted inside the HUD the game builds and drawn with the HUD's own font asset, layer and camera, and it borrows the Chinese glyphs the session already has loaded, so installed Chinese localisation / font packs are picked up automatically. Set false to draw with the bundled / system font instead.
UseUiLyrics false Draw the lyrics on this mod's own screen space overlay canvas (the UI version). Off by default, because the game's own HUD text is the default renderer. With both this and the native HUD text switched off nothing is drawn at all; when an enabled renderer fails the next one is used automatically, and the IMGUI overlay stays the last resort.

Note: the renderer entries used by earlier builds no longer exist - a native-text entry and a Chinese-glyph entry used to sit side by side, and both were merged into UseNativeHudLyrics, which UseUiLyrics now accompanies as the optional overlay. Lines left over from an older LyricDisplay.cfg are not read any more: delete them or leave them orphaned.

TrackDetails

The song-details line above the lyrics (the old "Title - Artist - Album" header), split into its own category. Every switch takes effect live, and the master switch gates all of them: with Enabled off the line is hidden no matter what the sub-switches say.

Setting Default Description
Enabled true Master switch of the song-details line. Off hides the line entirely, regardless of the switches below.
ShowTitle true Show the track title in the details line (missing values render as unknown).
ShowArtist true Show the artist in the details line.
ShowAlbum true Show the album in the details line.
ShowOtherInfo true Append extra tag information - comment, genre, track number, year - after the album when the file carries it. Only fields the file actually has are shown, so nothing renders as noise.

Note: Display:ShowHeader is kept as a legacy alias of TrackDetails:Enabled; both must be on for the line to show. The extra information comes from the file tags - ID3v2 COMM / TCON / TRCK / TYER or TDRC frames for MP3, and the COMMENT / GENRE / TRACKNUMBER / DATE or YEAR Vorbis comments for FLAC - so a track whose tags carry none of them simply shows no extra part.

Distance

Setting Default Description
MaxAudioDistance 35 Max distance (game units) to show a boombox's lyrics; farther boomboxes are ignored.
MaxSimultaneousSources 3 Max number of boomboxes shown at once (closest first, playing sources first).

Gradient

Setting Default Description
GradientEnabled true Karaoke gradient: played words are tinted, upcoming words stay in the unplayed color.
PlayedColor FF0000 Color of already-played words (hex RGB, e.g. FF0000).
UnplayedColor FFFFFF Color of not-yet-played words (hex RGB, e.g. FFFFFF).

Fixes

Setting Default Description
FixLowBatteryPitchAfterRecharge false Restore the boombox pitch to normal once its battery recharges above 5% while the music is still playing (the vanilla game keeps the lowered pitch until the track stops). Off by default to preserve vanilla behaviour.

Format

LyricDisplay has no format settings of its own any more. FLAC playback comes from the Lyriclib library: it decodes the .flac files below a folder named CustomBoomboxMusic (the same discovery rule CBM itself uses) and registers them with the CustomBoomboxMusic catalog. Those registrations carry the CRC-32 CBM computes from the file bytes, so /play, the randomizer and CRC-based network sync treat them like any other track. Every other format keeps being loaded by CustomBoomboxMusic - LyricDisplay never touches ogg / mp3 / wav / m4a / aiff. The flac switch now belongs to the library: its [Format] entry (default on) lives in BepInEx/config/Lyriclib.cfg.

Diagnostics

Setting Default Description
VerboseLogging false Re-print the low-level tracking diagnostics (HUD state, skip reason, lyric resolution) even while nothing changes, at most one line per topic every 30 seconds. Enable only while troubleshooting.

Diagnostic output is state-driven: a line is printed when the reported state changes, not on a timer. A held condition (e.g. no boombox in range) therefore produces one line instead of one line every few seconds.

Rendering paths

There are three renderers behind one shared glyph pipeline. The plugin picks the first one that is both enabled and available:

  1. Native HUD text (UseNativeHudLyrics, on by default): the lyric text is mounted inside the HUD the game builds - under the canvas that carries HUDManager.weightCounter - and drawn with the HUD's own font asset, layer and camera, so the lyrics are produced by the game HUD itself, exactly where the game's own HUD text lives, instead of by a canvas of this mod. The label is mounted right after HUDManager.Start, with a lazy re-attach as a safety net and a re-mount whenever a scene load rebuilds the HUD.

  2. UI overlay (UseUiLyrics, off by default): this mod's own screen space overlay canvas, drawn with the same text engine, the same signed distance field glyphs and the same rich text tags the game UI uses, but outside the game's HUD hierarchy.

  3. IMGUI overlay (always available): the last resort. It stays in place, so an enabled renderer that fails never leaves the player without lyrics.

Both TextMeshPro renderers resolve their glyphs through the same shared lookup, which borrows what the session already has (see below); the panel is measured with GetPreferredValues and capped to a fraction of the HUD, and it sits at the height the IMGUI overlay uses. With UseNativeHudLyrics and UseUiLyrics both switched off nothing is drawn at all.

Font & License

LyricDisplay ships with a Source Han Sans SC subset font so that CJK lyrics render correctly out of the box. If the bundled font file is missing or cannot be loaded, the renderer falls back to an installed Source Han Sans / Noto CJK family and finally to generic CJK system fonts (Microsoft YaHei etc.). The bundled file is registered for the current process only (private font registration), which is what makes the family visible to Unity's dynamic font lookup in this game build.

When UseNativeHudLyrics is on, the HUD draws with the Chinese glyphs the session already has loaded instead of the bundled one: the source face behind a TextMeshPro glyph asset first, then any CJK capable font the game or another mod loaded at runtime. That covers the Chinese localization and font packs on Thunderstore as well - including packs released later - with nothing to update here. The lyrics then share the glyphs the game already renders, the bundled file is neither parsed nor registered with GDI, and the installed family scan is skipped. Only runtime resources of the session are read; nothing provider specific (no plugin GUID, assembly or type name, no mod list) is looked up, and no code and no asset of another mod is copied into this one. The lookup is lazy, runs once per session on the first HUD draw and is memoized, so it adds nothing to the per-frame path. With the switch off, the bundled / fallback path described above is used unchanged.

The glyph lookup tries, in order: the asset handed in by the caller (for the native HUD text that is the game's own HUD font), the glyph assets the session already has loaded for Chinese, the native session font exposed by font-fix style mods, an installed Chinese system family, and finally the preferred asset again even without Chinese coverage - so at least the Latin part of a line stays readable. Every failure path returns "nothing usable" instead of throwing, which lets the caller fall back to the next renderer.

Source Han Sans is licensed under the SIL Open Font License, Version 1.1. The full license text is included as Fonts/LICENSE.txt and LICENSE.txt in this package, and in source/Fonts/LICENSE.txt.

Performance notes

The plugin is written so that a frame which changes nothing does almost nothing:

  • OnGUI runs on Repaint events only, and the render state and skip reason are computed once per frame in Update instead of being recomputed per GUI event.
  • The composed lyric panel is memoized per renderer, and the karaoke memo is kept per render track, so a line whose progress did not move reuses the string and the builder it already has instead of allocating a new one. A panel whose glyphs did not change also reuses its measured size, so a repaint that only advances the gradient never runs the font measurement again, and a frame that reproduces the previous one is detected with a single reference check.
  • While nothing is on screen the boombox scan runs every sixth frame (about 10 Hz at 60 fps, so a boombox that starts playing is picked up within roughly 100 ms) and returns to every frame as soon as a track is visible; hiding the HUD resets the countdown, so the first visible frame polls immediately.
  • The native HUD label and the UI overlay label only write RectTransform.anchoredPosition when the target position actually changed, instead of marking the canvas dirty every frame.
  • Config values are re-read only after a setting actually changed (each entry subscribes to its change event), with a slow two-second refresh as a safety net, and colour / toggle-key strings are parsed only on change.
  • Distance filtering and sorting use squared distance, the playback clock and audio output sampling run only for tracks that will actually be shown and are throttled per track, and reflection lookups are cached per type.
  • Text outlines are drawn with two GUI passes instead of an 8-direction outline loop.
  • A hidden HUD (toggled off, in a menu scene, or spectating with ShowInSpectate off) skips the whole per-frame rebuild, and the player position / Camera.main are only resolved while a boombox is actually tracked.

Troubleshooting

Start with [Diagnostics] VerboseLogging = true and read the BepInEx console or BepInEx/LogOutput.log. The plugin reports why it is not drawing through the skip-reason line:

Skip reason Meaning
not in game scene (scene='<name>') You are on the main menu or a loading / non-gameplay scene; the HUD is drawn in gameplay scenes only.
display toggled off The HUD was hidden with ToggleDisplayKey. Press it again.
spectating (player dead) You are dead and spectating while ShowInSpectate is false.
no audio source within range No boombox is playing within MaxAudioDistance game units.
no renderable lyrics for nearby sources A boombox is in range, but no .lrc file matched its name (files are matched by base file name) or the file produced no timed lines.

Common cases:

  • No lyrics at all, and the log says the mod loaded: check Enabled, press ToggleDisplayKey (End by default), make sure you are in a gameplay scene, and that the boombox is within MaxAudioDistance.
  • Nothing is drawn although a track is playing: UseNativeHudLyrics and UseUiLyrics are both off. Turn at least one on.
  • Nothing is drawn and the log warns No .lrc lyric file was found: the plugin indexed no lyric file at all, so neither renderer has anything to draw. Put the .lrc files in BepInEx/plugins/LyricDisplay/Lyrics (a folder named Lyric is accepted as well), or next to the music inside a folder named CustomBoomboxMusic, and name each file exactly like its audio track. The startup line LyricDisplay v… loaded; N lyric file(s) indexed from … reports how many files were found.
  • Chinese renders as boxes: the bundled font could not be loaded and no CJK font was found. Turn UseNativeHudLyrics on to borrow the session's Chinese glyphs, or install a CJK font pack.
  • The gradient never moves: the line has no word tags. The log reports it as [Gradient] current line has NO word tags - karaoke skipped (line='...'); only enhanced LRC files carry per-word timings.
  • Lyrics are shifted against the music: check the [offset:±ms] tag in the lyric file and that the file really belongs to that track (same base file name).
  • A flac track plays but shows no lyrics, or does not play at all: flac playback and the flac track registration belong to Lyriclib. The log reports [FLAC] playback is provided by Lyriclib <version> when it was found and warns when flac tracks are present without it.
  • A renderer failed: the log names the failing path ([HUD][Native] ... / [HUD][TMP] ...) and the plugin falls through to the next enabled renderer, so the lyrics normally stay visible.

Source Code

The source/ folder contains the full C# source (Plugin.cs, SongTracker.cs, LyricLibrary.cs, LyricParser.cs, LyricTimeline.cs, LyricHudRenderer.cs, TrackMetadata.cs, BoomboxBatteryFix.cs, NativeFontRegistrar.cs, LyriclibBridge.cs, FontFixBridge.cs, TmpGlyphSource.cs, NativeLyricText.cs, TmpLyricOverlay.cs, LyricRichText.cs, LethalConfigBridge.cs, Diag.cs), the project file LyricDisplay.csproj and the bundled font files.

LethalConfigBridge.cs adds the Reload button to this mod's page when the optional LethalConfig mod is installed, and does nothing at all when it is not; the PreloadMusicFolders switch lives in Plugin.cs next to the other general settings. BoomboxBatteryFix.cs implements the optional low-battery pitch recovery fix behind FixLowBatteryPitchAfterRecharge. The FLAC decoder and the catalog bridge that earlier builds carried as source/Flac/ are no longer part of this package: they moved into the separate Lyriclib library, which loads the flac tracks on its own and which LyricDisplay calls nothing into. LyriclibBridge.cs is the only trace left - it reports in the log whether the library was found. See CHANGELOG.md for the version history.


LyricDisplay(中文版)

简体中文

LyricDisplay 是一个用于 Lethal Company 的 BepInEx 插件,它会把音响(Boombox)正在播放的音乐歌词渲染为 HUD 元素,显示在物品栏上方。它兼容原版音响与 CustomBoomboxMusic(CBM) 添加的自定义曲目,并且在多个音响处于范围内时可以同时显示各自歌词。

歌词来自与音乐放在一起的 .lrc 文件。本插件不联网获取任何内容,也不分析音频:它只按曲目的基础文件名找到对应歌词文件,随后跟随音响的播放时钟推进。

一览

项目 内容
版本 1.0.4
游戏 Lethal Company
加载器 BepInEx 5.4.2305(BepInExPack)
必需模组 CustomBoomboxMusic 2.4.2 或更高、Lyriclib 1.0.0
配置文件 BepInEx/config/LyricDisplay.cfg(首次启动时生成)
歌词目录 BepInEx/plugins/LyricDisplay/Lyrics(名为 Lyric 的文件夹同样接受),以及 BepInEx/plugins 下任意名为 CustomBoomboxMusic、Lyrics 或 Lyric 的文件夹
HUD 切换键 End(ToggleDisplayKey)
默认渲染器 游戏自身 HUD 文本(UseNativeHudLyrics = true)

1.0.4 新功能

  • LethalConfig 的 Reload 按钮现在真的会出现。 该按钮的反射注册此前只认得回调参数为 System.Action 的按钮项;LethalConfig 1.4.x 实际传入的是它自己的委托类型(GenericButtonOptions.GenericButtonHandler,void()),因此没有任何构造函数匹配,桥接代码记录警告后跳过了按钮 —— 该条目从未出现在本模组页面上。现在注册逻辑接受该委托形态,并改用 AddConfigItem(BaseConfigItem, Assembly) 重载显式传入本插件的程序集,使按钮确实落在本模组的页面上,而不再依赖反射调用链下并不可靠的 Assembly.GetCallingAssembly()。

  • 按钮问题可自诊断。 若日后 LethalConfig 再次改版,BepInEx/LogOutput.log 中的警告会直接打印桥接代码实际找到的构造函数签名,而不再只说"没找到可用的"。

  • LethalConfig 页面新增 Reload 按钮。 安装 LethalConfig 后,本模组页面会多出一个按钮:点击即在游戏运行中重新读取歌词与音乐文件夹,游戏运行期间拷贝进来的歌曲与 .lrc 文件当场被索引,无需重启游戏。它走的正是本模组已有的「文件夹内容变化后补读」同一条代码路径,因此结果与下一次播放音响自然得到的结果一致。

  • 可选的音乐文件夹预加载(PreloadMusicFolders,默认关闭)。 开启后,在玩家选择联网 / 本地模式之前(游戏尚未进入主菜单时就)读取歌词与音乐文件夹,而不是等到第一台音响开始播放。该选项用于排查文件夹或索引问题;开启开关的每一局都会重新打印一次警告,因为读取时机早于游戏本身的节奏,即使整局都没有播放过音响也要付出加载时间与内存开销:除非你知道你在做什么,否则不要使用该功能。/ Do not use this feature unless you know what you are doing.

  • 版本号重新统一。 Plugin.PLUGIN_VERSION、csproj 的 <Version> 与 manifest.json 现均为 1.0.4;此前工程文件仍写 1.0.3。

更早版本(1.0.3)要点:

  • 歌词改由游戏自身的 HUD 来画。 UseNativeHudLyrics(默认开启)是 1.0.3 新增的渲染方式,也是现在的默认画法:歌词挂在游戏自身构建的 HUD 层级里、负重计数器旁边,用 HUD 自己的字体、图层、相机与缩放绘制,看上去和 HUD 上其它文字是同一套。中文歌词直接用游戏自身的中文字形,装了中文化或中文字体模组就能正常显示,不需要额外配置。
  • 模组自带的叠加层改为可选。 UseUiLyrics(默认关闭)为希望歌词脱离游戏 HUD 层级的用户保留本模组自建的屏幕空间叠加 Canvas。两个开关都关闭时不绘制歌词;IMGUI 叠加层仍为最后兜底。
  • 渲染器配置项合并为上述两个开关。 UseNativeHudLyrics 既用游戏自身 HUD 文本绘制,也在同一条路径里借用游戏运行期已有的字形。
  • 名为 Lyric(单数)的文件夹同样会被索引。 放在 Lyric(而非 Lyrics)目录中的歌词文件现可被找到,无论是位于插件旁还是 BepInEx/plugins 下任意位置,因此按早期包结构摆放的歌词集在更新后仍可继续使用。
  • 稳定帧开销更低。 屏幕上没有歌词时音响扫描降为每 6 帧一次;组合面板与卡拉 OK 缓存按渲染器 / 按轨道分别缓存;字形未变的歌词行直接复用已测量尺寸,不为每一次渐变推进重测一遍;HUD 标签在位置未变化时不再弄脏 Canvas——与上一帧相同的帧只需一次引用比较即可判定。

功能特性

追踪

  • 实时歌词 HUD:当前歌词行(以及可选的下一句或译文)显示在物品栏上方,且仅在游戏场景中渲染(主菜单与加载界面不会显示)。

  • 原版 + CustomBoomboxMusic 支持:安装 CBM 时通过其公开的 AudioManager 曲目目录解析曲名,未安装时自动回退为音频片段名,因此原版音响同样可用。

  • 距离过滤与多音响显示:附近音响按距离排序,最近的若干台(最多 MaxSimultaneousSources 台,播放中的优先)堆叠显示,每台带独立头部信息行;超出 MaxAudioDistance 的音响会被忽略。

  • 观战支持:死亡后观战时歌词仍然可见,由 ShowInSpectate 控制;观战状态通过反射检测(死亡标志 + 激活的观战 UI),不硬依赖游戏程序集。

歌词内容

  • 双语歌词:当 LRC 文件中存在与原句共享同一时间戳的翻译行时,HUD 会显示原句并在其下方显示译文;完全相同的重复行视为无译文。

  • 第二行解析:无译文时,在当前句下方显示下一句歌词(即 ShowNextLine 行为)。

  • 卡拉 OK 逐字渐变:增强型 LRC 导出(QQ 音乐 / 网易云 <start,duration>word 标签)可渲染逐字红白卡拉 OK 渐变;正在演唱的字会从白色平滑过渡为红色。中日韩 / 谚文 / 假名的逐字标签行保持无间隙,拉丁词边界则与纯文本完全一致地保留。

  • 时间轴驱动高亮:卡拉 OK 高亮边界完全由歌词字时间推导,卡拉 OK 富文本每个 Update 从当前时间轴位置重建一次,因此渐变始终与实时播放一致,无需音频处理或任何能量调制。

  • 曲目头部信息行:歌词上方显示 标题 - 歌手 - 专辑,读取自 ID3v2 标签(TIT2 / TPE1 / TALB)或 FLAC 的 VORBIS_COMMENT 块(TITLE / ARTIST / ALBUM),缺失时回退到 歌手 - 标题 (专辑) 文件名格式;缺少的字段显示为 unknown。

渲染

  • 默认使用游戏原生 HUD 文本:UseNativeHudLyrics(默认开启)用游戏自身 HUD 绘制歌词——歌词文字挂在游戏构建的 HUD 层级内、负重计数器旁边,使用 HUD 自身的字体、图层与相机——并直接采用游戏运行期已有的中文字形,因此已安装的中文本地化 / 字模模组同样生效;关闭该开关时才使用内置字体。

  • 可选 UI 叠加层:UseUiLyrics(默认关闭)改为使用本模组自建的屏幕空间叠加 Canvas 绘制歌词;已启用的渲染器不可用时,IMGUI 叠加层仍为最后兜底;两者都关闭则不绘制任何歌词。

  • 文本对齐与偏移:面板内支持 Left / Center / Right 对齐,外加屏幕垂直 / 水平偏移,便于把面板移出原版 HUD 的布局区域。

  • 低电量音调修复(可选):启用 FixLowBatteryPitchAfterRecharge 后,音响电量回升到 5% 以上时会恢复原版正常音调,避免音乐一直以慢速播放直到曲目重启。

性能

  • 无变化即跳过渲染工作:OnGUI 仅过滤 Repaint 事件;逐帧布局与渲染缓存避免重复测量字体(已测尺寸以面板字形为键,只推进渐变的重绘直接复用该尺寸,不再重测文本);组合后的歌词面板按渲染器分别记忆化——仅当曲目字符串、字号、头部开关或卡拉 OK 进度确实变化时才重建,因此与前一阵完全相同的帧不产生任何分配,直接复用已有面板字符串。

  • 空闲帧开销极低:屏幕上没有任何歌词时,音响扫描降为每 6 帧一次,一旦有曲目可见立即恢复逐帧;HUD 不可见时(已关闭 / 菜单场景 / 观战且 ShowInSpectate 关闭)整段逐帧重建被跳过;仅在确实追踪到音响时才解析玩家坐标与 Camera.main。

  • 文本相关工作限流:HUD 与叠加层标签仅在其锚点位置真正变化时才写入,不再逐帧弄脏 Canvas;配置值仅在设置真正变更后才重新读取,颜色 / 切换键字符串仅在变更时解析;描边文本用两趟 GUI 绘制,替代原先的 8 方向描边循环。

  • 诊断安静:日志为状态驱动——仅在所报状态发生变化时打印,而非按定时器打印;可选的详细日志心跳最多每 30 秒每个主题一行。

前置要求

模组 版本 作用
BepInExPack 5.4.2305 或更高 本插件所运行的加载器
CustomBoomboxMusic(CBM) 2.4.2 或更高 本插件解析曲名所用的自定义曲目目录,同时负责加载非 flac 音频格式
Lyriclib(BiliBiliMOXL-Lyriclib,GUID Lyriclib) 1.0.0 解码名为 CustomBoomboxMusic 的文件夹下的 .flac 文件,并注册进 CBM 曲库

三者均已在 manifest.json 中声明为硬依赖,r2modman / Thunderstore Mod Manager 安装本模组时会自动一并安装。

LethalConfig 为可选模组,且有意未声明为依赖。安装它后,本模组页面会出现上文所述的 Reload 按钮;未安装时所有设置仍通过 BepInEx/config/LyricDisplay.cfg 生效,其他行为不变。

代码层仍可容忍缺失——无 CBM 时回退原版音响,无 Lyriclib 时 flac 文件不加载(即 CBM 的原始行为)——但受支持的运行环境为两者齐备。

曲目元数据(标题 - 歌手 - 专辑)读取自 ID3v2 标签,FLAC 文件则读取其原生 VORBIS_COMMENT 块。除 flac 外,音频解码不归本模组负责:由游戏本体与 CBM / LethalModUtils 处理,其加载器仅映射 .ogg / .mp3 / .wav / .m4a / .aiff。

flac 播放已委托给配套库 Lyriclib(BepInEx/plugins/BiliBiliMOXL-Lyriclib):由它解码名为 CustomBoomboxMusic 的文件夹下的 .flac 文件并注册进 CBM 曲库,使其行为等同普通曲目。LyricDisplay 不再自行解码音频,仅在日志中报告是否找到 Lyriclib。

安装方法

  1. 为 Lethal Company 安装 BepInExPack(BepInEx 5.4.2305 或更高版本)。

  2. 安装 CustomBoomboxMusic(2.4.2 或更高)与 Lyriclib。使用模组管理器时会自动完成,因为 manifest.json 已将两者列为必需依赖;手动安装时把三个插件文件夹都放进 BepInEx/plugins/ 即可。

  3. 将本包中的 LyricDisplay 文件夹复制到 BepInEx/plugins/,目录结构如下:

    BepInEx/plugins/LyricDisplay/LyricDisplay.dll
    BepInEx/plugins/LyricDisplay/Fonts/...
    BepInEx/plugins/LyricDisplay/source/...(可选,供参考)
    
  4. 启动一次游戏。插件会写出 BepInEx/config/LyricDisplay.cfg,可在该文件或任意 BepInEx 配置编辑器中按需调整,必要时重启游戏。

  5. 可选验证:BepInEx 控制台 / BepInEx/LogOutput.log 中会输出加载信息,例如 LyricDisplay v1.0.4 loaded; N lyric file(s) indexed from <folder>、两条 Harmony 钩子信息,以及在解析完成后是否找到配套库的结论。

添加歌词

  • 在插件旁创建 Lyrics 文件夹(BepInEx/plugins/LyricDisplay/Lyrics)并放入 .lrc 文件。若该文件夹不存在,则改用名为 Lyric 的文件夹,因此按旧包结构摆放的歌词在更新后仍可继续使用。

  • 歌词文件必须与其音频曲目使用完全相同的基础文件名。例如 CBM 曲目名为 Artist - Title (Album),则歌词文件需命名为 Artist - Title (Album).lrc。

  • 歌词文件也会在 BepInEx/plugins 下任意名为 CustomBoomboxMusic、Lyrics 或 Lyric 的文件夹中自动发现(递归,任意嵌套深度)。CBM 音乐文件夹中的歌词优先于本地 Lyrics 文件夹;匹配时先按曲目名,其次按音频文件基础名。

  • 支持的 LRC 语法:

    • 时间标签 [mm:ss]、[mm:ss.xx] 与 [mm:ss.xxx]。
    • 同一行歌词携带多个时间戳。
    • 全局 [offset:±ms] 元数据标签,作用于文件内所有时间戳。
    • 卡拉 OK 逐字标签 <start,duration>word(QQ 音乐 / 网易云增强 LRC 导出),偏移相对行首、单位为毫秒。
    • 双语 LRC:与原句共享同一时间戳的第二行被视为译文。
    • 文件优先按 UTF-8 读取,对旧文件提供 ANSI/GBK 回退。
    • 当同一曲目存在于多个歌词文件夹时,带卡拉 OK 逐字标签的副本优先于普通副本;均带标签时取最新的一份;文件夹名包含 backup、bak 或 old 的目录在发现过程中会被跳过。
  • 最小示例文件(Artist - Title (Album).lrc):

    [offset:0]
    [00:12.50]First line of the song
    [00:12.50]第一句歌词(与原句同一时间戳,作为译文)
    [00:16.20]<0,220>Ka<220,240>ra<460,260>o<520,300>ke
    

    前两行演示双语排版(原句 + 译文),最后一行演示逐字卡拉 OK 标签。

配置项

首次启动后,所有选项均位于 BepInEx/config/LyricDisplay.cfg。配置文件内每一项也自带说明,下表重复同样的默认值并补充了一点背景。

安装 LethalConfig 后,同样的条目也会出现在游戏内配置菜单的本模组页面中,且该页面还带有一个并非配置项的控件:

  • Reload 按钮:在游戏运行中重新读取歌词与音乐文件夹。游戏开着时把歌曲或 .lrc 文件拷进文件夹后按它即可,新文件立刻被索引,不必重启游戏。该按钮仅在安装了 LethalConfig 时存在,不会写入 LyricDisplay.cfg,也没有对应的控制台选项。

通用

设置项 默认值 说明
Enabled true 模组总开关。设为 false 时 LyricDisplay 完全不加载、不运行:不打任何 Harmony 补丁、不跟踪歌词、不绘制任何文本,直到重新开启并重启游戏为止。
ToggleDisplayKey End 显示 / 隐藏 HUD 的按键。接受 Unity KeyCode 名称,例如 F9 或 End。因游戏使用 Input System 包,该按键通过 IMGUI 事件流读取。
PreloadMusicFolders false 插件一启动就读取歌词与音乐文件夹——此时游戏尚未进入主菜单,因而早于玩家选择联网 / 本地模式——而不是等到第一台音响开始播放。该选项用于排查文件夹或索引问题。开启开关的每一局都会重新打印一次警告。除非你知道你在做什么,否则不要使用该功能。Do not use this feature unless you know what you are doing.

显示

设置项 默认值 说明
VerticalOffset 0.26 歌词距屏幕底部的高度(0–1)。0.13 时正好位于物品栏上方。
HorizontalOffset 0 面板左右偏移,为屏幕宽度的比例。负数 = 左移,正数 = 右移。
FontSize 22 歌词字号。
ShowNextLine true 在无译文时于当前句下方显示下一句歌词。
BackgroundAlpha 0.55 背景不透明度(0 = 透明,1 = 实心)。
HoldAfterEnd 4 音乐停止后末句保留的时间(秒)。
TextAlign Center 面板内文本对齐方式:Left、Center 或 Right。
ShowHeader true TrackDetails 主开关(Enabled)的兼容别名,保留以兼容旧配置。
ShowInSpectate true 死亡后观战时是否显示歌词。设为 false 则隐藏。
UseNativeHudLyrics true 使用游戏自身 HUD 绘制歌词:歌词文字挂在游戏构建的 HUD 层级内,使用 HUD 自身的字体资产、图层与相机,并借用游戏运行期已有的中文字形,因此已安装的中文本地化 / 字模模组会被自动识别。设为 false 则改用内置 / 系统字体绘制。
UseUiLyrics false 改为使用本模组自建的屏幕空间叠加 Canvas 绘制歌词(UI 版本)。默认关闭,因为游戏自身 HUD 文本才是默认渲染器。与 UseNativeHudLyrics 同时关闭则不绘制任何歌词;已启用的渲染器若失败会自动改用下一个,IMGUI 叠加层是最后的兜底。

注:早前版本使用的渲染器配置项已不复存在——原先「原生文本渲染器」与「借用中文字形」是两个并列项,现均并入 UseNativeHudLyrics,并搭配 UseUiLyrics 作为可选的叠加层。既有 LyricDisplay.cfg 中残留的旧行不再被读取——可自行删除或留作无效项。

歌曲详情(TrackDetails)

歌词上方的歌曲详情行(即原来的"标题 - 歌手 - 专辑")拆分成的独立分类。所有开关实时生效,总开关控制全部子开关:Enabled 关闭时无论子开关如何,整行都隐藏。

设置项 默认值 说明
Enabled true 歌曲详情行的总开关。关闭后无论下方开关如何,整行隐藏。
ShowTitle true 在详情行显示歌曲名(缺失字段显示 unknown)。
ShowArtist true 在详情行显示歌手。
ShowAlbum true 在详情行显示专辑。
ShowOtherInfo true 在专辑之后追加额外标签信息——注释、流派、音轨号、年份(文件带有才显示)。

注:Display:ShowHeader 作为 TrackDetails:Enabled 的兼容别名保留;两者都开启时详情行才会显示。额外信息来自文件标签——MP3 取 ID3v2 的 COMM / TCON / TRCK / TYER 或 TDRC 帧,FLAC 取 COMMENT / GENRE / TRACKNUMBER / DATE 或 YEAR Vorbis 注释——文件里没有这些标签时就不显示额外部分,不会出现无意义的占位。

距离

设置项 默认值 说明
MaxAudioDistance 35 显示某台音响歌词的最大距离(游戏单位);更远的音响会被忽略。
MaxSimultaneousSources 3 同时显示的音响数量上限(优先最近的,播放中的优先)。

渐变

设置项 默认值 说明
GradientEnabled true 卡拉 OK 渐变:已唱过的字着色,未唱的字保持未播颜色。
PlayedColor FF0000 已唱部分的颜色(十六进制 RGB,例如 FF0000)。
UnplayedColor FFFFFF 未唱部分的颜色(十六进制 RGB,例如 FFFFFF)。

修复

设置项 默认值 说明
FixLowBatteryPitchAfterRecharge false 音乐仍在播放、电量回升至 5% 以上时,将音响音调恢复为正常(原版游戏会一直保持降调直到曲目停止)。默认关闭以保留原版行为。

格式

LyricDisplay 不再有自己的格式配置项。flac 播放由 Lyriclib 库提供:它解码名为 CustomBoomboxMusic 的文件夹下的 .flac 文件(与 CBM 自身的扫描规则一致)并注册进 CustomBoomboxMusic 曲库。注册条目携带按文件字节算出的、与 CBM 相同的 CRC-32,因此 /play、随机播放与基于 CRC 的联机同步都将其视作普通曲目。其他格式仍由 CustomBoomboxMusic 加载,LyricDisplay 不接管 ogg / mp3 / wav / m4a / aiff。flac 开关现归该库所有:其 [Format] 条目(默认开启)位于 BepInEx/config/Lyriclib.cfg。

诊断

设置项 默认值 说明
VerboseLogging false 即使在状态未变化时也重复输出底层追踪诊断(HUD 状态、跳过原因、歌词解析),每个主题最多每 30 秒一行。仅在排查问题时开启。

诊断输出为状态驱动:仅在所报状态发生变化时打印,而非按定时器打印。因此持续成立的条件(例如附近没有音响)只会输出一行,而不是每隔几秒输出一行。

渲染路径

插件共有一条字形管线、三条渲染路径,会选用第一个「已启用且可用」的渲染器:

  1. 原生 HUD 文本(UseNativeHudLyrics,默认开启):歌词文字挂在游戏构建的 HUD 层级内——HUDManager.weightCounter 所在的 Canvas 下——使用 HUD 自身的字体资产、图层与相机绘制。歌词因此由游戏 HUD 本体绘制、与游戏自己的 HUD 文字同处一层,而不是由本模组自建 Canvas 绘制。文本在 HUDManager.Start 之后立即挂载,并以惰性重挂作为兜底,切换场景重建 HUD 后会自动重挂。

  2. UI 叠加层(UseUiLyrics,默认关闭):本模组自建的屏幕空间叠加 Canvas,使用与游戏 UI 相同的文本引擎、有向距离场字形与富文本标签,但不进入游戏 HUD 层级。

  3. IMGUI 叠加层(始终可用):最后的兜底,因此某个已启用渲染器失败时,玩家不会失去歌词显示。

两条 TextMeshPro 路径共用同一套字形查找(见下文),面板用 GetPreferredValues 测量并限制在 HUD 一定比例内,高度与 IMGUI 叠加层一致。UseNativeHudLyrics 与 UseUiLyrics 同时关闭则不绘制任何歌词。

字体与许可

LyricDisplay 内置了**思源黑体(Source Han Sans SC)**子集字体,保证中日韩歌词开箱即用地正确渲染。若内置字体文件缺失或无法加载,渲染器会回退到已安装的思源黑体 / Noto CJK 字体族,最后回退到系统通用 CJK 字体(微软雅黑等)。内置字体仅对当前进程注册(私有字体注册),这也是本游戏版本中 Unity 动态字体查找能够看到该字体族的原因。

UseNativeHudLyrics 开启时,HUD 直接采用游戏运行期已加载的中文字形:优先使用 TMP 字形资产背后的源字体,其次使用游戏或其它模组在运行时加载的中文字体。Thunderstore 上的中文本地化 / 字模模组因此同样被自动借用——包括后续发布的模组,本模组无需任何改动。使用这些字形时,歌词与游戏共用同一套字形,内置字体文件不会被解析,也不会注册到 GDI,已安装字体族扫描同样跳过。这里只读取运行期已加载的字体资源,不读取任何模组的身份(插件 GUID / 程序集名 / 类型名),也不维护兼容名单,不复制其任何代码与资源。查找为惰性执行,每局仅在首次绘制 HUD 时运行一次并缓存,不增加任何逐帧开销。关闭该开关时,仍走上面的内置 / 回退路径。

字形查找的尝试顺序为:调用方传入的首选资产(对原生 HUD 文本即游戏自身 HUD 字体)、游戏运行期已加载的中文字形资产、由字体修复类模组暴露的原生会话字体、已安装的中文系统字体族,最后再回到首选资产(即使不含中文覆盖),以保证至少拉丁部分可读。所有失败路径都返回「不可用」而不抛异常,从而让调用方切换到下一个渲染器。

思源黑体采用 SIL Open Font License, Version 1.1 许可。完整许可文本包含在本包的 Fonts/LICENSE.txt 与 LICENSE.txt,以及 source/Fonts/LICENSE.txt 中。

性能说明

插件的写法保证「什么都没变的帧几乎不做事」:

  • OnGUI 仅在 Repaint 事件上运行,渲染状态与跳过原因在 Update 中每帧只计算一次,不再按 GUI 事件重复计算。
  • 组合后的歌词面板按渲染器分别记忆化,卡拉 OK 缓存按渲染轨道各自保存:进度未推进的行直接复用已有字符串与构建器,不再产生新分配。字形未变的行同时复用已测量尺寸,只为推进渐变而重绘时不会再次触发字体测量;与上一帧相同的帧由一次引用比较即可判定。
  • 屏幕上没有歌词时,音响扫描每 6 帧执行一次(60 fps 下约 10 Hz,因此刚开始播放的音响大约 100 ms 内即被发现),一旦有曲目可见立即恢复逐帧;隐藏 HUD 会重置计数,使第一个可见帧立刻执行扫描。
  • 原生 HUD 标签与 UI 叠加层标签仅在锚点目标位置真正变化时才写入 RectTransform.anchoredPosition,不再逐帧弄脏 Canvas。
  • 配置值仅在设置真正变更后才重新读取(每个配置项订阅自身的变更事件),另设 2 秒的慢速兜底刷新;颜色 / 切换键字符串仅在变更时解析。
  • 距离过滤与排序使用平方距离;播放时钟与音频输出采样仅对实际会显示的曲目运行并按曲目限流;反射查找按类型缓存。
  • 文本描边用两趟 GUI 绘制,替代原先 8 方向描边循环。
  • HUD 不可见时(已关闭 / 菜单场景 / 观战且 ShowInSpectate 关闭)整段逐帧重建被跳过;仅在确实追踪到音响时才解析玩家坐标与 Camera.main。

故障排查

先开启 [Diagnostics] VerboseLogging = true,再查看 BepInEx 控制台或 BepInEx/LogOutput.log。插件会通过「跳过原因」行说明当前为何没有绘制:

跳过原因 含义
not in game scene (scene='<name>') 你处于主菜单或加载 / 非游戏场景;HUD 仅在游戏场景绘制。
display toggled off HUD 已被 ToggleDisplayKey 隐藏,再按一次即可恢复。
spectating (player dead) 你已死亡并在观战,而 ShowInSpectate 为 false。
no audio source within range MaxAudioDistance 游戏单位内没有正在播放的音响。
no renderable lyrics for nearby sources 范围内有音响,但没有匹配到 .lrc 文件(按基础文件名匹配),或该文件没有可用时间行。

常见情况:

  • 完全没有歌词,日志显示模组已加载:检查 Enabled;按一次 ToggleDisplayKey(默认 End);确认处于游戏场景;确认音响在 MaxAudioDistance 范围内。
  • 曲目在播放却什么都没画:UseNativeHudLyrics 与 UseUiLyrics 都被关闭了,至少开启其中一个。
  • 什么都没画,且日志警告 No .lrc lyric file was found:插件没有索引到任何歌词文件,两条渲染路径都无内容可画。请把 .lrc 文件放入 BepInEx/plugins/LyricDisplay/Lyrics(名为 Lyric 的文件夹同样接受),或与音乐一起放在名为 CustomBoomboxMusic 的文件夹内,并确保文件名与其音频曲目完全一致。启动行 LyricDisplay v… loaded; N lyric file(s) indexed from … 会报告索引到的文件数量。
  • 中文显示为方块:内置字体加载失败且未找到 CJK 字体。开启 UseNativeHudLyrics 借用会话中文字形,或安装 CJK 字模。
  • 渐变始终不动:该行没有逐字标签。日志会输出 [Gradient] current line has NO word tags - karaoke skipped (line='...');只有增强型 LRC 文件才带逐字时间。
  • 歌词与音乐不同步:检查歌词文件中的 [offset:±ms] 标签,并确认该文件确实属于这首曲目(基础文件名一致)。
  • flac 曲目能播放但没有歌词,或干脆不播放:flac 播放与曲目注册属于 Lyriclib。找到该库时日志输出 [FLAC] playback is provided by Lyriclib <version>,存在 flac 曲目却未安装时会打印警告。
  • 某个渲染器失败:日志会指明失败的路径([HUD][Native] ... / [HUD][TMP] ...),插件会自动切换到下一个已启用的渲染器,通常歌词仍可见。

源代码

source/ 文件夹包含完整 C# 源码(Plugin.cs、SongTracker.cs、LyricLibrary.cs、LyricParser.cs、LyricTimeline.cs、LyricHudRenderer.cs、TrackMetadata.cs、BoomboxBatteryFix.cs、NativeFontRegistrar.cs、LyriclibBridge.cs、FontFixBridge.cs、TmpGlyphSource.cs、NativeLyricText.cs、TmpLyricOverlay.cs、LyricRichText.cs、LethalConfigBridge.cs、Diag.cs)、项目文件 LyricDisplay.csproj 与内置字体文件。

LethalConfigBridge.cs 在安装了可选的 LethalConfig 模组时,把 Reload 按钮加到本模组页面上,未安装时它不做任何事;PreloadMusicFolders 开关则位于 Plugin.cs 中,与其他通用设置放在一起。BoomboxBatteryFix.cs 实现了由 FixLowBatteryPitchAfterRecharge 控制的可选低电量音调恢复补丁。曾作为 source/Flac/ 携带的 FLAC 解码器与曲库桥接代码已不在本包内:它们迁入独立的 Lyriclib 库,由该库自行加载 flac 曲目,LyricDisplay 侧不调用任何接口。仅保留 LyriclibBridge.cs 作为痕迹,用于在日志中报告是否找到了该库。版本历史见 CHANGELOG.md。

Thunderstore development is made possible with ads. Please consider making an exception to your adblock.