微信小游戏_上架与性能优化_项目学习笔记

视频教程: 微信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)的组件,不进行任何渲染。

核心设计思路

  1. 最小化渲染开销:
    • 通过重写 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

image

性能优势

  1. 零网格:不生成顶点,不占用 GPU 资源
  2. 零渲染:不参与渲染管线,不产生 Draw Call
  3. 内存更少:不需要纹理、材质等资源
  4. 更高效:适合大量不可见点击区域

适用场景

  • 需要大面积的点击区域但不想渲染
  • UI 布局中需要占位但不可见的区域
  • 需要精确控制点击区域而不影响视觉效果
  • 性能敏感场景,需要大量可点击区域

注意事项

  • 必须设置 RectTransform 的大小,否则射线检测可能无效
  • 可以通过 raycastTarget 属性控制是否接收射线检测
  • 如果需要遮罩功能,可以配合 Mask 组件使用

总结:Empty4Raycast 在仅需射线检测的场景下,性能优于透明度为 0 的 Image,因为它完全不参与渲染。

二.Unity UI渲染机制深度解析

Graphic.cs

Unity UI 渲染与 Rebuild 机制简易解析


        /// <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;
            }
        }


关于SerializeFieldSerializable

在Unity中,SerializeFieldSerializable 都是用于序列化的,但用途略有不同:

[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;
}

注意事项

  1. 公有字段默认会被序列化,不需要加[SerializeField]
  2. 如果自定义类不加[Serializable],即使加了[SerializeField]也不会在Inspector显示
  3. 序列化只保存数据,不保存方法逻辑
  4. 某些类型(如Dictionary)Unity默认不支持序列化,需要特殊处理

简单总结:[Serializable]让自定义类可序列化,用[SerializeField]让非公有字段在Inspector可见。

posted @ 2025-12-30 23:19  专心Coding的程侠  阅读(3)  评论(0)    收藏  举报