Your first commands
Turn a static method into a command with one attribute.
Anatomy of a command
A command is a static method with the [Command] attribute. Its parameters are the
parameters of the command. That's all it takes:
using MI.Console;
using UnityEngine;
// Wallet stands for your game's own code
public static class CheatCommands
{
[Command("give-gold")]
[CommandSummary("Add gold to the player's purse")]
static void GiveGold([ParamInfo("How much gold")] int amount)
{
Wallet.Gold += amount;
Debug.Log($"Gold: {Wallet.Gold}");
}
}
> give-gold 500
Gold: 1500
- The method can be
public,internalorprivate. - Its class can be any class, static or not.
- What the method returns, if anything, is logged:
static int Gold() => Wallet.Gold;logsgold: 1500.
Where commands can live
| Declared on | Runs | Page |
|---|---|---|
| A static method or property | Once, as typed | This page, and Parameters |
| A method or property of a component | On the objects of the scene you name: @Enemy* heal 20 | Commands on objects |
An extension method of GameObject or of a component | On the objects of the scene you name | Commands on objects |
Use [SecuredCommand] instead of [Command] for a command that must ask for a password:
see Protected commands.
Names
Without a name, a command is named after its method in kebab-case: SetLanguage is
typed set-language, HTTPGet is http-get. Names are case-insensitive.
Give the name yourself: [Command("set-lang")]. Renaming the method then doesn't
rename the command, which your aliases, scripts and MirageConsole.Execute calls may rely on.
- A name can have letters, digits, underscores and hyphens (not first).
- Two commands with the same name are reported as a conflict, and only the first is registered.
How commands are found
The console finds the commands by reflection when the game starts. It only looks at the assemblies that reference it (yours, the samples), not Unity's or other packages', and logs what it found:
[Console] 18 commands registered: 4 of 234 assemblies scanned in 2 ms
- Your commands must be in an assembly loaded when the game starts. A command in an assembly you load later isn't found.
- If your code uses assembly definitions (
.asmdef), the assembly with your commands must referenceMirageInteractive.Console. - A command that can't be registered (a parameter of a type the console can't read, a name already taken...) is reported in Unity's Console, and the others still work.
[DoNotLookForCommand]on a class makes the console skip it.
To see them without starting the game, open Tools > Mirage Console > Check Commands. The window
finds the commands the same way the console does when the game starts, and lists them group by group, with their
syntax, summary, aliases and where they are declared. Above them it lists the problems: what the console would log
(a name already taken, a parameter of a type it can't read, an alias in conflict, a method that returns an
IEnumerator), an alias that can't run, the
commands without a [CommandSummary], and a group default that holds 20 commands or more.
It checks again after each compilation, and on Check. In the Editor it also sees the commands of
your Editor assemblies, which a build doesn't have.
Writing output
Write to the console with Unity's usual methods: Debug.Log, Debug.LogWarning,
Debug.LogError. What a command logs while it runs goes to the CLI (and to All Messages, like every
message of the game). Start your messages with a tag to sort them:
Debug.Log("[Economy] Gold: 1500").
What the method returns is logged too, after the name of the command: static int Gold() logs
gold: 1500. Don't also log the value you return, or it shows twice.
Coroutines and async methods
A command runs at once, on the main thread, and the console doesn't wait for it:
- A method that returns an
IEnumeratorisn't run as a coroutine: none of its body runs. The console warns about it when it registers the command, and so does Check Commands. Make the command returnvoidand start the coroutine itself, withStartCoroutineon a MonoBehaviour. - An
asyncmethod runs until its firstawait, then goes on by itself. What it logs after that still shows, but what it returns isn't logged, and an exception it throws after theawaitisn't reported as an error of the command.
Refusing a value
The console checks that each value converts to the type of its parameter. When your command needs more (a range,
a value that isn't empty...), throw an InvalidParameterValueException. The console reports it with the
syntax of the command, like a value it couldn't convert:
using System.Globalization; // CultureInfo
[Command("volume")]
static void SetVolume(float percent)
{
if (percent < 0f || percent > 100f)
throw new InvalidParameterValueException("volume", "percent",
percent.ToString(CultureInfo.InvariantCulture), "a percentage, from 0 to 100");
AudioListener.volume = percent / 100f;
}
> volume 150
[Command Error] '150' isn't a valid value for 'percent' (a percentage, from 0 to 100). Type 'help volume' for more information
Usage: volume <percent>
Any other exception your command throws is logged with its stack trace, and the console keeps working.
Going further
- Parameters and types: required and optional parameters, flags, shortcuts, and every type a parameter can have.
- Aliases, groups and docs: shorter forms, groups, and the help of your commands.
- Commands on objects: run a component's method on the objects of the scene.