Wiki

Clone wiki

NeGuen Tools / Documentation / 0.2

NG Tools

Table of Contents

  1. Usage

    1. NG Console
      1. Module
      2. Editor Openers
    2. NG Game Console
      1. NG Game Console
      2. NG CLI
      3. NG Server Command
    3. NG Remote Scene
      1. NG Remote Hierarchy
      2. NG Remote Inspector
    4. NG Draggable Object
    5. NG Fav
    6. NG Inspector Gadget
    7. NG Prefs
    8. NG Hierarchy Enhancer
    9. NG Nav Selection (Windows only)
    10. NG Settings
    11. Additional Tools
      1. Loggers
      2. ScriptableObject Creator
  2. Technical Usage

    1. NG Console
      1. Module
      2. StreamLog
        1. StartMode
        2. EndMode
      3. Row
      4. ILogFilter
      5. IStackFrameFilter
      6. ILogExporter
      7. Export/Import settings
      8. Editor Openers
      9. Preset
      10. Theme
    2. NG Game Console
      1. NG Game Console
        1. DataConsole
      2. NG CLI
        1. Root command
    3. NG Remote Scene
      1. TypeHandler
      2. TypeHandlerDrawer
      3. ComponentExposer
      4. ArgumentDrawer
    4. NG Fav
      1. ICustomFavorite
    5. NG Prefs
      1. PrefsManager
    6. NG Hierarchy Enhancer
      1. IHierarchyMenuItem
      2. DynamicObjectMenu
    7. NG Settings
      1. Custom settings
    8. NG Network
      1. TCP Listener

The package was tested with:

  • 5.3.1p1
  • 5.2.0f3
  • 5.1.2p3
  • 5.0.4f1
  • 4.6.1f1
  • 4.5.0f6

NG Tools is a gathering of many useful tools to improve your experience through Unity. It embed many user-friendly features that will change your way to do things, your efficiency will be undoubtful, once your get in there, you will never get back, be aware, you are going to be addicted!

1 Usage

1.1 NG Console

NG Console is like Unity's Console, but way more efficient. Based on modules, each module brings a feature that might be useful to you. Below is the explanation of each built-in modules.

Base features

All features from the native Console (NG Console is strongly linked to Unity Console, all primary features are sync'ed):

  • Intercept logs from Unity Console Any logs that enter Unity's Console are catched and displayed in NG Console as well.
  • Clear Clear the console.
  • Collapse Logs are stacked, reducing number of visibles logs.
  • Break on error Break the game when an error occured.
  • Clear on play Clear the console whenever the user starts to play.
  • Persistent logs Logs are saved during the session of Unity (Saved between serializer's passes).
  • All types log All types can be toggled (Normal, Warning, Error).
  • Auto-scroll Stick the scrollbar to the bottom when new logs come if the scrollbar was already stick to the bottom.

Additional features

  • Type log exception Error is split between Error and Exception. They are totally dissociated.
  • Interactive stack trace Display all frames from the stack trace. Each frame is clickable if the file is editable.
    • Colored source code preview When the mouse hovers a stack frame, if the file is reachable in your hard disk, the source code is displayed in colors.
    • Folder pingable Directly ping folder by clicking on it in the stack frame.
  • Advanced copy Copy the first line of the selected logs or its full content or the whole stack trace.
  • Log icon An icon representing the emitter is drawn just after the log's foldout if a context is provided. Clicking on it will ping the object.
  • Time Logs can display the time, but only when it was received by NG Console, therefore a little delay might be detected. (Thus, the time is not reliable!)
  • Multi selection You can select many logs.
  • Multi copy You can copy from all selected logs, it will append each logs in the buffer.
  • Multi deletion You can temporary delete selected logs, unfortunately it is not persistent between Unity serializer's passes.
  • Modular context menu Context menu on log is modular. Any module or filter can implement a new menu item.
  • Export logs Export all logs from a stream in your desired format. There are many options using the export wizard, giving all keys to export to your own format.
  • Export/Import settings You can export/import settings from/to all modules.

1.1.1 Module

A module implements any behaviour you might will. Easy to implement new features or to interact with others.

  • Main module Mimics the native Unity Console. It displays incoming logs through streams. It separates normal logs and compiler's in different stream.

    • Stream log You can create many streams to display logs. Each streams can be customized to display specific logs depending on filters.
      • Log
        • A log is the content you write to the console. But there are many ways to display a log. Beside the default log just containing a message and a context, NG Console provides extra functions to output logs in a different ways.
        • See: How to implement a Log (called as Row in the engine).
        • See: Additional loggers.
      • Filter
        • A filter accepts or rejects incoming logs regarding its condition.
        • ContentFilter accepts logs by matching a keyword in it.
        • HierarchyFilter accepts logs coming from a GameObject or its children.
        • MaskTypeFilter accepts logs matching a type (Normal, Warning, Error or Exception).
        • NameHierarchyFilter accepts logs by matching a keyword with log's Object's name.
        • TagFilter accepts logs coming from GameObject with a certain tag.
        • See: How to implement a filter.
  • Archive module Logs can be archived, each archived log can be assigned a note and put in folders.

    • Folder You can create many folders and name them. Drag & drop archived logs between folders to keep a neat archive place.
  • Recorder module Receives logs and sends them to samples.

    • Sample A sample simply starts and stops receiving logs depending on its settings. Nice for debugging specific logs in a given window.
    • See: How to implement a StartMode or EndMode for Sample.
  • Color Markers module Colors log's background regarding markers' filters and color.

  • Command Line Interface module (CLI) Implemented especially for NG CLI, allowing NG Console to be able to receive logs from a remote Unity build. Also, if the distant build has activate CLI, you will be able to remotely execute commands into the build.

See: How to implement a Module for NG Console.

1.1.2 Editor openers (Only for those who got problems with Unity Console's Go to line)

NG Console can natively open any of the following editors with the Go to line feature:

  • VisualStudio
  • MonoDevelop
  • Sublime 2
  • Notepad++
  • Vim
  • Emacs

If your editor is missing, you can technically implement it yourself, since NG Console uses the same technic as Unity, otherwise just ask for it, I will be pleased to give you assistance!

Some numbers

  • CPU Footprint Can easily handle 20k and certainly even more, way more!
  • Memory Footprint With all the features brought by NG Console, it only takes 1.5 times the memory of Unity's Console.

See: Look at NG Console's settings in NG Settings.

1.2 NG Game Console

1.2.1 NG Game Console

Runs a console window in-game displaying incoming logs and additionnal data. Data might be anything you want to display about your scene, your environment, your game.

There is 2 rendering modes, Logs and Data:

  • Logs rendering displays logs and has a header filled with short data version.
  • Data rendering displays a list of all data in their complet version.

How To Use: Drop the script NG Game Console on a GameObject. If you want to add data to the console, add any script inheriting from DataConsole on a GameObject and put it in NG Game Console's field "Data Console".

See: How to implement a DataConsole for NG Game Console.

1.2.2 NG CLI

NG CLI allows to type or execute commands in the game.

There is 2 ways to use it:

  • By linking it to a NG Game Console, NG CLI is anchored to it and provides an input beneath the console to type commands.
  • Through an instance of NG Server Command. NG Server Command allows you to execute commands via network from the module Remote in NG Console.

Commands are exposed through NG CLI's field "Root Commands". Fills this field with any Behaviour. Every properties (with both Get and Set) and methods with the attribute CommandAttribute will be exposed as sub-commands of the Behaviour.

How To Use: Drop the script NG CLI on a GameObject. To make it stick to a console, you need to assign the field "Game Console" with a script NG Game Console.

How To Use: To use it via network, drop the script NG Server Command on a GameObject and assign the field "Cli" with a script NG CLI.

See: How to implement a root command for NG CLI.

See: Refer to the class CommandAttribute for more informations.

1.2.3 NG Server Command

NG Server Command grants the possibility to send debug logs to module Remote in NG Console. If field Cli is assigned, module Remote is allowed to remotely type and execute commands.

How To Use: Drop the script NG Server Command on a GameObject, then drop any script inheriting from AbstractTcpListener, this script is required to told the server how to communicate with the outside. * DefaultTcpListener is the default built-in listener.

See: How to implement an AbstractTcpListener for a server.

See: Scene GameConsoleCLI to see an example of NG Game Console, NG CLI and NG Server Command.

1.3 NG Remote Scene

A powerful tool to modify the scene on your device, but from Unity Editor itself!

NG Remote Scene was built to work from an empty project on any device with NG Remote Scene activated. Meaning you do not need to have the scripts to have it working.

1.3.1 NG Remote Hierarchy

Equivalent of Hierarchy window in Unity Editor. This is the base window from where you connect to the device, change settings, change global behaviours of NG Remote Scene.

It supports base features of Hierarchy window:

  • List full hierarchy
  • Drag & Drop
  • Filtering
  • Delete GameObject

Options provided:

  • Network Refresh: An interval in second between each send of packets.
  • Sync Tags: Defines if you choose to use the tags in your current project or let an input text.

Note: You can technically have many NG Remote Hierarchy. But it is not tested.

1.3.2 NG Remote Inspector

Equivalent of Inspector window in Unity Editor. An NG Remote Inspector is associated to a NG Remote Hierarchy. From there, you can change any value of a GameObject and see the result on your device. It support materials and shaders editing as well.

With NG Remote Scene you can easily test many configurations on your build and apply them directly on your GameObject with the supported feature Copy Component.

Batch Mode: All requests toward the server are batched. Batches can be saved and reapplied at any time.

Historic: Displays packets sent to the server.

It supports almost all features of Inspector window:

  • Active: A checkbox to toggle GameObject's active state.
  • Name: An input to change GameObject's name.
  • IsStatic: A checkbox to toggle GameObject's static state.
  • Tag: A popup to change GameObject's tag. Look at NG Remote Hierarchy's options to change the behaviour.
  • Layer: A popup to change GameObject's layer.
  • List all components: A list of all components and their fields.
    • Almost all types are supported: Char, Byte, SByte, Int16, Int32, UInt16, UInt32, Single, Double, String, UnityEngine.Object, array, List<>, struct and class. Int64 and UInt64 are supported but only in Unity 5. Decimal is not supported, since Unity Editor does not support it at all.
  • Drag & Drop Object: Allows to drag & drop GameObject from NG Remote Hierarchy into Object field, and also between fields. Note that because the type might be unknown, you will be able to drag Object into any fields without harming the build.
  • Copy Component: Copies all values of a Component, like Unity's Inspector.
  • Materials: List all materials and their shader's properties.

1.4 NG Draggable Object

Replaces the default drawer for any Unity's Object. It grants you the ability to drag & drop in the Inspector the Object from a field to another field.

NG Draggable Object is smart, when you drag a GameObject into a field, it will display you all its Component matching the field's type instead of assigning the first like Unity does.

Furthermore, when you drag a Component into a field with an unmatching type, it applies the behaviour described just previously.

When there is more than one Component matching the field's type, a label "#N" is display at the left of the field, with N the position of the Component in the GameObject. Right-click on the field to switch to another Component from the same GameObject.

See: Look at NG Draggable Object's settings in NG Settings.

1.5 NG Fav

Saves your selections in NG Fav. Any GameObject or asset can be saved, even runtime GameObject!

Runtime GameObject defines an entity that you create after pressing Play. A hero, some bullets, an item, a path, a mountain, a card, etc...

When you stop playing, the runtime GameObject is destroyed, so is its reference in NG Fav. But there is a simple way to remember the reference, you must implement the interface ICustomFavorite in one the script of the GameObject.

Each selection can be focus by clicking on it or by pressing the shortcut (For the first 10 selections, Shift + F1 to F10).

Add new selection by right-clicking on any GameObject or any assets and click on "Add Selection". Else, drag & drop your selection into NG Fav to create a new one, or drop it on an existing selection to append to it.

NG Fav handles multi-list of favorites. Because it remains the same between projects. Use it to have a specific list of favorites between projects and scenes.

See: How to implement an ICustomFavorite for NG Fav.

See: Look at NG Fav's settings in NG Settings.

1.6 NG Inspector Gadget

A gathering of tiny tools, slightly improving your experience in Unity's Inspector.

1.6.1 Reorder Components

Adds the entry "Reorder Components" in context menu of Component in Inspector.

Click on it to open a little wizard allowing you to easily reorder Component in the GameObject.

1.6.2 Clone Component

Adds the entry "Clone Component" in context menu of Component in Inspector.

Click on it to clone the current Component and append the new one rightafter the current one.

1.7 NG Prefs (Windows only for the moment)

Changes any values from EditorPrefs or PlayerPrefs.

Add, change and delete any keys.

Display a list of all data, filter by keys, refresh the whole list or clear them all!

1.8 NG Hierarchy Enhancer

Whenever you hover over a GameObject with your mouse in Hierarchy window, an NG icon appears. Pass over it to deploy a menu.

The menu will call the method OnHierarchyGUI() from each script on the GameObject.

The menu dynamicly generates GUI regarding scripts on it. The purpose of that is to display, change or action anything you want from Hierarchy without selecting the GameObject and then alter the value through the Inspector. Like a shortcut.

See: Look at NG Hierarchy Enhancer's settings in NG Settings.

See: How to implement Hierarchy GUI for NG Hierarchy Enhancer.

1.9 NG Nav Selection (Windows Only)

Any selections you do from Hierarchy and Project windows are memorized.

Buttons "Previous" and "Next" on your mouse allow you to switch the current selection.

Notice: This is different from Undo and Redo. In fact, if you Undo in Unity, you will cancel the last thing you have done, if it was a selection, you will switch to your penultimate selection, for everything else, it will be canceled. NG Nav Selection only focus on the selection.

See: Look at NG Nav Selection's settings in NG Settings.

1.10 NG Settings

All settings from all tools in NG Tools are gathered in NG Settings which saves almost everything in an asset in the project. It is the equivalent of Unity's Preferences, but bigger and modular.

This way, the settings file is automatically exportable. Also, because it lives in an asset, you can easily create many settings files and switch between them seamlessly.

  • NG Console

    • Fine-grained settings Almost all settings in NG Console can be changed.

    • Fine-grained editors' extensions You can choose which executable must open which extension.

    • Themes Ovewrites visual settings in your settings.

    • Presets Overwrites behaviour settings in your settings.

See: How to implement your own settings in NG Settings.

1.11 Additional Tools

Here will be described independent tools not relying on any of the previous tools above.

1.11.1 Loggers

NG Console provides the debug class NGDebug containing many useful loggers.

NGDebug.LogHierarchy(GameObject gameObject)
NGDebug.LogHierarchy(Component component)
NGDebug.LogHierarchy(RaycastHit hit)
NGDebug.LogHierarchy(Transform transform)
Writes the hierarchy of the given argument.

NGDebug.Log(params Object[] objects)
NGDebug.Log(params RaycastHit[] hits)
NGDebug.LogCollection(IEnumerable<RaycastHit> hits)
NGDebug.LogCollection<T>(IEnumerable<T> objects)
Writes a log containing a pingable button for each reachable object of the given list. Use it with GetComponents() to instantly have a visible list of all components in NG Console.

NGDebug.MTLog(string message)
NGDebug.MTLog(string message, Object context)
Writes a log containing the stack trace. Useful when writing logs in multi-threads context.

NGDebug.Snapshot(object o)
NGDebug.Snapshot(object o, Object context)
NGDebug.Snapshot(object o, BindingFlags bindingFlags)
NGDebug.Snapshot(object o, BindingFlags bindingFlags, Object context)
Output all fields matching the BindingFlags from the given object to the console.

1.11.2 ScriptableObject Creator

If you give attention to details, you might have noticed the new entry "Scriptable Object" in the menu Assets/Create.

Often people ask for a tool to easily create ScriptableObject asset, and there is none embedded in Unity.

So! Here it comes! A friendly and easy to use wizard to generate your assets!

2 Technical Usage

2.1 NG Console

2.1.1 Module

Class Module is the backbone of NG Console, allowing to create a visible module (Selectable through the header menu) or an invisible module able to do a background task.

  • MainModule is the main module, can not be more obvious. It displays logs as the native console with usefull features aside.
  • RecorderModule is visible and allows to create samples and records logs in them.
  • ArchiveModule is visible and interfers with other modules. It allows to save logs and assigns them a note.
  • ColorMarkersModule is an invisible module, but it manually adds a button in the header menu. It colors background of log regarding markers' settings.

How to implement a Module:

  • Create a class inheriting from Module.
  • Add attribute Serializable.
  • [Optional] Add attribute VisibleModule if you want your module to be exposed and visible.

An example of Module:

using NGToolsEditor;
using System;
using UnityEngine;

[Serializable]
[VisibleModule(70)]
public class TestModule : Module
{
    public  TestModule()
    {
        this.name = "test";
    }

    public override void    OnGUI(Rect r)
    {
        GUI.Button(r, "Button");
    }
}
Note: NG Console contains 5 (+1) built-ins Module: MainModule, RecorderModule, ArchiveModule, ColorMarkersModule and CLIModule (and DebugModule).

See: Refer to the class Module for more informations.

2.1.2 StreamLog

StreamLog simply displays logs. By default, it can filter by Unity's log types (Normal, Warning, Error and Exception) and can be deeply modified by your own filters which can change the stream of displaying logs.

The class can be overriden to display your own stream.

MainStream is used by MainModule. The only feature it provides is the Collapse option synchronized with the native console. This stream can not be deleted.

CompilerStream is also used by MainModule. It appears when compiler outputs warnings or errors and disappears when there is none. This stream can not be deleted.

SampleStream is used by RecorderModule. A sample records incoming logs in a finite frame of conditions, defined by a start and an end modes.

2.1.2.1 StartMode

StartMode is the base class used by SampleStream to start a sample.

Note: NG Console contains 2 built-ins StartMode: TimeStartMode and RegexStartMode.

See: Refer to the class StartMode for more informations.

2.1.2.1 EndMode

EndMode is the base class used by SampleStream to end a sample.

Note: NG Console contains 4 built-ins EndMode: TimeEndMode, RegexEndMode, MaxLogEndMode and NeverEndMode.

See: Refer to the class EndMode for more informations.

Note: NG Console contains 4 built-ins StreamLog: StreamLog, MainStream, CompilerStream and SampleStream.

See: Refer to the class StreamLog for more informations.

2.1.3 Row

NG Console invokes its 2 events CheckNewLogConsume then PropagateNewLog for each new log.

The first event does not handle the incoming log but check if it is consumed or if it must be consumed. A StreamLog can chose to discard logs that are consumed, depending on its purpose.

For example, MainStream and CompilerStream do not care of about consumption, they accept all logs. Nevertheless, StreamLog does. When a log is consumed, all next StreamLog in the list will reject it. That way, you can have a StreamLog dedicated to memory, AI, debugs, warning or initializations, etc...

The second event effectively handles the log regarding what happened just before in the previous event.

How to implement a Row:

  • Create a class inheriting from Row.
  • Add attribute Serializable.
  • [Optional] Add attribute RowLogHandler if you want your implementation be called when a new log arrives. (It is possible you do not want it to be called. Like RemoteRow, a special Row implemented and used only by RemoteModule to handle asynchronous Row from network.)
    • If RowLogHandler is present, you must implement the following method:
          private static bool CanDealWithIt(UnityLogEntry log)
          {
              return true; // Here is the condition of your Row, checking if it should handle the log.
          }
      

An example of Row:

using NGToolsEditor;
using System;
using UnityEditor;
using UnityEngine;

[Serializable]
[RowLogHandler(1000)]
public class TestRow : Row
{
    private static bool CanDealWithIt(UnityLogEntry log)
    {
        return log.condition.Contains("test");
    }

    public override float   GetWidth()
    {
        return 0F;
    }

    public override float GetHeight()
    {
        return EditorGUIUtility.singleLineHeight;
    }

    public override void    DrawRow(RowsDrawer rowsDrawer, Rect r, int i, bool? collapse)
    {
        float   originWidth = this.editor.position.width - rowsDrawer.verticalScrollbarWidth + rowsDrawer.scrollPosition.x;

        r.x = rowsDrawer.scrollPosition.x;
        r.width = originWidth;
        r.height = this.editor.settings.log.height;

        // Draw background when focus, even or odd.
        this.DrawBackground(rowsDrawer, r, i);

        // Handle default log focus.
        this.HandleDefaultSelection(rowsDrawer, r, i);

        GUI.Label(r, "[Test]", this.editor.settings.log.style);
    }
}
To test it, just write a log containing the word "test" or from a file with the word "test" in its path. A simple text "[Test]" should appear.

Note: NG Console contains 6 built-ins Row: DefaultRow, CompileRow, MultiContextRow, DataRow, RemoteRow and BlankRow.

See: Refer to the class Row and UnityLogEntry for more informations.

2.1.4 ILogFilter

ILogFilter checks whether or not a log matches a condition. It is most often used through GroupFilters, a class providing an easy way to handle multiple filters. It is used by StreamLog to filter incoming logs and ColorMarkersModule to colorize log's background.

How to implement a filter:

  • Create a class implementing the interface ILogFilter.
  • Add attribute Serializable.
  • Define your condition in method CanDisplay().
  • If you want your filter to be dynamic, you can implement GUI in OnGUI().
  • Method ContextMenu() is used by some Row implementing a context menu. It allows your filter to interact with logs.
  • Create a file named after the filter's class name in the folder Locale/english. Add it the pair "XXXX=YYYY" with XXXX the exact string of the filter's class name and YYYY a human readable version in english.

An example of a filter:

using NGToolsEditor;
using System;
using UnityEditor;
using UnityEngine;

[Serializable]
public class TestFilter : ILogFilter
{
    public bool Enabled { get; set; }

    public event Action ToggleEnable;

    private string  keyword;

    public FilterResult CanDisplay(Row row)
    {
        if (row.log.condition.Contains(this.keyword) == true)
            return FilterResult.Accepted;
        return FilterResult.None;
    }

    public void OnGUI()
    {
        this.keyword = EditorGUILayout.TextField("Filter test keyword: ", this.keyword);
    }

    public void ContextMenu(UnityEditor.GenericMenu menu, Row row, int position)
    {
        menu.AddItem(new GUIContent("Menu Test"), false, () => Debug.Log("Hello there!"));
    }
}
Note: NG Console contains 5 built-ins ILogFilter: ContentFilter, HierarchyFilter, MaskTypeFilter, NameHierarchyFilter and TagFilter.

See: Refer to the interface ILogFilter and class GroupFilters for more informations.

2.1.5 IStackFrameFilter

IStackFrameFilter allows to remove a frame from the stack trace based on the raw frame given by Unity's log.

This interface gives an easy way to create your own Debug.Log().

An example of a IStackFrameFilter:

using NGToolsEditor;

public class UselessFrameFilter : IStackFrameFilter
{
    public bool Filter(string frame)
    {
        return frame.StartsWith("YourDebugClass:") == true ||
                frame.StartsWith("AnotherDebugClass:WithSpecificMethod") == true;
    }
}
To test, change the strings by your own debug class. Anytime you will open a stack trace, your class will not be in there.

Note: NG Console contains 1 built-in IStackFrameFilter: UselessFrameFilter.

See: Refer to the interface IStackFrameFilter for more informations.

2.1.6 ILogExporter

ILogExporter allows to export logs in your own format. Only Module from NG Console must have implemented it.

How to implement a log exporter:

  • Create a class implementing the interface ILogExporter.
  • I strongly recommend you to look at existing implementation of ILogExporter. It might be rough, but the implementations cover good cases.
  • Please accept my apologies about this short todo.

Note: NG Console contains 2 built-ins ILogExporter: RawExporter and XMLExporter.

See: Refer to the interface ILogExporter for more informations.

2.1.7 Export/Import settings

Exports any portion of your settings through a fine-grained tree. The same way, import any settings you have exported.

To expose your settings to the export wizard, NG Console provides 2 attributes:

  • ExportableAttribute exposes a field to the export wizard or sets the behaviour of a class when exported.

An example of ExportableAttribute:

using NGToolsEditor;

public class ExampleModule : Module
{
    [Exportable]
    public int      aInteger;
    [Exportable]
    public string   aString;
}

  • HideFromExportAttribute prevents a class to be exported when the class is in an exportable field array.

An example of HideFromExportAttribute:

using NGToolsEditor;

public class ExampleModule : Module
{
    public class ClassA
    {
    }

    // This class will be excluded from the list when exported.
    [HideFromExport]
    public class SpecialA : ClassA
    {
    }

    // An exportable field array.
    [Exportable]
    public ClassA[] array;
}

2.1.8 Editor Openers

An opener is just an alternative way to open your asset. It gives the possibility to associate a set of extensions with a software.

It falls back on Unity if the asset is not handle by an opener from NG Tools.

To modify those, you must change NG Settings -> General -> General -> Editor Extensions.

How to implement an opener:

  • Create a class implementing IEditorOpener.
  • The method CanHandleEditor() checks if the given editor's path is handled. Notice that you only have the path of the software, this is the only data provided to the opener.
  • In addition of the method CanHandleEditor(), the property "defaultArguments" is used when CanHandleEditor() returns true. CanHandleEditor() is called when you choose an executable in Editor Extensions in NG Settings as explained just above.
  • Look at the implementation of NotepadPlusPlusOpener to see a very simple opener.

Note: NG Console contains 6 built-ins IEditorOpener: MonoDevelopOpener, VisualStudioOpener, NotepadPlusPlusOpener, SublimeOpener, VimOpener and EmacsOpener.

See: Refer to the interface IEditorOpener for more informations.

2.1.9 Preset

Preset grants you the possibility to change in an instant all settings about NGConsole's behaviour.

All implementations of Preset are displayed in section "Presets" in NG Settings.

Note: NG Console contains 3 built-ins Preset: FastestPreset, MinimalPreset and VerbosePreset.

See: Refer to the class Preset for more informations.

2.1.10 Theme

Theme allows you to create your own predefined custom style.

Allowing to alter the header menu, the visual of a log and its stack trace, the display of the source code and many other tiny details.

All implementations of Theme are displayed in section "Themes" in NG Settings.

Note: NG Console contains many built-ins Theme: DarkTheme, LightTheme, and alll FontSizeXXTheme.

See: Refer to the class Theme for more informations.

2.2 NG Game Console

2.2.1 NG Game Console

2.2.1.1 DataConsole

Displays a data on the console, anything you want from the scene, GameObject, gameplay, rules, statistics, etc...

How to implement a DataConsole:

  • Create a class inheriting from DataConsole.
  • Override methods CanDrawShortGUI() and ShortGUI() if you want a short version of your data.
  • Override methods CanDrawFullGUI() and FullGUI() if you want a complet version of your data.
  • Drop your script on a GameObject.
  • Add the instance of your DataConsole in field "Data Console" of NGGameConsole in Inspector window.

An example of a DataConsole:

using NGTools;
using UnityEngine;

public class TestData : DataConsole
{
    public override bool    CanDrawShortGUI()
    {
        return true;
    }

    public override void    ShortGUI()
    {
        GUILayout.Label("Short Test");
    }

    public override bool    CanDrawFullGUI()
    {
        return true;
    }

    public override void    FullGUI()
    {
        GUILayout.Label("Full Test");
    }
}

2.2.2 NG CLI

2.2.2.1 Root command

A root command is simply a command with sub-commands. NG CLI requires root commands to execute anything.

A command triggers an action, change a variable, call a function or anything else.

How to implement root command:

  • Create script inheriting from MonoBehaviour.
  • Create properties or methods with the attribute CommandAttribute.
  • Drop your script on a GameObject.
  • Add the instance of your script in field "Root Commands" of NGCLI in Inspector window.

Note: The attribute CommandAttribute can be applied on public properties, static or not, with a getter and a setter.

Note: The attribute CommandAttribute can be applied on public methods, static or not, with a "string" return type. CommandAttribute only supports primary types, decimal and string as arguments.

An example of a root command:

using NGTools;
using UnityEngine;

public class TestRootCommand : MonoBehaviour
{
    [Command("instanceInteger", "")]
    public int a { get; set; }
    [Command("instanceString", "")]
    public string b { get; set; }
    [Command("instanceFloat", "")]
    public float c { get; set; }
    [Command("instanceBoolean", "")]
    public bool d { get; set; }
    [Command("staticDecimal", "")]
    public static decimal e { get; set; }
    [Command("staticInt", "")]
    public static int f { get; set; }
    [Command("staticString", "")]
    public static string g { get; set; }
    [Command("staticFunction", "")]
    public static string Fn1()
    {
        return "A lambda result.";
    }
    [Command("staticFunctionWithArgument", "")]
    public static string Fn2(int b)
    {
        return "Fn2(" + b + ")";
    }
}
You can use commands to interact with your game, to change your configuration, stop the timer, save the state of anything, change scene, delete things, start a song, dance salsa, eat tacos and more and more!

2.3 NG Remote Scene

2.3.1 TypeHandler

The class TypeHandler gives the possibility to implement your own way to serialize and deserialize a given type, choosing which fields or properties you want to transmit via network.

Basically, a custom class/struct gets all its public fields automatically serialized/deserialized; but if you want it to be more efficient or just want to discard fields from the serialization pass, you might need to implement a TypeHandler.

Any implementation of TypeHandler needs to be assigned the attribute PriorityAttribute to define an order between TypeHandler. Additionally, each TypeHandler must have its implementaton of TypeHandlerDrawer.

Here is the implementation of the built-in BooleanHandler:

using System;

namespace NGTools
{
    [Priority(0)]
    public class BooleanHandler : TypeHandler
    {
        public override bool    CanHandle(Type type)
        {
            return type == typeof(Boolean);
        }

        public override void    Serialize(ByteBuffer buffer, Type fieldType, object instance)
        {
            buffer.Append((Boolean)instance);
        }

        public override object      Deserialize(ByteBuffer buffer, Type fieldType)
        {
            return buffer.ReadBoolean();
        }
    }
}

Note: NG Remote Scene contains built-ins for all primary types from .Net (Boolean, Byte, SByte, Char, Int16, Int32, Int64, UInt16, UInt32, UInt64, Single, Double), plus complex types (Array, enum, class, struct, string).

Note: NG Remote Scene also contains built-ins for some types from UnityEngine (Object, Rect, Quaternion, Vector2, Vector3, Vector4, Color).

See: Refer to the class TypeHandlerDrawer and PriorityAttribute for more informations.

2.3.2 TypeHandlerDrawer

When you implement a TypeHandler, you need to implement a TypeHandlerDrawer.

The drawer is the equivalent of PropertyDrawer from Unity. It draws the type in NG Inspector, but it has to take care of things.

TypeHandlerDrawer is a bit more complex, because NG Remote Scene has to manage network, meaning the value you are watching might be obsolete, and any change you do is asynchronous.

Therefore, the drawer has to display feedbacks and informations about the value for the sake of the user experience. But lucky you, it is already done.

Also, the class TypeHandlerDrawer needs the attribute TypeHandlerDrawerForAttribute to be linked to a TypeHandler.

Here is the implementation of the built-in BooleanDrawer with comments (Don't be afraid! It is not that big, just the comments make it feel bigger):

using NGTools;
using System;
using UnityEditor;
using UnityEngine;

namespace NGToolsEditor
{
    // Which TypeHandler it is related.
    [TypeHandlerDrawerFor(typeof(BooleanHandler))]
    public class BooleanDrawer : TypeHandlerDrawer
    {
        // An animator displaying a smooth animation when a new value is fetched from the server.
        private BgColorContentAnimator  anim;
        // Because the update is asynchronous, it displays the last value assigned.
        // Moreover, it handles dragging the field, preventing the value to shake when the server updates while you are dragging (For integer and float).
        private ValueMemorizer<Boolean> drag;

        public  BooleanDrawer(TypeHandler typeHandler) : base(typeHandler)
        {
            this.drag = new ValueMemorizer<Boolean>();
        }

        public override void    Draw(Rect r, DataDrawer data)
        {
            if (this.anim == null)
                this.anim = new BgColorContentAnimator(data.inspector.Repaint, 1F, 0F);

            // A notification is generated when the server sends the new value.
            if (data.inspector.hierarchy.GetUpdateNotification(data.GetPath()) == true)
            {
                this.drag.NewValue((Boolean)data.value);
                this.anim.Start();
            }

            using (this.anim.Restorer(0F, .8F + this.anim.Value, 0F, 1F))
            {
                EditorGUI.BeginChangeCheck();
                // Draws the field with the current working value (Remember the drag story!).
                Boolean newValue = EditorGUI.Toggle(r, ObjectNames.NicifyVariableName(data.name), this.drag.Get((Boolean)data.value));
                if (EditorGUI.EndChangeCheck() == true)
                {
                    this.drag.Set(newValue);
                    this.AsyncUpdateCommand(data.unityData, data.GetPath(), newValue, typeof(Boolean));
                }

                // Display last value if a new request is pending.
                this.drag.Draw(r);
            }
        }
    }
}

See: Refer to the class TypeHandler and TypeHandlerDrawerFor for more informations.

2.3.3 ComponentExposer

Sometimes, it happens that you specifically needs to discard a field or a property from being serialized or even reached.

An easy example would be the class MeshRenderer. Whenever the TypeHandler handling classes serializes MeshRenderer, it would extract all fields and properties including the property "material", which would leads to an unwanted instance of the material (Because when you make a call to this property, Unity automatically creates a copy of the Object). It would be the same for the class MeshFilter and its property "mesh".

To avoid this unfortunate issue, ComponentExposer provides 2 methods you might override to explicitly expose fields and properties.

Also, your class must have the attribute ComponentExposingForAttribute to define which type it is related to.

Here is the implementation of the built-in MeshRendererExposer:

using System;
using System.Reflection;
using UnityEngine;

namespace NGTools
{
    [ComponentExposingFor(typeof(MeshRenderer))]
    public class MeshRendererExposer : ComponentExposer
    {
        public override PropertyInfo[] GetPropertyInfos(Component component)
        {
            PropertyInfo[]  fields = new PropertyInfo[7];

            Type    type = component.GetType();

            fields[0] = type.GetProperty("enabled");
            fields[1] = type.GetProperty("shadowCastingMode");
            fields[2] = type.GetProperty("receiveShadows");
            // The field "material" is explicitly not included!
            fields[3] = type.GetProperty("sharedMaterials");
            fields[4] = type.GetProperty("useLightProbes");
            fields[5] = type.GetProperty("reflectionProbeUsage");
            fields[6] = type.GetProperty("probeAnchor");

            return fields;
        }
    }
}
Note: NG Remote Scene contains some built-ins ComponentExposer: MeshFilterExposer, MeshRendererExposer, TrailRendererExposer, TransformExposer.

See: Refer to the class ComponentExposingForAttribute for more informations.

2.3.4 ArgumentDrawer

NG Inspector allows to invoke method of any Component directly through the inspector.

But you might have complex arguments in your methods, hence ArgumentDrawer.

ArgumentDrawer draws editor GUI for a specific type, like PropertyDrawer.

Your implementation of ArgumentDrawer must have the attribute ArgumentDrawerForAttribute to be linked to a type.

Here is the implementation of the built-in BooleanArgumentDrawer:

using System;
using UnityEditor;

namespace NGToolsEditor
{
    [ArgumentDrawerFor(typeof(Boolean))]
    public class BooleanArgumentDrawer : ArgumentDrawer
    {
        public BooleanArgumentDrawer(string name) : base(name, typeof(Boolean))
        {
        }

        public override void    OnGUI()
        {
            this.value = EditorGUILayout.Toggle(this.name, (Boolean)this.value);
        }
    }
}
Note: NG Remote Scene contains built-ins for all primary types from .Net (Boolean, Byte, SByte, Char, Int16, Int32, Int64, UInt16, UInt32, UInt64, Single, Double), plus only one complex type (String).

Note: NG Remote Scene also contains built-ins for some types from UnityEngine (Rect, Quaternion, Vector2, Vector3, Vector4, Color).

See: Refer to the class ArgumentDrawerForAttribute for more informations.

2.4 NG Fav

2.4.1 ICustomFavorite

NG Fav saves asset based on the path of it. It works for all cases except for runtime GameObject.

For that case NG Fav provides the interface ICustomFavorite to make the ephemeral reference a reliable reference.

How to implement ICustomFavorite:

  • Implement the interface ICustomFavorite on a class.
  • Inside the method GetFavorite(), you need to set the arguments identifier and resolver. resolver must be assigned with a static function!
  • Argument identifier is optional, but not resolver. Because the callback resolver can return a specific GameObject, therefore is does not require an identifier.
  • Drop the class in the GameObject you want to put in favorite.

Callback resolver returns a GameObject based on the number you gave through identifier.

Recalling runtime GameObject is truly useful when you have thousands of GameObject in Hierarchy window and you do not want to scroll over the whole list or use filter to look for your GameObject, and redoing that each time you restart your game.

2.5 NG Prefs

2.5.1 PrefsManager

Class PrefsManager allows to save, edit and delete data. Use it to implement your own system of preferences.

This class is nearly too simple and not enough advanced, it only supports int, string and float.

Note: NG Prefs contains 2 built-ins PrefsManager: PlayerPrefsManager and EditorPrefsManager.

See: Refer to the class PrefsManager for more informations.

2.6 NG Hierarchy Enhancer

2.6.1 Hierarchy GUI

Draws GUI about your script directly in Hierarchy window.

An example:

private float OnHierarchyGUI(Rect r)
{
    r.xMin = r.xMax - 100F;
    if (GUI.Button(r, "What") == true)
    {
        Debug.Log("Else?");
    }
    return r.xMin;
}

Note: NG Hierarchy Enhancer draws thing from right to left, therefore the return value must be the leftmost position in X axis you consumed.

2.6.2 DynamicObjectMenu

Because you can not implement the method on Unity's scripts. NG Hierarchy Enhancer provides DynamicObjectMenu to bypass this issue.

Each time you open the menu, all classes inheriting from DynamicObjectMenu are called and given the GameObject as argument. From there, it is up to you to make your shopping.

Note: NG Hierarchy Enhancer contains 1 built-in DynamicObjectMenu: GameObjectMenu (Allows you to toggle Active state, to manage AudioSource, Renderer and ParticleSystem).

See: Refer to the class DynamicObjectMenu for more informations.

2.7 NG Settings

Settings are saved in a ScriptableObject in an asset.

Modules can save their settings in this asset which is shareable.

  • NG Console An example is done by NG Console which implements many sections in NG Settings.

    • Themes Implement your own theme in addition of the default Light and Dark themes based on Unity Editor.

    • Presets Like theme, you can implement your own predefined settings in addition of the default Fastest, Minimal and Verbose settings.

2.7.1 Custom settings

How to implement and integrate your own settings:

  • First way:
    • Implement a class inheriting from AbstractModuleSettings with any settings you want.
    • In your module, add a field of type AutoExposeSettings<T> with T the class you just created.
    • In OnEnable() of your module, assign your field with an instance of AutoExposeSettings<T>.
    • In OnDisable(), add a call to Close() from your instance of AutoExposeSettings<T>.
    • In case you need to initialize GUI, you must override the method InitializeGUI() from AbstractModuleSettings. Of course this is optional, if you have your own way to initialize GUI.

Here is the implementation of the advanced way: First, create the setting class:

using NGToolsEditor;
using UnityEngine;

public class DummySettings : AbstractModuleSettings
{
    public float    floatSetting = 70F;
    public string   stringSetting = "Test";
    public GUIStyle style;

    protected override void Reset()
    {
        base.Reset();

        this.floatSettingfloatSetting = 70F;
        this.stringSetting = "Test";
        this.style = null;
    }

    protected override void InitializeGUI()
    {
        if (this.style == null)
            this.style = new GUIStyle();
    }
}

Integrate your setting class through an instance of AutoExposeSettings:

using NGToolsEditor;
using System;
using UnityEditor;

[Serializable]
[VisibleModule(50)]
public class DummyModule : Module, IStreams
{
    [NonSerialized]
    private AutoExposeSettings<RemoteSettings>  settings;

    public override void    OnEnable(NGConsole editor, int id)
    {
        base.OnEnable(editor, id);

        this.settings = new AutoExposeSettings<DummySettings>("Dummy Module", Utility.GetConsolePath() + "/NGConsole/Modules/Dummy/DummySettings.asset");
    }

    public override void    OnDisable()
    {
        base.OnDisable();

        this.settings.Uninit();
    }
}

  • Second way:
    • Implement a serializable class with your settings.
    • Add a public field with the class you just created in the partial class NGSettings.
    • In your Module or your EditorWindow, add a field of type SectionDrawer.
    • In the method OnEnable(), instantiate the field. It requires 2 arguments, the first is the name of your section and second is the type of your serializable class.
    • In the method OnDisable(), call the method Uninit() from your field of type SectionDrawer.
using System;
using UnityEngine;

namespace NGToolsEditor
{
    public partial class NGSettings : ScriptableObject
    {
        [Serializable]
        public class TestSettings : Settings
        {
            public float    floatSetting = 70F;
            public string   stringSetting = "Test";
            public GUIStyle style;

            protected override void InitGUI()
            {
                this.style = new GUIStyle(GUI.skin.button);
            }
        }
        public TestSettings test = new TestSettings();
    }
}
using NGToolsEditor;
using System;
using UnityEngine;

[Serializable]
[VisibleModule(50)]
public class DummyModule : Module, IStreams
{
    [NonSerialized]
    private SectionDrawer sectionDrawer;

    public override void    OnEnable(NGConsole editor, int id)
    {
        base.OnEnable(editor, id);

        this.sectionDrawer = new SectionDrawer("Test Section", typeof(NGSettings.TestSettings));
    }

    public override void    OnDisable()
    {
        base.OnDisable();

        this.editorDrawer.Uninit(); 
    }
}

The first way is a bit complex, but it automatically handles the drawing for you. Use it when you want to create your own settings and put them in a different asset than the one used by NG Tools.

The second way is also a bit complex, the main difference with the previous method is your settings are shared in the same asset as NG Tools.

See: Implementation in NGConsole, MainModule, RemoteModule.

2.8 NG Network

2.8.1 TCP Listener

Network is handled through a TCP connection, NG Tools provides the class AbstractTcpListener to implement the behaviour. It only cares about accepting new clients.

The content is managed by the class Client which just receives and parses packets.

Every server in NG Tools requires a TCP connector, it is done by assigning an instance of an AbstractTcpListener in the listener field of the server (Notice that they are both inheriting from MonoBehaviour).

Here is the implementation of DefaultTcpListener, the only built-in in NG Tools:

using System;
using System.Net;
using System.Net.Sockets;

namespace NGTools
{
    public class DefaultTcpListener : AbstractTcpListener
    {
        public override void    StartServer()
        {
            this.tcpListener = new TcpListener(IPAddress.Any, port);
            this.tcpListener.Start(this.backLog);
            this.tcpListener.BeginAcceptTcpClient(new AsyncCallback(this.AcceptClient), null);
        }

        private void    AcceptClient(IAsyncResult ar)
        {
            // TcpListener is null when the server stops.
            if (this.tcpListener == null)
                return;

            Client  client = new Client(this.tcpListener.EndAcceptTcpClient(ar));
            this.clients.Add(client);

            this.tcpListener.BeginAcceptTcpClient(new AsyncCallback(this.AcceptClient), null);
        }
    }
}
As you can see, it only creates a TcpListener from the .Net framework, then wait for clients.

In the case a TcpListener does not fit your needs, you may need to implement your own version of a TCP listener.

Note: NG Tools contains a built-in DefaultTcpListener.

See: Refer to the class BaseServer, Packet and Client for more informations.

Updated