微信小游戏_上架与性能优化_项目学习笔记
视频教程: 微信300个怪同屏 11 Draw call 非ECS架构,钓鱼小游戏
** 持续更新中......**
一.一个仅用于接收射线检测(Raycast)的组件Empty4Raycast,不进行任何渲染。
它继承自 MaskableGraphic,专门用于创建仅支持射线检测(Raycast)而不进行渲染的UI区域。
// Empty4Raycast 继承链
Empty4Raycast → MaskableGraphic → Graphic → UIBehaviour
Empty4Raycast.cs
using UnityEngine;
using System.Collections;
namespace UnityEngine.UI
{
public class Empty4Raycast : MaskableGraphic
{
protected Empty4Raycast()
{
useLegacyMeshGeneration = false;
}
protected override void OnPopulateMesh(VertexHelper toFill)
{
toFill.Clear();
}
}
}
Empty4Raycast 的用途
Empty4Raycast 是一个仅用于接收射线检测(Raycast)的组件,不进行任何渲染。
核心设计思路
- 最小化渲染开销:
- 通过重写 OnPopulateMesh 并调用 toFill.Clear(),确保不生成任何网格数据
- 完全不参与UI渲染管线,从根源上避免渲染开销
2.保留射线检测功能:
- 继承 MaskableGraphic 获得完整的射线检测能力
- 支持遮罩、裁剪等UI交互特性
--
-/*
使用场景
// 典型用法:创建一个不可见的点击区域
GameObject clickArea = new GameObject("ClickArea");
RectTransform rectTransform = clickArea.AddComponent<RectTransform>();
Empty4Raycast empty = clickArea.AddComponent<Empty4Raycast>();
// 设置大小和位置
rectTransform.sizeDelta = new Vector2(100, 100);
rectTransform.anchoredPosition = Vector2.zero;
// 现在这个区域可以接收点击事件,但不会渲染任何内容
性能对比:Empty4Raycast vs 透明度为 0 的 Image

性能优势
- 零网格:不生成顶点,不占用 GPU 资源
- 零渲染:不参与渲染管线,不产生 Draw Call
- 内存更少:不需要纹理、材质等资源
- 更高效:适合大量不可见点击区域
适用场景
- 需要大面积的点击区域但不想渲染
- UI 布局中需要占位但不可见的区域
- 需要精确控制点击区域而不影响视觉效果
- 性能敏感场景,需要大量可点击区域
注意事项
- 必须设置
RectTransform的大小,否则射线检测可能无效 - 可以通过
raycastTarget属性控制是否接收射线检测 - 如果需要遮罩功能,可以配合
Mask组件使用
总结:Empty4Raycast 在仅需射线检测的场景下,性能优于透明度为 0 的 Image,因为它完全不参与渲染。
二.Unity UI渲染机制深度解析
Graphic.cs
/// <summary>
/// Rebuilds the graphic geometry and its material on the PreRender cycle.
///在预渲染阶段重新构建图形几何结构及其材质。
/// </summary>
/// <param name="update">The current step of the rendering CanvasUpdate cycle.</param>
///当前处于渲染 CanvasUpdate 阶段的步骤。
/// <remarks>
/// See CanvasUpdateRegistry for more details on the canvas update cycle.
/// 有关画布更新阶段的更多详细信息,请参阅 canvas update cycle.
/// </remarks>
///UI Rebuild是Unity UI系统更新渲染内容的过程,主要分为:
// 1. 布局重建(Layout Rebuild)
// 当UI元素位置、大小变化时触发
// 2. 图形重建(Graphic Rebuild)
// 当UI元素视觉属性变化时触发
public virtual void Rebuild(CanvasUpdate update)
{
if (canvasRenderer == null || canvasRenderer.cull)
return;
switch (update)
{
case CanvasUpdate.PreRender:
if (m_VertsDirty)
{
UpdateGeometry();
m_VertsDirty = false;
}
if (m_MaterialDirty)
{
UpdateMaterial(); // 这里会调用OnPopulateMesh
m_MaterialDirty = false;
}
break;
}
}
关于SerializeField 和 Serializable
在Unity中,SerializeField 和 Serializable 都是用于序列化的,但用途略有不同:
[SerializeField] (属性)
作用: 让私有或受保护的字段在Unity Inspector中可见且可编辑。
public class Example : MonoBehaviour
{
[SerializeField]
private int health = 100; // 私有字段,但会在Inspector显示
[SerializeField]
protected float speed = 5f; // 受保护字段,也会在Inspector显示
public int publicField; // 公有字段,默认就会在Inspector显示
}
特点:
- 只用于字段(变量)
- 让非公有字段在Inspector面板中可编辑
- 字段的值会被保存到场景/预制件中
[Serializable] (特性)
作用: 让自定义类或结构体可以被Unity序列化。
// 1. 标记自定义类为可序列化
[Serializable]
public class ItemData
{
public string itemName;
public int value;
public Sprite icon;
}
public class Inventory : MonoBehaviour
{
// 2. 现在这个自定义类可以在Inspector中显示和编辑
[SerializeField]
private ItemData[] items;
// 3. 也可以用在List中
[SerializeField]
private List<ItemData> itemList = new List<ItemData>();
}
特点:
- 用于类、结构体、枚举
- 让自定义类型可以被Unity的序列化系统处理
- 通常与
SerializeField配合使用
主要区别对比
| 特性 | [SerializeField] |
[Serializable] |
|---|---|---|
| 用途 | 序列化单个字段 | 序列化整个类/结构体 |
| 目标 | 字段(变量) | 类、结构体、枚举 |
| 常见场景 | 让私有字段在Inspector可见 | 让自定义类型能在Inspector中编辑 |
| 是否必需 | 可选,用于非公有字段 | 必需,用于自定义类型 |
一起使用的例子
[Serializable]
public class WeaponStats
{
public float damage;
public float fireRate;
public int maxAmmo;
}
public class Player : MonoBehaviour
{
// 公有字段 - 自动在Inspector显示
public string playerName;
// 私有字段 + SerializeField - 在Inspector显示
[SerializeField]
private int health = 100;
// 自定义类 + Serializable + SerializeField
[SerializeField]
private WeaponStats currentWeapon;
// 列表中的自定义类型
[SerializeField]
private List<WeaponStats> availableWeapons;
}
注意事项
- 公有字段默认会被序列化,不需要加
[SerializeField] - 如果自定义类不加
[Serializable],即使加了[SerializeField]也不会在Inspector显示 - 序列化只保存数据,不保存方法逻辑
- 某些类型(如Dictionary)Unity默认不支持序列化,需要特殊处理
简单总结:用[Serializable]让自定义类可序列化,用[SerializeField]让非公有字段在Inspector可见。

浙公网安备 33010602011771号