必备知识点-编辑模式下让指定代码运行
在类前加入特性[ExecuteAlways],可以让该类在编辑模式下也运行
[ExecuteAlways]
public class Lesson_ExecuteAlways : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
Debug.Log("update");
}
}
需求分析
九宫格布局概念
控件位置信息类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 对齐方式枚举
/// </summary>
public enum E_Alignment_Type
{
Up,
Down,
Left,
Right,
Center,
Left_Up,
Left_Down,
Right_Up,
Right_Down
}
/// <summary>
/// 该类 是用来表示位置 计算位置相关信息的 不需要继承Mono
/// </summary>
//加特性使得能在Unity面板上看到
[System.Serializable]
public class CustomGUIPos
{
//主要是处理 空间位置相关的内容
//要完成 分辨率自适应的相关计算
//该位置信息 会用来返回给外部 用于绘制控件
//需要对它 进行计算
private Rect rPos = new Rect(0,0,100,100);
//屏幕九宫格对齐方式
public E_Alignment_Type screen_Alignment_Type = E_Alignment_Type.Center;
//控件中心对齐方式
public E_Alignment_Type control_Center_Alignment_Type = E_Alignment_Type.Center;
//偏移位置
public Vector2 pos;
//宽高
public float width = 100;
public float height = 50;
//用于计算中心点的成员变量
private Vector2 centerPos;
/// <summary>
/// 计算中心点偏移的方法
/// </summary>
private void CalcCenterPos()
{
switch (control_Center_Alignment_Type)
{
case E_Alignment_Type.Up:
centerPos.x = -width/2 ;
centerPos.y = 0 ;
break;
case E_Alignment_Type.Down:
centerPos.x = -width / 2;
centerPos.y = -height;
break;
case E_Alignment_Type.Left:
centerPos.x = 0;
centerPos.y = -height/2;
break;
case E_Alignment_Type.Right:
centerPos.x = -width;
centerPos.y = -height / 2;
break;
case E_Alignment_Type.Center:
centerPos.x = -width / 2;
centerPos.y = -height / 2;
break;
case E_Alignment_Type.Left_Up:
centerPos.x = 0;
centerPos.y = 0;
break;
case E_Alignment_Type.Left_Down:
centerPos.x = 0;
centerPos.y = -height;
break;
case E_Alignment_Type.Right_Up:
centerPos.x = -width;
centerPos.y = 0;
break;
case E_Alignment_Type.Right_Down:
centerPos.x = -width;
centerPos.y = -height;
break;
}
}
//计算最终相对坐标位置的方法
private void CalcPos()
{
switch (screen_Alignment_Type)
{
case E_Alignment_Type.Up:
rPos.x = Screen.width/2 + centerPos.x + pos.x;
rPos.y = 0 + centerPos.y + pos.y;
break;
case E_Alignment_Type.Down:
rPos.x = Screen.width / 2 + centerPos.x + pos.x;
//为了能让pos.y为正时往上走
rPos.y = Screen.height + centerPos.y - pos.y;
break;
case E_Alignment_Type.Left:
rPos.x = 0 + centerPos.x + pos.x;
rPos.y = Screen.height/2 + centerPos.y + pos.y;
break;
case E_Alignment_Type.Right:
rPos.x = Screen.width + centerPos.x - pos.x;
rPos.y = Screen.height/2 + centerPos.y + pos.y;
break;
case E_Alignment_Type.Center:
rPos.x = Screen.width / 2 + centerPos.x + pos.x;
rPos.y = Screen.height / 2 + centerPos.y + pos.y;
break;
case E_Alignment_Type.Left_Up:
rPos.x = 0 + centerPos.x + pos.x;
rPos.y = 0 + centerPos.y + pos.y;
break;
case E_Alignment_Type.Left_Down:
rPos.x = 0 + centerPos.x + pos.x;
rPos.y = Screen.height + centerPos.y - pos.y;
break;
case E_Alignment_Type.Right_Up:
rPos.x = Screen.width + centerPos.x - pos.x;
rPos.y = 0 + centerPos.y + pos.y;
break;
case E_Alignment_Type.Right_Down:
rPos.x = Screen.width + centerPos.x - pos.x;
rPos.y = Screen.height + centerPos.y - pos.y;
break;
}
}
public Rect Pos
{
get
{
//进行计算
//计算中心点偏移
CalcCenterPos();
//宽高直接赋值 返回给外部 别人直接使用来绘制控件
rPos.width = width;
rPos.height = height;
return rPos;
}
}
}
控件父类
public enum E_Style_OnOff
{
On, Off,
}
public class CustomGUIControl : MonoBehaviour
{
//提取控件的共同表现
//位置信息
public CustomGUIPos guiPos;
//显示内容信息
public GUIContent content;
//自定义样式
public GUIStyle style;
//自定义样式是否启用的开关
public E_Style_OnOff styleOnOrOff = E_Style_OnOff.Off;
private void OnGUI()
{
switch (styleOnOrOff)
{
case E_Style_OnOff.On:
StyleOnDraw();
break;
case E_Style_OnOff.Off:
StyleOffDraw();
break;
default:
break;
}
}
protected virtual void StyleOnDraw()
{
GUI.Button(guiPos.Pos, content, style);
}
protected virtual void StyleOffDraw()
{
GUI.Button(guiPos.Pos, content);
}
}
由此实现了自适应分辨率,但是目前还有问题,当同时绘制多个按钮时,按钮层级无法确定,接下来要解决这个问题。
所见及所得以及控制绘制顺序
解决2个问题:
1.所见即所得(编辑模式下能看到GUI)(使用特性ExecuteAlways)
2.可以控制控件得绘制顺序(前面的子对象先绘制)
创建CustomGUIRoot脚本
新建root对象作为父对象,使用ExecuteAlways特性使所见即所得
修改CustomGUIControl中的OnGUI为DrawGUI,作为提供给外部绘制GUI的方法
补充CustomGUIRoot脚本
[ExecuteAlways]
public class CustomGUIRoot : MonoBehaviour
{
//用于存储 子对象 所有GUI控件的容器
private CustomGUIControl[] allControls;
void Start()
{
allControls = this.GetComponentsInChildren<CustomGUIControl>();
}
//在这统一绘制子对象控件的内容
private void OnGUI()
{
//通过每一次绘制之前 得到所有子对象控件的 父类脚本
//这句代码 浪费性能 因为每次 gui 都会来获取所有的 控件对应的脚本
//allControls = this.GetComponentsInChildren<CustomGUIControl>();
//这样子只有编辑状态下一直运行
if (!Application.isPlaying)
{
allControls = this.GetComponentsInChildren<CustomGUIControl>();
}
//遍历每一个控件 让其 执行绘制 解决层级问题,后面的对象脚本后执行
for(int i = 0; i < allControls.Length; i++)
{
allControls[i].DrawGUI();
}
}
}
自定义常用控件
修改CustomGUIControl为抽象类,同时虚方法改为抽象方法。(父类不直接调用)
自定义文本和按钮控件
自定义文本
public class CustomGUILabel : CustomGUIControl
{
protected override void StyleOffDraw()
{
GUI.Label(guiPos.Pos, content);
}
protected override void StyleOnDraw()
{
GUI.Label(guiPos.Pos, content,style);
}
}
挂载到对象上后,可以设置为预设体prefab
自定义按钮
public class CustomGUIButton : CustomGUIControl
{
//提供给外部 用于响应 按钮点击的事件 只要在外部给予响应函数 就会执行
public event UnityAction clickEvent;
protected override void StyleOffDraw()
{
if (GUI.Button(guiPos.Pos, content))
{
//?调用 它会判断这个是否为空,空的就不会调用
clickEvent?.Invoke();
}
}
protected override void StyleOnDraw()
{
if (GUI.Button(guiPos.Pos, content,style))
{
clickEvent?.Invoke();
}
}
}
自定义多选框
public class CustomGUIToggle : CustomGUIControl
{
public bool isSel;
public event UnityAction<bool> changeValue;
//一直调用太浪费性能,因此用这个判断有无变化
private bool isOldSel;
protected override void StyleOffDraw()
{
isSel = GUI.Toggle(guiPos.Pos, isSel, content);
if(isOldSel != isSel)
{
changeValue?.Invoke(isSel);
isOldSel = isSel;
}
}
protected override void StyleOnDraw()
{
isSel = GUI.Toggle(guiPos.Pos, isSel, content,style);
if (isOldSel != isSel)
{
changeValue?.Invoke(isSel);
isOldSel = isSel;
}
}
}
自定义单选框
public class CustomGUIToggleGroup : MonoBehaviour
{
public CustomGUIToggle[] toggles;
public CustomGUIToggle frontTurTog;
void Start()
{
if(toggles.Length == 0)
{
return;
}
//通过遍历来为多选框 添加 监听事件函数
//在函数中处理
//当一个为true时,另外两个变为 false
for(int i = 0; i < toggles.Length; i++)
{
CustomGUIToggle toggle = toggles[i];
toggle.changeValue += (value) =>
{
//当传入的 value 是 true时 需要把另外两个
//变成false
if (value)
{
//意味着另外两个要变成false
for (int j = 0; j < toggles.Length; j++)
{
//这里有闭包 toggle就是上一个函数中申明的变量
//改变了它的生命周期
if (toggles[j] != toggle)
{
toggles[j].isSel = false;
}
}
//记录上一次为true的toggle
frontTurTog = toggle;
}
//来判断 当前变成false的这个toggle是不是上一次为true
//如果是 就不应该让它变成false 因为单选框必须有一个选择
else if(toggle == frontTurTog)
{
//强制改成true
toggle.isSel = true;
}
};
}
}
}
自定义输入框和拖动条控件
输入框
public class CustomGUIInput : CustomGUIControl
{
public event UnityAction<string> textChange;
private string oldStr = "";
protected override void StyleOffDraw()
{
content.text = GUI.TextField(guiPos.Pos, content.text);
if (oldStr != content.text)
{
textChange?.Invoke(content.text);
oldStr = content.text;
}
}
protected override void StyleOnDraw()
{
content.text = GUI.TextField(guiPos.Pos, content.text,style);
if (oldStr != content.text)
{
textChange?.Invoke(content.text);
oldStr=content.text;
}
}
}
拖动条
public enum E_Slider_Type
{
Horizontal,
Vertical,
}
public class CustomGUISlider : CustomGUIControl
{
//最小值
public float minValue = 0;
//最大值
public float maxValue = 1;
//当前值
public float nowValue = 0;
//水平还是竖直样式
public E_Slider_Type type = E_Slider_Type.Horizontal;
//小按钮style
public GUIStyle styleThumb;
public event UnityAction<float> changeValue;
private float oldValue = 0;
protected override void StyleOffDraw()
{
switch (type)
{
case E_Slider_Type.Horizontal:
nowValue = GUI.HorizontalSlider(guiPos.Pos, nowValue, minValue, maxValue);
break;
case E_Slider_Type.Vertical:
nowValue = GUI.VerticalSlider(guiPos.Pos, nowValue, minValue, maxValue);
break;
default:
break;
}
if(oldValue != nowValue)
{
changeValue?.Invoke(nowValue);
oldValue = nowValue;
}
}
protected override void StyleOnDraw()
{
switch (type)
{
case E_Slider_Type.Horizontal:
//一个条style 一个按钮
nowValue = GUI.HorizontalSlider(guiPos.Pos, nowValue, minValue, maxValue,style,styleThumb);
break;
case E_Slider_Type.Vertical:
nowValue = GUI.VerticalSlider(guiPos.Pos, nowValue, minValue, maxValue,style,styleThumb);
break;
default:
break;
}
if (oldValue != nowValue)
{
changeValue?.Invoke(nowValue);
oldValue = nowValue;
}
}
}
自定义图片绘制
public class CustomGUITexture : CustomGUIControl
{
//图片绘制的缩放模式
public ScaleMode scaleMode = ScaleMode.StretchToFill;
protected override void StyleOffDraw()
{
GUI.DrawTexture(guiPos.Pos, content.image, scaleMode);
}
protected override void StyleOnDraw()
{
GUI.DrawTexture(guiPos.Pos, content.image, scaleMode);
}
}