preface

Remind: In order to be able to learn the knowledge more thoroughly, remember more firmly I will write down the knowledge by means of teaching on Because can let a person in the process from students to the teacher This process will be dug up new knowledge and ideas is a self thinking mining switch and a depth of knowledge and the process of ascension If you can help to everyone that is the best If there is something wrong, please give me more advice! I’m just a vegetable chicken thanks for understanding!


Why use a UI framework

There are a lot of panels in the game: backpack panel, shop panel, Settings panel,

There will be frequent calls and switches between these panels. For example, there are various items in the backpack panel, and a prompt box of item information will appear when we put the mouse on the item. In addition, we need to press the back button to switch back to the main menu panel in the setting panel. My previous practice was to write a script on each panel I used to implement the corresponding function, I believe that many novice developers have come over like this. (Nani is me?)

Although this function is realized, but the maintenance is reduced, the repeated code also becomes more, in order to more convenient management and call the scene of the panel we need to write a complete set of framework to constrain them, otherwise it is easy to coexistence of panels, or switch failure and other problems.

SIKI has a small UI framework course for beginners that is very good, the most important thing is simple!! Really friendly for a novice like me! Let’s learn together!


Framework ideas

First to briefly introduce the framework of the idea, the framework is mainly to achieve the reading of the panel storage and management, to help us better switch between the panel management

First of all,

1. We store all the panel information we need to use through Json files, such as the name of the panel, the type of the panel, the loading path of the panel, etc., according to the actual project requirements

Create a panel abstract class (the base class for all panels) to implement the common panel methods, such as the things to do when the panel enters and the things to do when the panel exits

The code reads the Json file to get all the panel information, which we can then use to manipulate the panel

4. The core of the framework is how to efficiently manage these panels. Let’s first go through the process of creating and closing panels

At this point, the player can only operate the backpack panel, not the character panel (think about that in many games). When we want to operate the character panel, we need to close the backpack panel before we can operate the character panel

Here’s another picture to illustrate

Now we are located at the top of the Settings menu under the menu interface, now we can only operate the sub-menu interface, if we want to operate the Settings menu interface we need to close the sub-menu first, then we can operate the Settings menu

Now that the submenu is closed, we can manipulate the Settings menu!

Similarly, if we want to access the main menu, we have to close the Settings menu to access the main menu

So how does Siki manage the switching relationship between panels? Those of you who are familiar with data structures may immediately understand the above diagram! Right, a Stack, which is a collection of lifO objects lifO, lifO, lifO, lifO, lifO. Right? Wouldn’t that just be used to store and manage our panels

The first thing that goes to the main menu, so we put it at the bottom of the container, and then the player clicks on the Settings menu, so we put it on top of the main menu, and then the player clicks on the sub-menu of the Settings menu, and we put the sub-menu on top of the Settings menu

We made it easy to switch between panels by allowing the player to only manipulate the top panel in the stack, pushing it in (panel in) and popping it out (panel out)

Well, to be honest, I think WHAT I’m saying is a piece of shit.


In field

Thought managed once! Now let’s do it step by step!

1. Create a Json file and write the panel information into Json

So let’s do the first step

We use Json file to store all the panel information we need to use, such as: panel name, panel type, panel loading path, etc., according to the actual project requirements

Before we create the Json file, we need to make a prefab of the panels that we’re going to use and put them in the Resources directory and then load them in Resources, so I created a new image here to show you, Call it “SettingPanel” and put it in the UIPanel folder of Resources

Ok, now let’s create a Json file. Json is a Json file, note that we also need to put our Json file in the Resources folder because we will get it from Resources later) and put our panel information in it

Json file

{
	"data": [{"uiPanelType":"settingPanel"."uiPanelPath":"UIPanel/SettingPanel"}}]Copy the code

Here is a brief description of the Json file format

Curly braces {} save objects: Objects can contain a variety of data, including arrays. Square brackets [] save arrays: Arrays can contain objects. Data is separated by commas

Don’t understand we can directly find a parse the Json web site online Here recommend www.bejson.com/convert/jso…

Let’s go straight to Json and go to C# entity class and see what we can do. Let’s copy the Json data that we wrote and then click generate entity class

And just to make it easier for you to see, I’m going to make a copy of the generated entity and show it to you

  
 public class DataItem
 {  
 	  public string uiPanelType { get; set; }
 
  	  public string uiPanelPath { get; set; }}public class Root
 {
     public List <DataItem> data { get; set; }}Copy the code

I wonder if you noticed that these two classes just map the data in the Json file

A close comparison will show what this means

Note that the data must correspond to the information in the Json file we wrote (and vice versa). If you write one wrong word, the Json will fail to parse


// Parsing succeeded

public string uiPanelType { get; set; }
public string uiPanelPath { get; set; }
public List <DataItem> data { get; set; }

"uiPanelType":"settingPanel"."uiPanelPath":"UIPanel/SettingPanel"}



// Failed to parse mapping class and Json data name is not the same parsing error

public string PanelType { get; set; }
public string PanelPath { get; set; }
public List <DataItem> paneldata { get; set; }

"uiPanelType":"settingPanel"."uiPanelPath":"UIPanel/SettingPanel"}
Copy the code

What I speak may be as bad as a piece of shit or even worse (I help you speak) is the best is to go to Baidu or Valley dad to check the information, now I will also be the most basic operation back I will further study and then write an article about Json out (disaster society ~)

That was our one-click generated mapping class so let’s write it down ourselves

PanelData class

public class PanelData
{
    /// <summary>
    /// UI panel type
    /// </summary>
    public string uiPanelType;
    /// <summary>
    /// UI panel path
    /// </summary>
    public string uiPanelPath;
}
Copy the code

UIPanelDataList class

public class UIPanelDataList
{
    public PanelData[] data;
}
Copy the code

So once we’re done with the two mapping classes we’re going to write a UIPanelType enumeration class to define our panel type, and then we’re going to take the UIPanelType data in the Json file, and then we’re going to convert the String to an enumeration to get the panel type

UIPanelType class:

public enum UIPanelType
{
     settingPanel           // Set panel
}
Copy the code

2. Create the base class for the panel

Create a panel abstraction class (the base class for all panels) to implement methods common to panels, such as things to do when the panel enters and things to do when the panel exits

UIPanelBase class is a little bit more simple and I don’t want to talk about how you can define the methods that you have in common with your panels according to your own needs, okay

UIPanelBase class:

using UnityEngine;

public class UIPanelBase : MonoBehaviour
{
    /// <summary>
    /// push status
    /// </summary>
    public virtual void PushState(a)
    {
        Debug.Log("Push state");
    }

    /// <summary>
    / / / out of the stack
    /// </summary>
    public virtual void PopState(a)
    {
        Debug.Log("Out of stack state");
    }

    /// <summary>
    /// The panel is restored
    /// </summary>
    public virtual void RemotState(a)
    {
        Debug.Log("Restore state"); }}Copy the code

3. Read Json files

The code reads the Json file to get all the panel information, which we can then use to manipulate the panel

I recommend using LitJson, but you can also use Unity’s JsonUtility, whichever you feel comfortable using.

To use LitJson, you need to reference it, download litjson. DLL, and put it in your Plugins folder. If you don’t have one, create your own

Now we can create the core UIManager class

MainUIManager class

The first step to referencing litJson is as simple as saying “Using litJson;”

Then make the MainUIManager class a singleton.


using System.Collections.Generic;
using UnityEngine;
using LitJson;

public class MainUIManager : MonoBehaviour
{
	/// <summary>
    / / / MainUIManager singleton
    /// </summary>
    public static MainUIManager Instance;
	
	public void Awake(a)
	{
		  Instance = this; }}Copy the code

After the singleton is done we need two dictionaries one to store the data parsed from Json and one to store the UIPanelBase of the panels (we need to add a script on each panel to operate on and inherit the UIPanelBase class) We get the UIPanelBase class on the panel and we get the instance of the panel.)


using System.Collections.Generic;
using UnityEngine;
using LitJson;

public class MainUIManager : MonoBehaviour
{
	/// <summary>
    / / / MainUIManager singleton
    /// </summary>
    public static MainUIManager Instance;
	
	 /// <summary>
    // save the panel data parsed from the Json file
    /// </summary>
    private Dictionary<UIPanelType, string> oriPanelDataDic = new Dictionary<UIPanelType, string> ();/// <summary>
    // save the panel instance
    /// </summary>
    private Dictionary<UIPanelType, UIPanelBase> newPanelDataDic = new Dictionary<UIPanelType, UIPanelBase>();
	
	public void Awake(a)
	{
		  Instance = this; }}Copy the code

I’m not very good at English and I’m a little bad at naming things

Now we are ready to read the data in the Json file


using System.Collections.Generic;
using UnityEngine;
using LitJson;

public class MainUIManager : MonoBehaviour
{
	/// <summary>
    / / / MainUIManager singleton
    /// </summary>
    public static MainUIManager Instance;
	
	 /// <summary>
    // save the panel data parsed from the Json file
    /// </summary>
    private Dictionary<UIPanelType, string> oriPanelDataDic = new Dictionary<UIPanelType, string> ();/// <summary>
    // save the panel instance
    /// </summary>
    private Dictionary<UIPanelType, UIPanelBase> newPanelDataDic = new Dictionary<UIPanelType, UIPanelBase>();
	
	/// <summary>
    // the text resource is used to read the data in the Json file
    /// </summary>
    private TextAsset uiPanelJson;
	
	public void Awake(a)
	{
		  Instance = this;
	}
	
	 /// <summary>
    // initialize Json data
    /// </summary>
    private void InitJson(a)
    {
        // Get Json file data
        uiPanelJson = Resources.Load<TextAsset>("UIPanelData");

		/ / parsing Json
        UIPanelDataList panelData = JsonMapper.ToObject<UIPanelDataList>(uiPanelJson.text);

        // Store data in the dictionary
        foreach (PanelData item in panelData.data)
        {
            // Convert String to enumeration and store it in the dictionaryoriPanelDataDic.Add((UIPanelType)System.Enum.Parse(typeof(UIPanelType), item.uiPanelType.ToString()), item.uiPanelPath); }}}Copy the code

Then we take the Canvas Transform component and so on and we write the logic to generate the panel and we need to set the parent object of the panel under the Canvas and I set a Tag called “UICanvas” so I look for it directly using the Tag


using System.Collections.Generic;
using UnityEngine;
using LitJson;

public class MainUIManager : MonoBehaviour
{
	/// <summary>
    / / / MainUIManager singleton
    /// </summary>
    public static MainUIManager Instance;
	
	 /// <summary>
    // save the panel data parsed from the Json file
    /// </summary>
    private Dictionary<UIPanelType, string> oriPanelDataDic = new Dictionary<UIPanelType, string> ();/// <summary>
    // save the panel instance
    /// </summary>
    private Dictionary<UIPanelType, UIPanelBase> newPanelDataDic = new Dictionary<UIPanelType, UIPanelBase>();
	
	/// <summary>
    // the text resource is used to read the data in the Json file
    /// </summary>
    private TextAsset uiPanelJson;
	
    private Transform uiCanvasTran;
	
	public void Awake(a)
	{
		  Instance = this;
		  InitJson();
          Init();
	}
	
	 /// <summary>
    // initialize Json data
    /// </summary>
    private void InitJson(a)
    {
        // Get Json file data
        uiPanelJson = Resources.Load<TextAsset>("UIPanelData");

		/ / parsing Json
        UIPanelDataList panelData = JsonMapper.ToObject<UIPanelDataList>(uiPanelJson.text);

        // Store data in the dictionary
        foreach (PanelData item in panelData.data)
        {
            // Convert String to enumeration and store it in the dictionaryoriPanelDataDic.Add((UIPanelType)System.Enum.Parse(typeof(UIPanelType), item.uiPanelType.ToString()), item.uiPanelPath); }}/// <summary>
    /// initialize the operation
    /// </summary>
    private void Init(a)
    {
        if (uiCanvasTran == null)
        {
            uiCanvasTran = GameObject.FindGameObjectWithTag("UICanvas").transform; }}}Copy the code

It is also easy to write the logic for the generation panel


using System.Collections.Generic;
using UnityEngine;
using LitJson;

public class MainUIManager : MonoBehaviour
{
	/// <summary>
    / / / MainUIManager singleton
    /// </summary>
    public static MainUIManager Instance;
	
	 /// <summary>
    // save the panel data parsed from the Json file
    /// </summary>
    private Dictionary<UIPanelType, string> oriPanelDataDic = new Dictionary<UIPanelType, string> ();/// <summary>
    // save the panel instance
    /// </summary>
    private Dictionary<UIPanelType, UIPanelBase> newPanelDataDic = new Dictionary<UIPanelType, UIPanelBase>();
	
	/// <summary>
    // the text resource is used to read the data in the Json file
    /// </summary>
    private TextAsset uiPanelJson;
	
    private Transform uiCanvasTran;
	
	public void Awake(a)
	{
		  Instance = this;
		  InitJson();
          Init();
	}
	
	 /// <summary>
    // initialize Json data
    /// </summary>
    private void InitJson(a)
    {
        // Get Json file data
        uiPanelJson = Resources.Load<TextAsset>("UIPanelData");

		/ / parsing Json
        UIPanelDataList panelData = JsonMapper.ToObject<UIPanelDataList>(uiPanelJson.text);

        // Store data in the dictionary
        foreach (PanelData item in panelData.data)
        {
            // Convert String to enumeration and store it in the dictionaryoriPanelDataDic.Add((UIPanelType)System.Enum.Parse(typeof(UIPanelType), item.uiPanelType.ToString()), item.uiPanelPath); }}/// <summary>
    /// initialize the operation
    /// </summary>
    private void Init(a)
    {
        if (uiCanvasTran == null)
        {
            uiCanvasTran = GameObject.FindGameObjectWithTag("UICanvas").transform; }}}/// <summary>
    /// Generate panel
    /// </summary>
     
    /// <returns></returns>
    public UIPanelBase GeneratePanel(UIPanelType panelType)
    {
        UIPanelBase uiPanelBase;
        newPanelDataDic.TryGetValue(panelType, out uiPanelBase);

        // uiPanelBase is not in the dictionary if it is empty
        if (uiPanelBase == null)
        {
            string panelPath;
            oriPanelDataDic.TryGetValue(panelType, out panelPath);
            GameObject newPanel = Instantiate(Resources.Load<GameObject>(panelPath));
            newPanel.transform.SetParent(uiCanvasTran, false);
            uiPanelBase = newPanel.GetComponent<UIPanelBase>();
            newPanelDataDic.Add(panelType, uiPanelBase);
            return uiPanelBase;
        }
        return uiPanelBase;
    }

Copy the code

Now that we’ve generated the panel and returned uiPanelBase, we can do the panel on and off the stack


using System.Collections.Generic;
using UnityEngine;
using LitJson;

public class MainUIManager : MonoBehaviour
{
	/// <summary>
    / / / MainUIManager singleton
    /// </summary>
    public static MainUIManager Instance;
	
	 /// <summary>
    // save the panel data parsed from the Json file
    /// </summary>
    private Dictionary<UIPanelType, string> oriPanelDataDic = new Dictionary<UIPanelType, string> ();/// <summary>
    // save the panel instance
    /// </summary>
    private Dictionary<UIPanelType, UIPanelBase> newPanelDataDic = new Dictionary<UIPanelType, UIPanelBase>();
	
	/// <summary>
    // the text resource is used to read the data in the Json file
    /// </summary>
    private TextAsset uiPanelJson;
	
    private Transform uiCanvasTran;
	
	private Stack<UIPanelBase> panelStack;
	
	public void Awake(a)
	{
		  Instance = this;
		  InitJson();
          Init();
	}
	
	 /// <summary>
    // initialize Json data
    /// </summary>
    private void InitJson(a)
    {
        // Get Json file data
        uiPanelJson = Resources.Load<TextAsset>("UIPanelData");

		/ / parsing Json
        UIPanelDataList panelData = JsonMapper.ToObject<UIPanelDataList>(uiPanelJson.text);

        // Store data in the dictionary
        foreach (PanelData item in panelData.data)
        {
            // Convert String to enumeration and store it in the dictionaryoriPanelDataDic.Add((UIPanelType)System.Enum.Parse(typeof(UIPanelType), item.uiPanelType.ToString()), item.uiPanelPath); }}/// <summary>
    /// initialize the operation
    /// </summary>
    private void Init(a)
    {
        if (uiCanvasTran == null)
        {
            uiCanvasTran = GameObject.FindGameObjectWithTag("UICanvas").transform; }}/// <summary>
    /// Generate panel
    /// </summary>
     
    /// <returns></returns>
    public UIPanelBase GeneratePanel(UIPanelType panelType)
    {
        UIPanelBase uiPanelBase;
        newPanelDataDic.TryGetValue(panelType, out uiPanelBase);

        // uiPanelBase is not in the dictionary if it is empty
        if (uiPanelBase == null)
        {
            string panelPath;
            oriPanelDataDic.TryGetValue(panelType, out panelPath);
            GameObject newPanel = Instantiate(Resources.Load<GameObject>(panelPath));
            newPanel.transform.SetParent(uiCanvasTran, false);
            uiPanelBase = newPanel.GetComponent<UIPanelBase>();
            newPanelDataDic.Add(panelType, uiPanelBase);
            return uiPanelBase;
        }
        return uiPanelBase;
    }
	
	/// <summary>
    /// the panel is pushed
    /// </summary>
    /// <param name="panelType"></param>
    public void PushPanel(UIPanelType panelType)
    {
        if(panelStack==null)
        {
            panelStack = new Stack<UIPanelBase>();
        }
        UIPanelBase uiPanelBase = GeneratePanel(panelType);
        uiPanelBase.PushState();
        panelStack.Push(uiPanelBase);
    }

    /// <summary>
    /// panel unstack
    /// </summary>
    public void PopPanel(a)
    {
        if (panelStack == null) return;

        // Get the top data
        UIPanelBase currentUIpanelBase = panelStack.Pop();
        currentUIpanelBase.PopState();

        if (panelStack.Count > 0) { UIPanelBase outUIpanelBase = panelStack.Peek(); outUIpanelBase.RemotState(); }}}Copy the code

So we’re done with our MainUIManager class and then it’s easy to call and we’ll add a script for SettingPanel “SettingPanel” (remember to inherit UIPanelBase)

public class SettingPanel : UIPanelBase
{
    public void AddButtonClick(a)
    { MainUIManager.Instance.PushPanel(UIPanelType.settingPanel); }}Copy the code

After the method is written, bind it to the Button to be clicked (for example, click the Setup Button to display the setup panel and bind the script of the setup panel to the setup Button component).

I’m not going to bind it. I’m just going to drag it onto OnClick and I’m going to select the method that we just wrote or baidu is going to do it in a second

Now that we’re all done run the game and see what happens! Normally, clicking on the Settings button now will bring up the panel


4, the last

If you pop up the panel That proved successful, popup also don’t be discouraged if you don’t study the code and see an error, in the above case we only do the panel into the stack operation, you can try to complete the panel of stack operation, a very simple only need to write one line of code, code is to think about why, rather than follow anything knock, Try adding some new features to the framework yourself!

Finally, I want to explain that the article is mainly used to consolidate the knowledge I learned with, so not those big guy said so detailed, if you see meng! Then I am very sorry, suggest to close to watch the full tutorial hahaha ~