Unity基础笔记—— Unity UI系统
Posted Dukenone
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity基础笔记—— Unity UI系统相关的知识,希望对你有一定的参考价值。
Unity UI系统
一、UI系统介绍
1. Unity2D 和 UI的区别
Unity2D:主要基于 SpriteRenderer 和 2D物理系统等组成。
UI:由 Canvas 以及 各种 UI 组件组成。
UI 的显示不基于 SpriteRenderer,且UI界面是完全贴合屏幕的,不会随相机的移动而移动。
2. UI系统的组成
- Canvas:画布,UI 的根节点,尺寸大小和画面分辨率一致;
- EventSystem:事件系统,与Canvas绑定,只有基于事件系统才能使按钮可以点击;
- Image / Text等组件:具体的功能组件,显示一张图、制作一个按钮等。
二、Image 组件
1. Image 组件
UI 中除了文本,最多的就是图片、按钮。Image 组件主要复制图片的显示。
Image 游戏物体必须放在 Canvas 游戏物体下才能生效。
主要属性:
- SourceImage:源图片,在脚本中叫 Sprite;
- Color:图片颜色;
- RaycastTarget:是否可以作为射线目标,后续点击、拖拽等事件需要使用;
- ImageType:显示模式;
- Simple:普通模式
- Sliced:切片,需要图片九宫格,要在资源层面处理
- Tiled:平铺
- Filled:填充
主要功能:
- Set Native Size:设置为图片的原始尺寸。
2. 图片资源
在项目管理器中点击一个图片资源,然后在检查器面板中可以看到资源的相关属性设置。
- TextureType:Unity中大多数情况下我们都需要选择为 Sprite
- Sprite Model:精灵模式,精灵是 Image 组件持有的实际图片。
- Single:一个图片一个精灵
- Multiple:一个图片拆成多个精灵
- Pixels Per Unit:像素单位,会影响实际在游戏中图片的尺寸,数值越大游戏中越小
- Pivot:图片的中心
- Sprite Editor按钮:编辑精灵
3. SpriteEditor
- PackageManager:
- 插件/功能管理器,Window -> PackageManager
- Unity 中有很多官方开发好的功能,但是不见得每个游戏项目都需要,所以我们可以在插件管理器中选择自己需要的功能来安装,SpriteEditor 需要 2D Sprite插件。
- 精灵编辑
- 设置 Pivot 轴心
- Revert:恢复设置
- Apply:确认操作
- 蓝色外框是精灵的九宫格
4. SpriteEditor-Slice 图片切片
图片切片:实际开发中很多时候我们并不会一个按钮一个图片,而是选择打包在一起,所以需要在 Unity 中进行分割。
- Automatic:自动分割,实际是根据不同游戏物体之间的透明通道进行;
- Grid By Cell Size:根据网格像素分割,根据一个宽高(像素)自动分割;
- Grid By Cell Count:根据网格数量分割,就是将一个图片等分拆成几行几列;
- Pivot:分割时每一个精灵的轴心;
- 拖拽鼠标:手动分割;
- 编辑过程中白色的框是一个精灵,点击后可以设置这个精灵的细节。
如果需要用脚本操作UI相关操作,必须引入UI的命名空间 System.UnityEngine.UI
。
三、Text 组件
1. Text 组件介绍
Text 组件就是用来显示文本的,比如等级、属性值、角色名称等等。
2. Text 组件常用属性
- Text:显示的文本内容;
- Font:字体;
- Font Style:字体样式,主要是加粗、斜体;
- Font Size:字体大小;
- Line Spacing:行间距;
- RichText:富文本,有html相关经验的可以用,没有可以无视;
- Color:字体颜色;
- Raycast Target:是否作为射线检测的目标,也就是射线可以检测到,意味着后续是否可以点击等。
3. Text 组件对齐属性
-
Alignment:文本的对齐方式;
-
Horizontal Overflow:水平溢出
(1)Wrap:文本将自动换行,达到水平边界。
(2)Overflow:文本可以超出水平边界,继续显示。
-
Vertical Overflow:垂直溢出
(1)Truncate:文本不显示超出垂直边界的部分。
(2)Overflow:文本可以超出垂直边界,继续显示。
-
Best Fit:尽可能匹配边缘
(1)Min Size:字体的最小大小;
(2)Max Size:字体的最大大小。
四、Button 组件
1. Button 的构成
Button:Image组件、Button组件
Text:Text组件
2. Button 样式
Interactable:可交互的,也就是按钮是否有效
Transition:过渡方式,按钮一般分为几种状态,比如鼠标悬浮、点击、不可用
- Target Graphic:按钮影响的 Image;
- Normal Color/Sprite:常规;
- Highlighted Color/Sprite:高亮、鼠标悬浮;
- Pressed Color/Sprite:按下;
- Disabled Color/Sprite:禁用;
- Fade Duration:颜色过渡时间
3. Navigation导航
在游戏中,我们经常使用 WASD 或 上下左右方向键等方式来选择按钮,Unity 中的UGUI 也具备这个功能,并且设置起来十分方便。
4. Button 事件
-
通过面板添加点击事件;
On Click()栏 -> 添加操作对象 -> 添加对象上绑定的方法
-
通过代码添加点击事件
private Button button; private void Start() button = GetComponent<Button>(); button.onClick.AddListener(ButtionClick2); public void ButtionClick2() Debug.Log("ButtonClick2");
五、InputField 输入框组件
1. InputField 结构
- InputField:Image 组件(背景图片)、InputField 组件
- Placeholder:Text 组件(玩家完全没输入时显示的内容)
- Text:Text组件实际输入的承载组件
2. 输入类型
InputField 中 ContentType 可以设置玩家输入的类型,比如邮箱、密码等。
3. InputField 事件
与 Button 事件类似,虽然事件不同,但是操作方法基本一致。
需要注意的是,如果需要手动获取 InputField 中玩家输入的值,要直接使用 InputField.text
,而不要去查找子物体 Text 中的组件。
-
通过面板添加事件:
- On Value Changed(String):输入框中值改变时进行同步
- On End Edit(String):结束输入框编辑进行同步
- On Submit(String):提交时进行同步
-
通过代码添加事件
private InputField inputField; void Start() inputField = GetComponent<InputField>(); // 修改InputField中的内容 /*inputField.text = "12345"; print(inputField.text);*/ // OnValueChanged + OnEndEdit inputField.onValueChanged.AddListener(OnValueChanged); inputField.onEndEdit.AddListener(onEndEdit); public void OnValueChanged(string value) Debug.Log(value); public void onEndEdit(string value) Debug.Log(value);
六、Toggle 切换组件
1. Toggle 基本使用
Toggle 结构:
- Toggle:Toggle组件
- Background:Image组件(背景图)
- Checkmark:Image组件(打勾)
- Label:Text组件(文本)
- Background:Image组件(背景图)
private Toggle toggle;
void Start()
toggle = GetComponent<Toggle>();
toggle.onValueChanged.AddListener(OnValueChanged);
private void Update()
if(Input.GetMouseButtonDown(0))
print(toggle.isOn);
private void OnValueChanged(bool bl)
print("bool: " + bl);
2. ToggleGroup
- 只能在 Add Component 中添加
七、案例:注册与登录
1. 案例介绍
注册:玩家输入账号、密码、重复密码、性别等来完成注册,其中需要对密码与重复密码进行一致性校验。
登录:玩家输入账号、密码后和之前输入的账号密码等信息进行匹配。
玩家输入有误时进行弹窗提示。
2. 案例分析
-
主面板:主面板上有两个按钮,注册、登录,点击后切换到对应面板。
-
注册面板:
-
玩家输入账号、密码、重复密码、性别后点击注册,对密码与重复密码进行匹配。
- 输入正确:弹窗“注册成功,请登录“
- 输入错误-账号与密码没输入:弹窗“请输入账号或密码”
- 输入错误-密码和重复密码不一致:弹窗“密码与重复密码不一致”
-
返回按钮:返回主面板
-
-
登录面板:
-
玩家输入账号、密码后点击登录:
- 输入正确:弹窗“登录成功”
- 输入错误-账号或密码没输入:弹窗“请输入账号或密码”
- 输入错误-数据库里没有对应的用户:弹窗“用户名或密码错误”
-
返回按钮:返回主面板
-
3. 案例实现
// 主面板 MainPanel
public class MainPanelTest : MonoBehaviour
// 单例模式
public static MainPanelTest instance;
private Button registerButton;
private Button loginButton;
private void Awake()
instance = this;
void Start()
registerButton = transform.Find("RegisterButton").GetComponent<Button>();
loginButton = transform.Find("LoginButton").GetComponent<Button>();
registerButton.onClick.AddListener(RegisterButtonClick);
loginButton.onClick.AddListener(LoginButtonClick);
private void RegisterButtonClick()
// 打开注册面板
RegisterPanelTest.instance.Show();
gameObject.SetActive(false);
private void LoginButtonClick()
// 打开登录面板
LoginPanelTest.instance.Show();
gameObject.SetActive(false);
public void Show()
gameObject.SetActive(true);
// 注册面板 RegisterPanel
public class RegisterPanelTest : MonoBehaviour
// 单例模式
public static RegisterPanelTest instance;
private InputField UserName;
private InputField Password;
private InputField RePassword;
private Toggle IsMale;
private Button BackButton;
private Button OKButton;
private void Awake()
instance = this;
UserName = transform.Find("UserName/InputField").GetComponent<InputField>();
Password = transform.Find("Password/InputField").GetComponent<InputField>();
RePassword = transform.Find("RePassword/InputField").GetComponent<InputField>();
IsMale = transform.Find("GenderGroup/Male").GetComponent<Toggle>();
BackButton = transform.Find("BackButton").GetComponent<Button>();
OKButton = transform.Find("OKButton").GetComponent<Button>();
BackButton.onClick.AddListener(BackButtonClick);
OKButton.onClick.AddListener(OKButtonClick);
gameObject.SetActive(false);
private void BackButtonClick()
// 返回主面板
MainPanelTest.instance.Show();
gameObject.SetActive(false);
private void OKButtonClick()
// 确定注册
// 输入检测
if (string.IsNullOrEmpty(UserName.text) ||
string.IsNullOrEmpty(Password.text) ||
string.IsNullOrEmpty(RePassword.text))
FloatWindowTest.instance.ShowInfo("请输入账号或密码");
else if(Password.text != RePassword.text)
FloatWindowTest.instance.ShowInfo("密码和重复密码不一致");
else
// 当前用户已存在
if(GameManager.Instance.GetUserInfo(UserName.text) != null)
FloatWindowTest.instance.ShowInfo("请勿重复注册");
else
UserInfo userInfo = new UserInfo(UserName.text, Password.text, IsMale.isOn);
// 保存用户信息
GameManager.Instance.SaveUserInfo(userInfo);
FloatWindowTest.instance.ShowInfo("注册成功");
public void Show()
gameObject.SetActive(true);
UserName.text = "";
Password.text = "";
RePassword.text = "";
// 登录面板 LoginPanel
public class LoginPanelTest : MonoBehaviour
// 单例模式
public static LoginPanelTest instance;
private InputField UserName;
private InputField Password;
private Button BackButton;
private Button OKButton;
private void Awake()
instance = this;
UserName = transform.Find("UserName/InputField").GetComponent<InputField>();
Password = transform.Find("Password/InputField").GetComponent<InputField>();
BackButton = transform.Find("BackButton").GetComponent<Button>();
OKButton = transform.Find("OKButton").GetComponent<Button>();
BackButton.onClick.AddListener(BackButtonClick);
OKButton.onClick.AddListener(OKButtonClick);
gameObject.SetActive(false);
private void BackButtonClick()
// 返回主面板
MainPanelTest.instance.Show();
gameObject.SetActive(false);
public void OKButtonClick()
// 输入检测
if (string.IsNullOrEmpty(UserName.text) ||
string.IsNullOrEmpty(Password.text))
FloatWindowTest.instance.ShowInfo("请输入账号或密码");
else
// 登录用户不存在
UserInfo user = GameManager.Instance.GetUserInfo(UserName.text);
if (user == null)
FloatWindowTest.instance.ShowInfo("用户不存在");
else if (user.Password != Password.text)
FloatWindowTest.instance.ShowInfo("用户名或密码错误");
else if (user.Password == Password.text)
FloatWindowTest.instance.ShowInfo("登录成功");
public void Show()
gameObject.SetActive(true);
UserName.text = "";
Password.text = "";
// 弹窗 FloatWindow
public class FloatWindowTest : MonoBehaviour
// 单例模式
public static FloatWindowTest instance;
private Text infoText;
private Button okButton;
private void Awake()
instance = this;
infoText = transform.Find("Info").GetComponent<Text>();
okButton = transform.Find("OKButton").GetComponent<Button>();
// 确认按钮点击事件
okButton.onClick.AddListener(OKButtonClick);
gameObject.SetActive(false);
public void ShowInfo(string info)
gameObject.SetActive(true);
infoText.text = info;
public void OKButtonClick()
gameObject.SetActive(false);
// 存储用户信息(实际中不这么使用)
public class UserInfo
public string UserName;
public string Password;
public bool IsMale;
public UserInfo(string userName, string password, bool isMale)
UserName = userName;
Password = password;
IsMale = isMale;
public class GameManager
private static GameManager instance;
// 注册后的用户信息
public List<UserInfo> UserInfos = new List<UserInfo>();
public static GameManager Instance
get
if (instance == null)
instance = new GameManager();
return instance;
public UserInfo GetUserInfo(string userName)
for(int i = 0; i < UserInfos.Count; i++)
if(userName == UserInfos[i].UserName)
return UserInfos[i];
return null;
public void SaveUserInfo(UserInfo userInfo)
UserInfos.Add(userInfo);
八、Slider 组件
1. 组件功能
-
组件结构
- Slider:Slider组件
- Background:Image组件(背景图)
- Fill Area:填充区域
- Fill:Image组件(填充物)
- Handle Slider Area:触摸区域
- Handle:Image组件(触摸物体实际图片
- Slider:Slider组件
-
组件使用
private Slider slider; void Start() slider = GetComponent<Slider>(); slider.onValueChanged.AddListener(SliderOnValueChanged); void SliderOnValueChanged(float value) print(value);
九、ScrollBar 组件
1. 组件结构
- Scrollbar:Scrollbar组件
- Sliding Area:拖拽区域
- Handle:Image组件(拖拽物体的实际显示)
- Sliding Area:拖拽区域
与 Slider 组件的比较:
- Value 值:Slider组件可以自由设定最大最小值,Scrollbar的Value只能从0到1;
- Size 大小:Scrollbar有一个Size属性用来控制滚动条的大小,而Slider组件不需要这个值;
- Number Of Steps步数:Scrollbar组件中滚动条可移动的步数(0和1没有意义,2表示滚动条只能移动到两个位置即最左和最右),而Slider组件中没有这个属性。
- Slider组件一般用于音量等数值的调节,Scrollbar组件一般用于页面的上下左右拉条。
十、DropDown 组件
1. 下拉组件结构
- DropDown:DropDown组件
- Label 显示选项
- Arrow 下拉箭头
- Template 模板:一般不显示,用于创建下拉框中的内容,在下拉框中点击选项时是新创建一个游戏物体来显示的。
DropDown组件:
- Caption Text:下拉选择改动的文本
- Caption Image:下拉选择改动的图片
- Options:用于修改选择的内容
十一、Scroll View 组件
1. Mask 遮罩组件
遮罩组件,是一种可以掩盖其他元素的控件。常用于修改其他元素的外观,或限制元素的形状,Scroll View 或是圆头像都有用到遮罩功能。
Mask 遮罩组件一般添加在父物体上,它将子物体限制为父物体的形状。
2. Scroll View 滚动视图组件
- Scroll View:Scroll Rect 组件
- Viewport 视界,用到了遮罩组件,用于显示滚动视图中的内容
- Scrollbar Horizontal:水平滚动条
- Scrollbar Vertical:垂直滚动条
十二、表格布局组件
1. 表格布局组件概述
类似背包这种想要排列整齐的时候,一般就会使用 Grid Layout Group 表格布局组件。
Grid Layout Group 表格布局组件,会自动管理其下方所在其他 UI 元素的大小、位置等信息。
在层级面板中,点击右键并没有表格布局组件,想要创建表格布局,必须先创建一个空的游戏物体,然后再手动添加 Grid Layout Group.
这样只是创建了表格布局,要实现表格,需要在该游戏物体下方创建其他游戏物体来填充表格,形成类似背包的表格。
2. 主要属性
Grid Layout Group 表格布局组件
- Padding:整个表格的边距
- Cell Size :一个单元格的大小。
- Spacing:单元格间距,x代表横向间距,y代表竖向间距。
- Start Corner:行/列,第一个成员从什么位置开始
- Start Axis:横着开始还是竖着开始
- Child Alignment:子对象对齐,如果布局元素未填满所有可用空间,则应用这个对齐方式。
- Constraint:将表格限制为固定数量的行或列。
十三、UI布局
1. RectTransform 常用属性
- Pos:UI 游戏物体的 X、Y、Z坐标,但是Z轴很少使用
- Width Height:UI 游戏物体的宽和高;
- Anchors:锚点,可以让游戏物体相对于父物体来定位
- Pivot:中心点,0~1
- Rotation:旋转
- Scale:缩放
2. 锚点定位
锚点用于固定子物体在父物体中的位置,无论父物体怎样改变,子物体相对于父物体的位置都不变。
- 相对于父物体什么位置进行定位;
- 相对于父物体进行怎样的弹性拉升。
十四、Canvas 画布
1. 画布
UI 的根节点就是 Canvas,意味着 UI 游戏物体都要放在 Canvas 下,才可以得到正确的显示。
一个场景中 Canvas 是可以存在多个的,并且也可以同时生效。
Canvas 游戏物体是由三个组件组成的:
- Canvas:画布
- Canvas Scaler:画布比例
- Graphic Raycaster:射线检测
2. 相关组件
-
Canvas 组件
Render Mode:渲染模式
- Screen Space-Overlay模式:Canvas 将置于屏幕最上层,自动填充屏幕,不会被其他模式的 Canvas 或 2D/3D 物体遮挡。
- Screen Space-Camera模式:Canvas 将置于相机前方,此时在 Canvas 和相机中间的2D/3D物体将显示在UI上面,利用这一点,可以实现在UI界面展示3D模型。
- Render Camera:对应的渲染相机,也就是该 Canvas 显示在哪个 Camera 前面
- Plane Distance:Canvas 与 Camera 的距离
- World Space模式:Canvas 将作为一个游戏对象显示在3D场景内,用于显示怪物血条等。
- Event Camera:接收 UI 事件的 Camera.
Pixel Perfect:完美像素,边缘更加清晰
Sort Order:画布排序,场景中具备多个 Canvas 时才有意义。
-
Canva Scaler 组件
Canvas Scaler 也是屏幕适配的主要方式,一般通过该组件就可以完成适配。
UI Scale Mode 缩放模式:
- Constant Pixel Size 模式:固定像素大小,不论屏幕分辨率尺寸大小如何变化,像素保持原有大小不变。
- Scale Factor:缩放倍数
- Scale With Screen Size模式:屏幕自适应常用方式
- Reference Resolution:参考分辨率,进行屏幕适配,自动缩放 UI 大小时,将以此作为参考。
- Constant Pixel Size 模式:固定像素大小,不论屏幕分辨率尺寸大小如何变化,像素保持原有大小不变。
十五、UI事件
1. 事件介绍
在 Button 中,按钮的点击就是事件,但是在实际项目中,UI 并不是只有点击这一种事件,比如鼠标悬浮、鼠标按下、鼠标弹起、鼠标拖拽等等,而且 UI 本身可能也并不是一个按钮,这些事件我们通过给游戏物体创建脚本来实现。
2. 事件接口
脚本中添加 using UnityEngine.EventSystems
事件相关命名空间。
脚本需要继承以下事件接口,并实现对应方法即可。
鼠标事件接口:
IPointerClickHandler
:鼠标点击IPointerDownHandler
:鼠标按下IPointerUpHandler
:鼠标弹起IPointerEnterHandler
:鼠标进入IPointerExitHandler
:鼠标退出
拖拽事件接口
IBeginDragHandler
:开始拖拽IDragHandler
:拖拽中IEndDragHandler
:停止拖拽
小松教你手游开发unity系统模块开发Unity Assetbundle打包笔记
*最近项目更新了Unity5.5.2,顺便更新了项目的ui打包,也更新一下这边的笔记首先打包分为两部分,一部分是打包成Assetbundle包,一部分是从Assetbundle包解包出来成为可用的资源。
首先说第一部分 打包
所有资源都可以打包,甚至不是资源(一些数据)也可以打包,只要你需要。
打包出来的东西都可以直接用,一个字体,一个Texture,一个Prefab,一个场景,都是一打出来成Assetbundle包就可以直接用,但是为什么大家还是要各自开发自己的打包流程呢?
最重要的原因就是,如果不论什么都直接打包,比如你在开发两个UI界面,这两个UI界面都用到同一个Texture,同一个Font,而你又各自打包两个Prefab,这时在两个bundle包里就都会包含这个Texture和这个Font,也就是它重复存在占用了多一份内存。
所以为了解决这种问题,基本上每个项目都会在打包的时候把Texture,Atlas,Font等用到的相同资源剥离,记录,之后再打包,等到用的时候再加载回来。
这就是每个项目自己的打包流程主要的开发工作。
而现在的unity版本(我们项目用的是unity5.3.5f)提供了剥离方法,以前的unity版本需要自己剥离。等下在下面讲打包的时候两种都会讲到。(虽然你只需要用一种,为了详细 就两种都讲)
接下来讲我们项目的打包流程。
首先打包一般的Texture,Atlas都比较简单,有两个是比较特殊需要独立开发的情况
1.打包Panel(UI界面)
2.打包Scene(游戏场景)
先来说一下打包Panel
一个UI界面里你会用到很多图片,很多字体,不管是Atlas或者Texture你都需要把这些剥离开来。
打包Panel我们使用自己的剥离方式。
假设现在有一个BattleUI.Prefab
下面是打包步骤。
1.清理数据,建立文件夹(存放不同类型的文件,如Texture、Atlas)
2.PrefabUtils.Instantiate实例化这个Prefab。
3.获取这个实例化后的Prefab下的所有动态组件(这个看不同项目)
4.获取这个GameObject上挂的所有NGUI脚本。(如果有动态组件,每个动态组件也要遍历所有NGUI脚本)
5.把NGUI脚本复制下来用一个类存放并挂载这个GameObject上,如:
Public Class PRefabHierarchyInfo:MonoBehavior
{
public Transform[] transforms;
public UILabel[] uilabels;
public UIPanel[] uipanels;
public UISprite[] uisprites;
}
6.把引用到的Texture、Atlas、Font、Panel(界面)记录下来
7.把原本NGUI脚本女上引用到的Texture.Atlas全部置空
*步骤3-7的目的在于,把这个界面或者多个界面用到的Texutre、Atlas信息来出来,统一处理,不然直接打包就会一个资源存在多份
8.把步骤6找到的所有资源打包BuildPipeline.Build(如果是Atlas不用CreateGameObject,其他要用PrefabUtility.CreatePrefab())(这里还可以根据不同平台不同要求设置Atlas属性等)
9.打包Panel
*原本想写在这里,发现东西太多另起一篇文章来记录
*下面是更新新的打包ui方式链接
http://blog.csdn.net/chrisfxs/article/details/60136875
接下来是打包Scene
打包场景的目的是在于,把场景中的所有动态组件抽出来,把所有用到的Material和Shader分离并单独打包
而我们打包场景的方式选择现在unity的自动剥离方法(改AssetBundle名即会独自打包(自动分离)(用AssetImporter类,改assetImporter.assetBundleName))
下面是打包步骤:
1.清除所有AssetBundleName
2.找到项目里所有需要打包的Scene
3.找到每个Scene下的所有需要打包和需要独立打包的东西(如Material、Shader)
4.获取所有需要打包的物件的资源地址,一个场景写一个Config文件并存放到里面
5.改每个scene的AssetBundleName
6.改每个需要单独打包(剥离)的物件的AssetBundleName(如Shader、Texture、Material、GameObject(动态组件))
7.BuildPipeline.BuildAssetBundle()
打包系统会把重新改了AssetBundleName的物件剥离并独自打包,剩余的统一打成一个场景包
这样就基本完成打包工作。
接下来是解包。
加载Panel
1.加载AssetBundle(AssetBundle.LoadFromFileAsync(path))
2.放入内存
3.在打开界面的时候实例化界面类,把数据还原。如,CDialog为界面类,CPanel为这个界面的数据类,把之前打包流程6-7记录的Atlas和Font等数据重新赋值回去
加载Scene场景
1.加载Config
2.加载场景Scene到内存
3.移步实例化Scene
4.加载Shader,Texture
5.加载Material
*4.5这两个顺序不能,因为之前打包有依赖关系,加载Material之前要先加载Shader、Texture
6.加载动态组件(根据之前的Config重新摆放物体等信息)
...
以上是关于Unity基础笔记—— Unity UI系统的主要内容,如果未能解决你的问题,请参考以下文章
Unity ❉ 基础知识 ☀️| 认识Unity引擎中几种最常用 UI系统,这一篇文章就够用了!
Unity学习笔记 之 关于 Unity UI 的 Slider 的代码记录