【BepInEx】使用GUILayout来制作高级UI

整个系列的完整目录可以在上方的【目录&汇总】的下拉列表或从标签[ BepInEx ]中看到;
转载时请在显著的位置注明作者小莫以及本博客地址

介绍

GUI和GUILayout都是unity的UI布局类,且都寄托在UnityEngine.IMGUIModule中,所以,我们在使用时,需要从游戏目录中将 UnityEngine.IMGUIModule.dll 引用到我们的项目中来。
其中,
GUI是手动布局,需要我们手动给定x,y,w,h的值;
GUILayout是自动布局,我们只需设置每行排列数量即可;
GUI的布局比较的繁琐,且非常不灵活,所以我比较推荐使用GUILayout来进行UI自动布局;
关于GUILayout的使用与介绍,Unity官网有更加详细的说明,我这边就不做复杂的讲解,大家可以另行见:https://docs.unity3d.com/ScriptReference/GUILayout.html

我这边只做基础的使用方法以及对一些常用的控件进行说明

制作一个窗口

上面说过GUILayout是寄托在UnityEngine.IMGUIModule中的类了,那么,请自行引用UnityEngine.IMGUIModule.dll(在游戏目录 XXX_Data\Managed中)文件到vs工程中。

现在来给大家介绍一个新函数—— OnGUI()
OnGUI()的作用和之前我们介绍的Update()是同类型的,都是在游戏运行后会一直循环执行函数中的内容,不过OnGUI()是unity专门为GUI设定的一个函数,虽然我们窗口写到OnGUI()和写到Update()都可以达到我们想要的效果,但是为了代码更加的规范,我建议大家将UI相关的代码写到OnGUI()中,监听插件相关的代码写到Update()中。

所以,若我们想实现按F9打开/关闭窗口的“开关”,我们可以这样写:

using System;
using BepInEx;
using BepInEx.Configuration;
using UnityEngine;

namespace AdvancedUI
{
    [BepInPlugin("aoe.top.plugins.AdvancedUI", "高级UI示例", "1.0.0.0")]
    public class AdvancedUI : BaseUnityPlugin
    {
        // 窗口开关
        public bool DisplayingWindow = false;
        // 启动按键
        private ConfigEntry ShowCounter { get; set; }

        [Obsolete]
        public void Start()
        {
            // 允许用户自定义启动快捷键
            ShowCounter = Config.AddSetting("打开窗口快捷键", "Key", new BepInEx.Configuration.KeyboardShortcut(KeyCode.F9));
        }
        public void Update()
        {
            // 监听脚本按键按下
            if (ShowCounter.Value.IsDown())
            {
                DisplayingWindow = !DisplayingWindow;
                if (DisplayingWindow)
                {
                    Debug.Log("打开窗口");
                }
                else
                {
                    Debug.Log("关闭窗口");
                }
            }
        }

        // GUI函数
        private void OnGUI()
        {
            if (this.DisplayingWindow)
            {
                // 定义窗口位置 x y 宽 高
                Rect windowRect = new Rect(500, 200, 500, 300);
                // 创建一个新窗口
                // 注意:第一个参数(20210218)为窗口ID,ID尽量设置的与众不同,若与其他Mod的窗口ID相同,将会导致窗口冲突
                windowRect = GUI.Window(20210218, windowRect, DoMyWindow, "我的一个窗口");

            }
        }

        public void DoMyWindow(int winId)
        {

        }
    }
}

以上代码即可简单实现按F9打开/关闭窗口:

自动UI布局

定义新的区域:GUILayout.BeginArea

默认,GUILayout都是从左上角0,0坐标开始进行自动布局的,有时候,我们希望它显示在窗口中的其他位置,我们则需要使用GUILayout.BeginArea来定义一个新的区域,然后再将我们的控件写进去。

注意:GUILayout.BeginArea需要搭配 GUILayout.EndArea一起使用,否则会报错。

示例:

public void DoMyWindow(int winId)
{
	GUILayout.BeginArea(new Rect(10, 20, 490, 250));
	// 这里的大括号是可选的,我个人为了代码的阅读性,习惯性的进行了添加
	// 建议大家也使用大括号这样包裹起来,让代码看起来不那么的乱
	{
		GUILayout.Label("这是一行普通的文本框");
		if (GUILayout.Button("按钮1"))
		{
			Debug.Log("你点击了按钮1");
		}
		if (GUILayout.Button("按钮2"))
		{
			Debug.Log("你点击了按钮2");
		}
	}
	GUILayout.EndArea();
}

运行结果:

横向自动布局:GUILayout.BeginHorizontal

默认,GUILayout是竖向自动排列的,但我们可以使用GUILayout.BeginHorizontal让我们的控件进行横向排列。

注意:GUILayout.BeginHorizontal需要与GUILayout.EndHorizontal配合使用,否则会报错。

示例:

public void DoMyWindow(int winId)
{
	GUILayout.BeginArea(new Rect(10, 20, 480, 250));
	{
		GUILayout.Label("这是一行普通的文本框");
		// 第一行
		GUILayout.BeginHorizontal();
		{
			if (GUILayout.Button("按钮1.1"))
			{
				Debug.Log("你点击了按钮1.1");
			}
			if (GUILayout.Button("按钮1.2"))
			{
				Debug.Log("你点击了按钮1.2");
			}
			if (GUILayout.Button("按钮1.3"))
			{
				Debug.Log("你点击了按钮1.3");
			}
			if (GUILayout.Button("按钮1.4"))
			{
				Debug.Log("你点击了按钮1.4");
			}
		}
		GUILayout.EndHorizontal();

		// 第二行
		GUILayout.BeginHorizontal();
		{
			if (GUILayout.Button("按钮2.1"))
			{
				Debug.Log("你点击了按钮2.1");
			}
			if (GUILayout.Button("按钮2.2"))
			{
				Debug.Log("你点击了按钮2.2");
			}
			if (GUILayout.Button("按钮2.3"))
			{
				Debug.Log("你点击了按钮2.3");
			}
			if (GUILayout.Button("按钮2.4"))
			{
				Debug.Log("你点击了按钮2.4");
			}
		}
		GUILayout.EndHorizontal();

	}
	GUILayout.EndArea();
}

运行结果:

窗口滚动条:GUILayout.BeginScrollView

若我们的内容太多,又不想制作分页,那么就可能需要用到滚动条了,滚动条需要定义一个全局 Vector2 变量来接收、传递、储存滚动条的x,y坐标位置;

注意:GUILayout.BeginScrollView需与GUILayout.EndHorizontal配合使用,否则会报错

示例:

private Vector2 scrollPosition;
public void DoMyWindow(int winId)
{
	GUILayout.BeginArea(new Rect(10, 20, 480, 250));
	{
		GUILayout.Label("这是一行普通的文本框");
		
		// 第一行
		GUILayout.BeginHorizontal();
		{
			if (GUILayout.Button("按钮1.1"))
			{
				Debug.Log("你点击了按钮1.1");
			}
			if (GUILayout.Button("按钮1.2"))
			{
				Debug.Log("你点击了按钮1.2");
			}
			if (GUILayout.Button("按钮1.3"))
			{
				Debug.Log("你点击了按钮1.3");
			}
			if (GUILayout.Button("按钮1.4"))
			{
				Debug.Log("你点击了按钮1.4");
			}
		}
		GUILayout.EndHorizontal();

		// 第二行
		GUILayout.BeginHorizontal();
		{
			if (GUILayout.Button("按钮2.1"))
			{
				Debug.Log("你点击了按钮2.1");
			}
			if (GUILayout.Button("按钮2.2"))
			{
				Debug.Log("你点击了按钮2.2");
			}
			if (GUILayout.Button("按钮2.3"))
			{
				Debug.Log("你点击了按钮2.3");
			}
			if (GUILayout.Button("按钮2.4"))
			{
				Debug.Log("你点击了按钮2.4");
			}
		}
		GUILayout.EndHorizontal();

		scrollPosition = GUILayout.BeginScrollView(scrollPosition, false, false, GUILayout.Width(480), GUILayout.Height(150));
		{
			if (GUILayout.Button("按钮3"))
			{
				Debug.Log("你点击了按钮3");
			}
			if (GUILayout.Button("按钮4"))
			{
				Debug.Log("你点击了按钮4");
			}
			if (GUILayout.Button("按钮5"))
			{
				Debug.Log("你点击了按钮5");
			}
			if (GUILayout.Button("按钮6"))
			{
				Debug.Log("你点击了按钮6");
			}
			if (GUILayout.Button("按钮7"))
			{
				Debug.Log("你点击了按钮7");
			}
			if (GUILayout.Button("按钮8"))
			{
				Debug.Log("你点击了按钮8");
			}
			if (GUILayout.Button("按钮9"))
			{
				Debug.Log("你点击了按钮9");
			}
			if (GUILayout.Button("按钮10"))
			{
				Debug.Log("你点击了按钮10");
			}
		}
		GUILayout.EndScrollView();

	}
	GUILayout.EndArea();
}

运行结果:

结语

GUILayout还可通过GUIStyle来为控件制作样式,但这部分我还研究的不够升入,我只会制作一些简单的样式(比如说设置背景颜色,固定宽高等,这些可以在vs中直接看到相关参数),等我研究出更加深入的样式后,可能会更新续篇。
到现在为止,BepInEx的教程系列就算完结了,感谢大家的游览和阅读,希望你能成为Mod领域中的一座大山,造福更多的玩家。
点赞
  1. Cody说道:
    Google Chrome Windows 10
    Great guide :)

发表评论

电子邮件地址不会被公开。必填项已用 * 标注