Bind a key
Problem. Your overlay needs an on/off key.
There are two routes, and the honest summary is: the quick one takes five minutes, the proper one requires reaching into the game's keybinding registry.
Quick: legacy Unity input
The game uses KeyCode-based bindings, so UnityEngine.Input works. Add the reference:
<Reference Include="UnityEngine.InputLegacyModule">
<HintPath>$(SPZ2_PATH)\UnityEngine.InputLegacyModule.dll</HintPath>
<Private>False</Private>
</Reference>
Then poll in your tick:
using UnityEngine;
private void Tick(float deltaTime)
{
if (Input.GetKeyDown(KeyCode.F4))
{
Enabled = !Enabled;
Logger.Info?.Log($"overlay {(Enabled ? "on" : "off")}");
}
}
Because OnTick only fires during a session, this is automatically inert in menus.
What you give up: the key is hard-coded, invisible in the game's keybinding settings, and can collide with a vanilla or another mod's binding. Fine for a personal tool or a first iteration; not what you want to ship widely.
Pick something unlikely to collide (F keys are safer than letters) and make it
configurable via your mod save data or a config file if players will
use it.
Proper: a real keybinding
Registered bindings appear in the settings UI, are rebindable, respect modifiers, and route through the same consumption model as vanilla — which matters, because that is what stops your key firing while a dialog or text field has focus.
The pieces involved:
// Game.Core
public class Keybinding
{
public string Id { get; }
public IText Title => ("keybinding." + Id).T();
public IText Description => ("keybinding." + Id + ".description").T();
}
new Keybinding("toggle-research", new KeySet(KeyCode.T));
Defaults are produced by DefaultKeybindings.ComputeDefaults(), which returns layers of
bindings; Keybindings copies those layers at construction and indexes them into
KeybindingsById. Consumption happens through InputDownstreamContext:
if (context.ConsumeWasActivated("my-mod.toggle-overlay")) { … }
ConsumeWasActivated marks the input as handled so nothing downstream sees it;
IsActivated checks without consuming.
You will also want translation entries for keybinding.<id> and
keybinding.<id>.description, or the settings UI shows the raw key.
Warning
ShapezShifter has no keybinding registration API at the time of writing, and
DetourHelper's postfix overloads cannot rewrite a return value — so you cannot
simply postfix ComputeDefaults() and append. Getting a binding registered means
reaching the Keybindings instance after construction (its fields are reachable via
the publicizer) and adding to it. That is unverified territory:
confirm against the assemblies for your game version before relying on it.
Gotchas
- Legacy
Inputdoes not know about UI focus. If the player is typing in a dialog, your key still fires. Check for an open dialog before acting on it, or accept the rough edge. - Do not poll input from a draw hook. Draw hooks can run more than once per frame in some configurations; a tick fires once.