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组件(文本)
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组件(触摸物体实际图片
- 组件使用
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组件(拖拽物体的实际显示)
与 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 大小时,将以此作为参考。
十五、UI事件
1. 事件介绍
在 Button 中,按钮的点击就是事件,但是在实际项目中,UI 并不是只有点击这一种事件,比如鼠标悬浮、鼠标按下、鼠标弹起、鼠标拖拽等等,而且 UI 本身可能也并不是一个按钮,这些事件我们通过给游戏物体创建脚本来实现。
2. 事件接口
脚本中添加
using UnityEngine.EventSystems
事件相关命名空间。
脚本需要继承以下事件接口,并实现对应方法即可。
鼠标事件接口:
IPointerClickHandler
:鼠标点击IPointerDownHandler
:鼠标按下IPointerUpHandler
:鼠标弹起IPointerEnterHandler
:鼠标进入IPointerExitHandler
:鼠标退出
拖拽事件接口
IBeginDragHandler
:开始拖拽IDragHandler
:拖拽中IEndDragHandler
:停止拖拽
版权归原作者 Dukenone 所有, 如有侵权,请联系我们删除。