Developing our Mod Menu - KimonoBoy/SHVDNTutorial-NucleiLite GitHub Wiki

We have generated some scripts using a Rich Text Editor. While these scripts are operational, they are not ideal since they become increasingly challenging to use as we create more of them. This is where LemonUI can be beneficial.

Introduction to LemonUI

Let me introduce you to LemonUI, which is a framework or library used to design various UI elements. These components consist of menus and their corresponding items. LemonUI was created by Lemon and is based on Guad's NativeMenu. It simplifies the process of designing menus and gives them a modern appearance as it provides the same style that the GTA V uses for menus.

Below is an example of the Menu we'll be creating together

Main Menu

image

Player Menu

image

Vehicle Spawner Menu

image

Weapons Menu

image

In the full Nuclei - Mod we'll elaborate on all the concepts learned so far and create a complete Mod Menu - each script and thought-process will be covered. You can see the full mod at Nuclei and its Nuclei - Wiki

Creating our Menu using LemonUI

Let's create a menu that does absolutely nothing, except for Toggling visible and not visible

Creating a new Class

  1. In your Solution Explorer - Delete the Class1.cs file by Right-Clicking the file -> Delete or Select the file and hit Del-key

image

  1. In your Solution Explorer - Right-Click your Project (in my case NucleiLite) and select Add -> New Item...

image

  1. In the Window that appears - Select the Class-item, give it a name (in my case Main.cs) and click Add

image

Note: You can name it whatever you like, I like to name my entry points of an application Main, hinting that this is the root

  1. You should now see the following

image

The Code

  1. Copy and Paste the below code to your newly created file
using System;
using System.Windows.Forms;
using GTA;
using GTA.UI;
using LemonUI;
using LemonUI.Menus;

namespace NucleiLite
{
    public class Main : Script
    {
        ObjectPool menuPool = new ObjectPool();
        NativeMenu mainMenu = new NativeMenu("NucleiLite", "Main Menu");

        public Main()
        {
            menuPool.Add(mainMenu);

            KeyDown += OnKeyDown;
            Tick += OnTick;
        }

        private void OnTick(object sender, EventArgs e)
        {
            menuPool.Process();
        }

        private void OnKeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.F5)
            {
                mainMenu.Visible = !mainMenu.Visible;
            }
        }
    }
}
  1. Build your script (CTRL + SHIFT + B)

Note: If you haven't set up a Post-Build Event or followed the First .DLL Script on how to Build and Locate your script please do so.

Displaying the Menu

  1. Reload() in the Console (or hit Reload-Key)

  2. Hit the F5 key and you should see the following menu appear

image

Code Breakdown - Simplified

Just like we did our single scripts from previously, let's break this down

using LemonUI;
using LemonUI.Menus;

We've added two new using-statements, these are a part of the new library we've implemented (LemonUI). This Library is used for creating UI Elements in our case Menus


namespace NucleiLite

A namespace can be considered a way to group files together - LemonUI is a namespace, LemonUI.Menus is another namespace, so if we were to use our NucleiLite and its functionality in another project, we would reference the namespace


ObjectPool menuPool = new ObjectPool();

An ObjectPool is used to store and manage a "collection" of objects in our case a "collection" of Menus


NativeMenu mainMenu = new NativeMenu("NucleiLite", "Main Menu");

The LemonUI and its associated libraries provides us with a bunch of different UI Elements, NativeMenu is one of those elements and is the Menu itself - we can define as many menus as we like, and later we'll be adding SubMenus to our Menus


        public Main()
        {
            menuPool.Add(mainMenu);

            KeyDown += OnKeyDown;
            Tick += OnTick;
        }

We're interrested in the following

menuPool.Add(mainMenu);

Remember how our ObjectPool named menuPool is like our "collection" of menus - this call adds our mainMenu item to that "collection"


        private void OnTick(object sender, EventArgs e)
        {
            menuPool.Process();
        }

In order for our menu to be updated and represented properly, we Process all of our objects inside our ObjectPool every frame


        private void OnKeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.F5)
            {
                mainMenu.Visible = !mainMenu.Visible;
            }
        }

The F5 key Toggles our mainMenu on and off


Code Breakdown - Advanced

Let's understand what's going on here

using LemonUI;
using LemonUI.Menus;
namespace NucleiLite

In C#, a namespace is a way to organize related code elements and to prevent naming conflicts. The namespace statement declares a scope that contains a set of related C# objects. In this code, the namespace statement is used to group all the classes, methods, and properties within a single namespace named NucleiLite.

The namespace declaration at the top of the code tells the C# compiler that all the classes and methods within the code belong to the NucleiLite namespace. This is done to ensure that any class or method defined within the namespace is unique and does not conflict with other namespaces or objects defined elsewhere in the program.

Using namespaces also allows developers to organize their code in a more logical and structured way, making it easier to find and understand the different parts of the program.

These using-directives allows us to work with the LemonUI framework.

You can learn more about namespaces and using directives


ObjectPool menuPool = new ObjectPool();

The ObjectPool class is used to manage the NativeMenu object that is created later in the code. The ObjectPool is a container that holds references to one or more objects that can be added to or removed from the pool as needed. The pool is used to efficiently manage the memory used by the objects and to provide a central location for accessing and manipulating them.

This above code creates a new instance of the ObjectPool class named menuPool. Once the ObjectPool is initialized, it can be used to manage any number of objects, including the NativeMenu that is created later in the code.

You can learn more about instantiation (Creating new) at Instance Constructors


NativeMenu mainMenu = new NativeMenu("NucleiLite", "Main Menu");

A new NativeMenu object is created called mainMenu with a title of NucleiLite and a subTitle of Main Menu. The NativeMenu is an object that can be displayed on the player's screen and can contain a variety of different menu items.


menuPool.Add(mainMenu);

Once the NativeMenu is created, it is added to the ObjectPool so that it can be easily managed and updated. The above code adds the mainMenu object to the ObjectPool named menuPool. By adding the NativeMenu to the ObjectPool, the menu can be easily accessed and updated. This allows for a more efficient and organized approach to managing the menu and its contents.


private void OnTick(object sender, EventArgs e)
{
    menuPool.Process();
}

The menuPool.Process() line is called within the OnTick method. This line calls the Process method of the ObjectPool class named menuPool. The Process method updates all of the objects contained in the ObjectPool, including the NativeMenus that are being added.

By calling the Process method of the menuPool within the Tick event handler, the menu is updated every game tick. This allows the menu to be responsive and up-to-date with the game state. The Tick event and the Process method allows for efficient and reliable updating of the menu.

You can learn more about the ObjectPool the NativeMenu and any other objects associated with LemonUI by inspecting the code - if you don't know how to, have a look at Documentation

Creating SubMenus

We'll implement the different scripts soon enough, but as shown by the images in the beginning of this section, we've divided our Menu into smaller categories - PlayerMenu, VehicleSpawner and Weapons so let's create these SubMenus first

  1. Copy and Paste the below code to your Main.cs class
using System;
using System.Windows.Forms;
using GTA;
using LemonUI;
using LemonUI.Menus;

namespace NucleiLite
{
    public class Main : Script
    {
        ObjectPool menuPool = new ObjectPool();
        NativeMenu mainMenu = new NativeMenu("NucleiLite", "Main Menu");
        NativeMenu playerMenu = new NativeMenu("NucleiLite", "Player Menu");
        NativeMenu vehicleSpawnerMenu = new NativeMenu("NucleiLite", "Vehicle Spawner Menu");
        NativeMenu weaponsMenu = new NativeMenu("NucleiLite", "Weapons Menu");

        public Main()
        {
            mainMenu.AddSubMenu(playerMenu);
            mainMenu.AddSubMenu(vehicleSpawnerMenu);
            mainMenu.AddSubMenu(weaponsMenu);

            menuPool.Add(mainMenu);
            menuPool.Add(playerMenu);
            menuPool.Add(vehicleSpawnerMenu);
            menuPool.Add(weaponsMenu);

            KeyDown += OnKeyDown;
            Tick += OnTick;
        }

        private void OnTick(object sender, EventArgs e)
        {
            menuPool.Process();
        }

        private void OnKeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.F5)
            {
                mainMenu.Visible = !mainMenu.Visible;
            }
        }
    }
}
  1. Build and Reload() and you should see the following

image

Code Breakdown - Simplified

Let's have a look

        NativeMenu playerMenu = new NativeMenu("NucleiLite", "Player Menu");
        NativeMenu vehicleSpawnerMenu = new NativeMenu("NucleiLite", "Vehicle Spawner Menu");
        NativeMenu weaponsMenu = new NativeMenu("NucleiLite", "Weapons Menu");

We've created all the submenus just like we did the mainMenu - we've set the title for all of them to be NucleiLite as this is the Banner title and should remain the same for all menus.


            mainMenu.AddSubMenu(playerMenu);
            mainMenu.AddSubMenu(vehicleSpawnerMenu);
            mainMenu.AddSubMenu(weaponsMenu);

We use the AddSubMenu() method defined in the NativeMenu class to add a subMenu to the selected menu (it takes another NativeMenu as a parameter (input))


            menuPool.Add(mainMenu);
            menuPool.Add(playerMenu);
            menuPool.Add(vehicleSpawnerMenu);
            menuPool.Add(weaponsMenu);

Just like we added the mainMenu to the menuPool we need to add any other menu to the pool as well, even the subMenus


Conclusion

We've set-up our Menu with Sub Menus, next we'll be implementing the different scripts we've created earlier into our menus.

Previous - NucleiLite Mod Menu
Next - Implementing Fix Player Script