Unity-进阶知识

ooowl
  • 游戏开发
  • unity
About 9 min

Unity-进阶知识

GUI控件

UI控件主要学习三个事情:UI控件使用,事件响应,分辨率自适应

GUI

IMGUIopen in new window(Immediate Mode GUI即时模式图形用户界面)一般简称GUI。是一个代码驱动的UI系统,不要用它为玩家制作UI功能,GUI主要用于开发和调试阶段创建游戏内调试工具在运行时检查和调整游戏状态;制作Unity的拓展工具和编辑器UI。比如地图编辑器,技能编辑器,资源打包自定义等。
在 MonoBehaviuor中有一个特殊的函数void OnGUI在里面写GUI相关的代码,类似于生命周期每帧执行,在OnDisable和LateUpdate之间执行,这意味着Inspector修改参数会有即时反馈。
Texture的缩放比例会始终保持一致,一般把这东西放出来都是为了编辑,权限public就行。 GUI的画布原点(0,0)是在左上角
所有控件必传的是位置信息内容

  • 使用 Rect 参数来指定控件的位置和尺寸。Rect 包含 xy 位置以及 widthheight 尺寸
  • 内容可以点进函数里面看,大部分都接受GUIContent比如下面的btncontent
  • 选传 GUIStyle,可以先声明一个GUIStyle对象,就可以编辑CSS啦😄(哈哈兜兜转转又回到web 按钮只有在按钮内部进行一次按下和点击才算点击。只能按一次,但是RepeatButton可以长按一直触发(竟然用if来判断我为什么感觉这方式有点沙币
    使用GUIStyle 中的 FixedWidth FixedHeight去修改图片的大小和响应区域,不要改Rect
    Normal(false)/OnNormal(true)修改是否选中的状态,单选可以自己做出来,注意看在一个循环返回中怎么处理的
    窗口必须 赋值在函数中DragWindow才能拖动!!!DragWindow可以确定可以拖动的范围
Click to see more
public class IMGUI : MonoBehaviour  
{  
    // 测试文本  
    public GUIContent content;  
    public Rect rect;  
    public Rect rect1;  
    public Texture tex;  
    // 测试按钮  
    public Rect btnrect=new Rect(600,800,100,100);  
    public GUIContent btncontent;  
  
    // toggle选项框  
    private bool isSealed0 = false;  
    private bool isSealed1 = false;  
    private int isSealed3 = 1;  
    public GUIStyle style1;  
    //  文字和密码  
    public string inputString="请接收输入";  
    public string inputStringPasswd="输入密码";  
    //  滑动条  
    public float nowValue=5f,minVlaue=0f,maxVlaue=10f;  
    // 直接画个图  
    public Texture2D demoTexture;  
    //  多选工具栏(按钮)  
    public int toolbarIndex = 0;  
    private string[] toolbarNames=new string[]{"选项一","选项二","选项三"};  
    public int gridIndex = 0;  
    private string[] gridNames=new string[]{"选项一","选项二","选项三","选项2一","选项2二","选项2三"};  
    private string[] demogride =new string[]{"选项一","选项二","选项三","选项2一","选项2二","选项2三","选项3一","选项3二","选项3三","选项4一","选项4二","选项4三"};  
    //  遮罩窗口  
    public Rect groupos = new Rect(50, 660, 200, 200);  
    //  滑动条窗口  
    public Rect scPos=new Rect();  
    public Vector2 nowPos=new Vector2();  
    public Rect showPos=new Rect();  
    //  窗口相关  
    public int windowUid = 1;  
    public Rect windowPos=new Rect(50,800,200,200);  
    private void OnGUI()  
    {   //  
        GUI.Label(new Rect(0,0,100,20),"烤全羊啊嗯"); //  使用Label绘制  
        GUI.Label(rect,tex);  
        content.text = "打组一块显示";  
        content.image = tex;  
        content.tooltip = "中嘞!"; //  鼠标移动上去显示  
        GUI.Label(rect1,content); // 可以给GUI组件设置tooltip  
        // 鼠标移上去的时候就可以直接显示tooltip,默认是空字符串,他会一直打印很烦  
        if(GUI.tooltip!="") Debug.Log(GUI.tooltip);  
        btncontent.text = "按钮哎";  
        if (GUI.Button(btnrect, btncontent)) // 这按钮只能按一次,但是RepeatButton可以长按一直触发  
        {  
            print("你们这是什么按钮啊,你们这个按钮害人不浅!");  
        }  
        // toggle选项框  
        isSealed0 = GUI.Toggle(new Rect(0, 0, 100, 30),isSealed0, "效果开关0");  
        isSealed1 = GUI.Toggle(new Rect(300, 300, 100, 30),isSealed1, "效果开关1",style1); // rect 的宽高是toggle的响应位置  
  
        if(GUI.Toggle(new Rect(50, 80, 100, 30),isSealed3==1, "效果开关3-0"))isSealed3 = 1;  
        if(GUI.Toggle(new Rect(50, 100, 100, 30),isSealed3==2, "效果开关3-1"))isSealed3 = 2;  
        if(GUI.Toggle(new Rect(50, 120, 100, 30),isSealed3==3, "效果开关3-2"))isSealed3 = 3;  
        if(GUI.Toggle(new Rect(50, 140, 100, 30),isSealed3==4, "效果开关3-3"))isSealed3 = 4;  
  
        //  文字和密码  
        inputString = GUI.TextField(new Rect(50, 160, 100, 30), inputString);  
        inputStringPasswd = GUI.PasswordField(new Rect(50, 200, 100, 30), inputStringPasswd,'星');// 接收密码字段  
        //  滑动条  
        nowValue = GUI.HorizontalSlider(new Rect(50, 250, 100, 30), nowValue, minVlaue, maxVlaue);  
        // 直接画个图  
        // 后面是 ScaleToFit 计算宽高,等比例缩放不裁剪 StretchToFill 无视比例拉伸 ScaleAndCrop 等比例缩放会裁剪  
        // imageAspect 自定义缩放宽高比  
        GUI.DrawTexture(new Rect(50, 350, 100, 100),demoTexture,scaleMode:ScaleMode.StretchToFill,alphaBlend:false,imageAspect: 10);  
        GUI.Box(new Rect(50, 400, 100, 100),"这是个盒子,一个非常单纯的盒子");  
        //  多选工具栏(按钮)  
        toolbarIndex=GUI.Toolbar(new Rect(50, 500, 200, 50),toolbarIndex,toolbarNames); // 需要不断赋值,可以使用switch case赋值  
        gridIndex=GUI.SelectionGrid(new Rect(50, 560, 200, 50), gridIndex, gridNames,3); // 就是满了会自动换行  
        //  相当于html里的遮罩,只显示窗口位置  
        GUI.BeginGroup(groupos); // Rect  
        GUI.Button(new Rect(0, 0, 80, 60),"你们这个按钮也害人不浅啊");  
        GUI.EndGroup();  
  
        //  Rect position组件的位置大小 Vector2 scrollPosition滑动到哪了 Rect viewRect 内容的范围大小  
        nowPos=GUI.BeginScrollView(scPos,nowPos,showPos);  
        GUI.SelectionGrid(new Rect(0, 0, 200, 50), 0, demogride,4); // 注意位置已经是相对位置了  
        GUI.EndScrollView();  
  
        //  窗口相关,必须 赋值 且 在函数中DragWindow才能拖动!!!  
        windowPos=GUI.Window(windowUid, windowPos, DrawWindow,"窗口标题"); // 会调用函数里面的东西进行绘制  
        GUI.ModalWindow(windowUid+1, windowPos, DrawWindow,"窗口标题"); // 相当于获取了焦点的窗口,相当于alert了  
    }  
    private void DrawWindow(int windowID) //  实际上这东西绑在了一个委托上  
    {  
        //  可以通过windowID来判断不同窗口的处理逻辑  
        GUI.RepeatButton(new Rect(10, 10, 40, 50), "按钮按钮");  
        if (windowUid == 1) // 给第一个窗口用的  
        {   // 允许拖动这一步也是必须的  
            GUI.DragWindow(new Rect(0,0,1000,20)); // 允许拖动哪一块范围,默认全能拖  
        }  
    }   
}

📌Tip

GUI控制显示隐藏,可以写在一个脚本中,用变量控制启用;或者挂在对象上,控制脚本是否启用;或者控制对象是是否启用。
一般是控制对象,高级一点的UI框架都是这么封装的

GUILayout

GUILayout 可以用来写顶栏工具和Inspector不太适合直接写UI 设置全局颜色GUI.color然后再设置文本颜色GUI.contentColorGUI.backgroundColor这两个颜色会相乘好怪,就这仨颜色。 颜色会跨脚本影响看执行顺序
右键创建GUI Skin 可以直接读取预设的主题,使用GUISkin skin来创建,赋值创建的GUISkin文件就可以了。绘制时使用了GUIStyle会优先使用指定的样式。

Click to see more
private void OnGUI()  
{  
    //  全局颜色  
    GUI.color = new Color(1f,0f,0f,1f); // RGBA颜色  
    GUI.Label(new Rect(150,100,100,100),"This is red text");  
    // GUI.color = Color.white; // 白色重置颜色  
    GUI.skin = null; // 规定接下来绘制使用GUISkin  
    // GUILayout 可以用来写顶栏工具和Inspector  
    GUILayout.Button("按钮1哦",GUILayout.ExpandWidth(false)); // GUILayout.Width(300);在此控件上失效  
    GUILayout.Button("按钮2哦");  
    GUILayout.Button("按钮3哦");  
    GUILayout.BeginVertical(); // 布局控制  
    GUILayout.EndVertical();  
    //  GUILayoutOption提供的选项  
    // 控件的固定宽高  
    GUILayout.Width(300);  
    GUILayout.Height(200);  
    // 允许控件的最小宽高  
    GUILayout.MinWidth(50);  
    GUILayout.MinHeight(50);  
    // 允许控件的最大宽高  
    GUILayout.MaxWidth(100);  
    GUILayout.MaxHeight(100);  
    // 允许或禁止水平的样式  
    GUILayout.ExpandWidth(true); // 允许水平  
    GUILayout.ExpandHeight(false); // 禁止高度  
}

IMGUI缺点是 代码控制繁琐,运行时才能看效果,不能分辨率自适应在mono类加特性[ExecuteAlways]可以让UI所见即所得 (其他的脚本其实也能用

UI血条跟随屏幕动,使用IMGUI的小框架,因为屏幕的Y轴是从左下角算起的,而IMGUI原点是从右上角算的,注意怎么转换

public Texture maxHpBG;  
public Texture hpBG;  
public Rect maxHpRect;  
public Rect hpRect;  
  
private void OnGUI()  
{  
    Vector3 screenPos = Camera.main.WorldToScreenPoint(this.transform.position);  
    screenPos.y = Screen.height - screenPos.y; // 因为屏幕的Y轴是从左下角算起的,而IMGUI原点是从右上角算的,注意怎么转换  
  
    maxHpRect.x = screenPos.x - 50;  
    maxHpRect.y = screenPos.y - 50;  
    maxHpRect.width = 100;  
    maxHpRect.height = 15;  
    // 画血条背景
    GUI.DrawTexture(maxHpRect, maxHpBG);  
  
    hpRect.x = screenPos.x - 50;  
    hpRect.y = screenPos.y - 50;  
    hpRect.width = (float)Hp / maxHP * 100f;  
    hpRect.height = 15;  
    // 画血条
    GUI.DrawTexture(hpRect, hpBG);  
  
}

NGUI

组件核心

适合做游戏的UI,作者被Unity诏安了搞了UGUI,后面还出了有UIElements但是没铺开。
NGUI创建之后自带UIRoot和一个Camera,UIRoot上的Root脚本就是自适应分辨率的基础,也是UI的画布。
UIRoot的重要属性ScalingStyle 最好是建一个场景自己试,文字仅帮助回忆。
灵活模式Flexible 常用于PC端 该模式下都是以像素为基础

  • 可以规定最大和最小高度,在此高度范围内的时候不会缩放,不在范围区间的时候等比缩放
  • ShrinkPortraitUI是在竖屏的时候按照宽度来适配,AdjustByDPI使用DPI适配,一般都会勾上

约束模式Constrained自动按照屏幕尺寸缩放匹配,高分屏上UI会被放大可能会模糊。

  • 宽高通常设置市面上常见的尺寸比例,美术也会按照这个比例做素材
  • Fit如果被勾选,则表示按照哪个进行适配,勾选的那个保持不变,但是画布会始终填满屏幕
  • 俩Fit都勾选了不会被裁剪,但是会有黑边;都不勾就会被裁剪。横屏一般是勾高,竖屏一般勾宽

Panel也是创建之后自带脚本,也是面板根对象必须要带的脚本,其实panel可以创建多个,父对象有panel子对象才能显示,可以控制子对象的渲染和行为。
EventSystem默认挂摄像机上,让摄像机能够渲染出来物体,并且能够接收到NGUI的输入事件,能响应玩家的输入。2D和3D的UI区别是摄像机的投影模式是正交还是透视,可能只有VRAR才能用到3D的UI

一般来说一个面板字体和图集用的是一样的DrawCall2个就正常,把它们一块选择,同时往上提到同一层就行

UGUI

UI设计经验

做UI其实就三件事,数据准备->拼界面->写逻辑
在UI中一般都是用一个单独的根对象避免和其他的对象混合管理增加管理难度,命名最好有规范,比如btnBegine一看就知道这是啥,写C#写多了直接大驼峰罢
先分层,业务逻辑和响应等解耦, #todo UI中通用的设计逻辑

重看需求分析,看看一个游戏需要考虑的方面

特效类的GameObject有些不会自动移除的,如果是不能自动移除就添加一个脚本``,给一个特效能播放完的时间然后移除就可以了

字体和贴图

TMP 使用位图字体 对象池 ECS

[Unity] Text Life 免费开源文字动画框架_哔哩哔哩_bilibiliopen in new window
GitHub - kuronekoyang/ReflectionTool: C# Reflection Wrapper Generation & Method Hooking Tools For Unityopen in new window

游戏是数据的一种体现,利用程序把美术和数据组织起来 从需求出发 用逻辑分析 找出功能共同点 设计游戏通用规则 配置数据表 啊啊



Last Edit: 2025-04-20 23:59:02

Loading...