ComputerApps
This page uses GameObject, Sprite from Unity. The glossary says what each one is.
The game has computer desks the player can sit at. ComputerApps puts an app on them. An app is an icon on each desk’s screen that opens your own interface, and it can also open in a full-screen window. The API also gives any object in the world a use prompt like the game’s own (“Use”, “Read” and so on). You draw the interface yourself, using the UI kit page.
ComputerApps apps on the desk computers
Call Register to put an app icon on every desk, and AddInteraction to give a world object a use prompt. OpenStandalone opens an app in a full-screen window.
public static class ComputerApps
{
public static bool IsAvailable { get; }
public static event Action<ComputerAppClosedArgs> AppClosed;
public static event Action<RectTransform> ScreenChanged;
public static RectTransform ActiveScreen { get; }
public static bool Register(string modId, string appId, string title, Sprite icon, Action<RectTransform> open);
public static void Close(string appId);
public static void RemoveAll(string modId);
public static bool OpenStandalone(string modId, string appId);
public static bool AddInteraction(GameObject target, string modId, string title, string message, Action onUse);
}
| Method | Returns | What it does |
|---|---|---|
Register(modId, appId, title, icon, open) | bool | Adds an app icon to every desk, the ones that exist now and the ones made later. |
Close(appId) | void | Hides the app on every desk, and closes its full-screen window if it shows that app. |
RemoveAll(modId) | void | Removes all of your mod’s apps, icons, windows and use prompts. |
OpenStandalone(modId, appId) | bool | Opens an app in a full-screen window over the game. |
AddInteraction(target, modId, title, message, onUse) | bool | Gives an object a use prompt. |
| Property | Type | What it means |
|---|---|---|
IsAvailable | bool | true when apps can be used. See Things to know. |
ActiveScreen | RectTransform | The screen of the computer the player is using now. null when none. |
| Event | Gives you | When it fires |
|---|---|---|
AppClosed | ComputerAppClosedArgs | An app view closes. |
ScreenChanged | RectTransform | The computer screen the player uses changes. It gives the new value. |
ComputerAppClosedArgs what AppClosed tells you
AppClosed hands you this when an app view closes. ModId and AppId tell you which mod and app it was.
public sealed class ComputerAppClosedArgs
{
public string ModId { get; }
public string AppId { get; }
}
| Property | Type | What it means |
|---|---|---|
ModId | string | The mod that registered the app. |
AppId | string | The id of the app. |
Putting it together
Adds a Notes app, and a prompt on an object called Whiteboard that opens it in a full-screen window.
using BepInEx;
using BepInEx.Unity.Mono;
using UnityEngine;
using ObenseuerCore;
public class NotesApp : BaseUnityPlugin
{
private void Awake()
{
ComputerApps.Register("example.notesapp", "notes", "Notes", null, host => BuildNotes(host));
}
private void Start()
{
GameObject board = GameObject.Find("Whiteboard");
if (board != null) ComputerApps.AddInteraction(board, "example.notesapp", "Whiteboard", "Read", () => ComputerApps.OpenStandalone("example.notesapp", "notes"));
}
private void BuildNotes(RectTransform host)
{
}
}
| Step | What it does |
|---|---|
ComputerApps.Register(...) | Adds the Notes icon to every desk. |
ComputerApps.AddInteraction(board, ...) | Gives the Whiteboard a “Read” prompt. |
ComputerApps.OpenStandalone(...) | Opens Notes in a full-screen window when the prompt is used. |
Apps on the desks
IsAvailableistruewhen the framework may change game methods and the feature group has not been turned off. The desk changes are made the first timeRegisteris called, not at start-up. The feature group isobenseuer.moddingframework.computerapps, the name you will see in the log. (Core.PatchingAllowedis true and the feature group has not been disabled.)Registerreturnsfalsewhen the desk changes cannot be made, or whenappIdis already used. Changes are refused whenCore.PatchingAllowedis false. The first registration of anappIdwins, and a warning is logged for the second. An emptyappId, anappIdthat contains/, or an emptytitlealso returnsfalseand logs once.Registerthrows for a badmodIdoropen. A null or emptymodIdthrowsArgumentException, and a nullopenthrowsArgumentNullException.titlemust not be empty.iconmay benull, and then the title is drawn on the tile.- The icons form a grid at the top right of each desk. Each icon is 84 x 72. There are 5 rows, columns are 96 pixels apart, rows are 84 pixels apart, and the margin is 12 pixels.
- An icon is created once for every desk and app, and reused after that.
- When the player clicks the icon,
open(host)is called.hostis aRectTransformthat fills the desk’s wallpaper, one for each desk and app, and it is an empty area you build your interface in. - The framework clears
hostbefore everyopencall: each child is renamed, deactivated and destroyed. You always build into an empty area, the same as withOpenStandalone. Nothing you leave in the area survives the next click, so keep your own state in your mod. - If
openthrows, the error is logged to your mod’s log and the area is hidden. - The area needs a click catcher above it, or its buttons receive no clicks. The desk logs one warning when none is found above the area. Each time an app is shown, the framework makes sure the scene has an event system, which the interface needs. (The host must sit under a
GraphicRaycaster. The event system is an EventSystem.) - Typing in an app does not make the player leave the computer. While an input field in an app has focus, the desk’s own key handling is skipped.
Closing and events
- Apps hide when the player leaves the computer.
Close(appId)hides the app on every desk, and closes the full-screen window if it shows that app.Closealways runs, even while a save loads or saves. Each close raisesAppClosed. - A desk view raises
AppClosedwhen it was showing and then one of these happens:Close(appId), the player leaving the computer (HideAll), a single scene load (not an additive one),RemoveAllfor its mod, or anopenthat throws. A view that was already hidden raises nothing. - The full-screen window raises
AppClosedwhen it closes: its Close button, Esc, a single scene load,RemoveAllfor its mod, orOpenStandaloneof another app. AppClosedfires once for each view that closes. ItsModIdis the mod that registered the app, andAppIdis the app’s id.- A handler cannot tell a desk view from the full-screen window. The arguments carry only
ModIdandAppId. If you need to tell them apart, track your own view. Handlers are kept apart from each other, and you unsubscribe with-=. ActiveScreenis the screen of the computer the player is using now, ornull. It is set when the player starts using a computer, and cleared when the use stops or a single scene load happens.ScreenChangedfires with the new value on each change, not on every frame. AWebViewcan follow it withPinTo(see Web UI). (ActiveScreenis the desk’s wallpaperRectTransform. It is set inInteractableComputer.Useand cleared inStopUse.)
The full-screen window
OpenStandaloneopens the app over the whole game. It draws on top of everything, has a Close button at the top right, locks the game’s controls and shows the cursor. It changes no saved state, so it does not check whether the game is saving. (A ScreenSpaceOverlay canvas at sort order 500, a CanvasScaler at 1920x1080 (match 0.5) and a GraphicRaycaster. The controls lock isControlsDisabled.)OpenStandalonereturnsfalseand logs a warning when the app is unknown, or when the game is not ready or is busy. Busy means a menu, shop, pause, dialogue or the console is open. It returnstruewhen that app is already open.- The window creates an event system if the scene has none. The last window to close destroys it, if the framework created it. (It is an EventSystem with a StandaloneInputModule.)
- The window closes on Esc, on its Close button, on a single scene load, and when the window throws. A scene added on top of the current one does not close it.
- A text field or key row gets Esc first. Esc does not close the window while an input field in it has focus or a key row is waiting for a key. That press belongs to the field or row, and the desk computer and the Pause Menu ignore it too. Otherwise Esc closes the window and does not open the Pause Menu on the same press. (The close runs after the game’s
Update, inLateUpdate, while the controls lock still holds, so the game’sUpdatereturns before its Pause Menu check.)
Use prompts
AddInteractiongives an object the game’s own use prompt. The prompt showstitleand(message)the way the game shows its own prompts, andonUseruns when the player uses it. An error inonUseis logged to your mod’s log.- The object needs a collider.
targetneeds aCollider, and the game’sInteractObjectsmust exist. - Calling it again from the same mod updates the title, message and
onUse. AddInteractionreturnsfalsewhile a save loads or saves, when changes are not allowed (Core.PatchingAllowedis false), when there is no collider or interact system, when no usable layer is found, or when another mod owns the object. It also returnsfalsewhen the object already has a game component that handles use, because the game would show its own prompt instead. A warning is logged once. (A “game component that handles use” is any MonoBehaviour that implementsIInteractable, except the framework’s own prompt.)- The object keeps its layer when that layer can be used. Otherwise it gets the first usable layer of 17, 23, 29, 20 and 15.
RemoveAllputs the original layer back. (A usable layer is one that is inInteractObjects.MaskList.) RemoveAll(modId)removes your mod’s apps and icons, closes its window, and removes its use prompts.
Still to be checked in the game
- Icon placement and draw order on a real desk, and that clicks reach the icons. The desk click-catcher warning should not appear there.
- Clicks in the full-screen window, both with an event system in the scene and without one (the framework creates it).
- Whether the game’s
MaskListincludes the chosen layers at runtime. The check is the same bit test that the raycast uses. - That Esc, which is also the Pause Menu key, is used by the window first.
- Movement and cursor state after the window closes while the game UI is open.
AddInteractionon an object that already has anotherIInteractablereturnsfalse.