【组件初始化链条】简化Unity组件的初始化

简介

在游戏脚本中我们通过借助GetComponent或TryGetComponent方法获取组件,所以当需要获取较多组件时,我们不可避免地要书写一些重复代码,为了提升代码简洁程度,简化组件初始化逻辑,本文以"组件初始化链条"为核心探索组件的初始化。

我们对于组件初始化面临以下几个问题:

1.当需要从同一个游戏对象上获取不同组件时,如何简化?

2.当获取组件时还需要对组件进行初始化或者获取组件的字段或属性,如何简化?

3.当需要从多个游戏对象上获取相同类型组件时,如何简化?

事实上,组件获取的逻辑大同小异,主要依赖于GetComponent或TryGetComponent方法,所以我们只需要关注从哪个GameObject 上获取哪个Component 即可,如果涉及到对组件进行一些自定义的处理,我们则可以借助委托类型的参数。除此之外,对于当前脚本而言,所需要的组件往往是必要的,缺一不可,所以我们仅关注所有组件是否被正确初始化,通过"组件初始化链条"则可以获取一个总的初始化结果值,如果需要自定义初始化结果值的收集可以自行改进本文代码。

相较于手动初始化,我们需要重复书写GetComponent或TryGetComponent方法的调用,并且需要手动对每个组件的初始化结果值进行收集,这并不利于开发者专注于游戏逻辑的开发。

采用统一的组件初始化方式,有利于团队协作,提升整体开发效率,方便组件初始化相关Bug的检测和处理。

代码示例

ComponentInitChain.cs

cs 复制代码
using System;
using UnityEngine;

/// <summary>
/// 组件初始化链条
/// </summary>
public class ComponentInitChain
{
    private GameObject gameObject;

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="gameObject">组件所挂载的游戏对象</param>
    public ComponentInitChain(GameObject gameObject)
    {
        this.gameObject = gameObject;
    }

    /// <summary>
    /// 初始化组件
    /// </summary>
    /// <typeparam name="T">组件类型</typeparam>
    /// <param name="component">组件的引用变量</param>
    /// <returns>当前组件初始化链条实例</returns>
    public ComponentInitChain InitComponent<T>(out T component) where T : Component
    {
        if (gameObject == null || !gameObject.TryGetComponent(out component)) component = null;
        return this;
    }

    /// <summary>
    /// 初始化组件
    /// </summary>
    /// <typeparam name="T">组件类型</typeparam>
    /// <param name="component">组件的引用变量</param>
    /// <param name="componentDeal">组件获取后的初始化处理</param>
    /// <returns>当前组件初始化链条实例</returns>
    public ComponentInitChain InitComponent<T>(out T component, Action<T> componentDeal) where T : Component
    {
        if (gameObject == null || !gameObject.TryGetComponent(out component)) component = null;
        if (component != null) componentDeal(component);
        return this;
    }

    /// <summary>
    /// 初始化组件
    /// </summary>
    /// <typeparam name="T0">组件类型0</typeparam>
    /// <typeparam name="T1">组件类型1</typeparam>
    /// <param name="component">T0类型组件的引用变量</param>
    /// <param name="component1">T1类型组件的引用变量</param>
    /// <returns>初始化结果值,组件均正确初始化则返回true,否则返回false</returns>
    public bool InitComponent<T0, T1>(out T0 component, out T1 component1) where T0 : Component where T1 : Component
    {
        InitComponent(out component).InitComponent(out component1);
        if (component == null || component1 == null) return false;
        return true;
    }

    /// <summary>
    /// 初始化组件
    /// </summary>
    /// <typeparam name="T0">组件类型0</typeparam>
    /// <typeparam name="T1">组件类型1</typeparam>
    /// <typeparam name="T2">组件类型2</typeparam>
    /// <param name="component">T0类型组件的引用变量</param>
    /// <param name="component1">T1类型组件的引用变量</param>
    /// <param name="component2">T2类型组件的引用变量</param>
    /// <returns>初始化结果值,组件均正确初始化则返回true,否则返回false</returns>
    public bool InitComponent<T0, T1, T2>(out T0 component, out T1 component1, out T2 component2)
    where T0 : Component where T1 : Component where T2 : Component
    {
        InitComponent(out component).InitComponent(out component1).InitComponent(out component2);
        if (component == null || component1 == null || component2 == null) return false;
        return true;
    }

    /// <summary>
    /// 初始化组件
    /// </summary>
    /// <typeparam name="T0">组件类型0</typeparam>
    /// <typeparam name="T1">组件类型1</typeparam>
    /// <typeparam name="T2">组件类型2</typeparam>
    /// <typeparam name="T3">组件类型3</typeparam>
    /// <param name="component">T0类型组件的引用变量</param>
    /// <param name="component1">T1类型组件的引用变量</param>
    /// <param name="component2">T2类型组件的引用变量</param>
    /// <param name="component3">T3类型组件的引用变量</param>
    /// <returns>初始化结果值,组件均正确初始化则返回true,否则返回false</returns>
    public bool InitComponent<T0, T1, T2, T3>(out T0 component, out T1 component1, out T2 component2, out T3 component3)
    where T0 : Component where T1 : Component where T2 : Component where T3 : Component
    {
        InitComponent(out component).InitComponent(out component1).InitComponent(out component2).InitComponent(out component3);
        if (component == null || component1 == null || component2 == null || component3 == null) return false;
        return true;
    }
}

ComponentsInitChain.cs

cs 复制代码
using System.Linq;
using UnityEngine;

/// <summary>
/// 组成集初始化链条
/// </summary>
public class ComponentsInitChain
{
    /// <summary>
    /// 初始化组件集
    /// </summary>
    /// <typeparam name="T">组件类型</typeparam>
    /// <param name="components">组件集的引用变量</param>
    /// <param name="gameObjects">组件所挂载的游戏对象集</param>
    /// <returns>组件集初始化结果值,若所有组件成功初始化则返回true,否则返回false</returns>
    public static bool InitComponents<T>(in T[] components, params GameObject[] gameObjects) where T : Component
    {
        if (components == null || components.Length == 0) return false;
        if (gameObjects == null || gameObjects.Length == 0) return false;
        if (components.Length == gameObjects.Length)
        {
            for (int i = 0; i < gameObjects.Length; i++)
            {
                components[i] = gameObjects[i].GetComponent<T>();
            }
            return components.All(c => c != null);
        }
        return false;
    }

    /// <summary>
    /// 初始化组件集
    /// </summary>
    /// <typeparam name="T0">组件类型0</typeparam>
    /// <typeparam name="T1">组件类型1</typeparam>
    /// <param name="component">T0类型组件的引用变量</param>
    /// <param name="component1">T1类型组件的引用变量</param>
    /// <param name="gameObjects">T0、T1类型组件所挂载的游戏对象</param>
    /// <returns>组件初始化结果值,若所有组件成功初始化则返回true,否则返回false</returns>
    public static bool InitComponents<T0, T1>(out T0 component, out T1 component1, params GameObject[] gameObjects)
    where T0 : Component where T1 : Component
    {
        component = null;
        component1 = null;
        if (gameObjects?.Length == 2)
        {
            component = gameObjects[0].GetComponent<T0>();
            component1 = gameObjects[1].GetComponent<T1>();
        }
        return component != null && component1 != null;
    }

    /// <summary>
    /// 初始化组件集
    /// </summary>
    /// <typeparam name="T0">组件类型0</typeparam>
    /// <typeparam name="T1">组件类型1</typeparam>
    /// <typeparam name="T2">组件类型2</typeparam>
    /// <param name="component">T0类型组件的引用变量</param>
    /// <param name="component1">T1类型组件的引用变量</param>
    /// <param name="component2">T2类型组件的引用变量</param>
    /// <param name="gameObjects">T0、T1、T2类型组件所挂载的游戏对象</param>
    /// <returns>组件初始化结果值,若所有组件成功初始化则返回true,否则返回false</returns>
    public static bool InitComponents<T0, T1, T2>(out T0 component, out T1 component1, out T2 component2, params GameObject[] gameObjects)
    where T0 : Component where T1 : Component where T2 : Component
    {
        component = null;
        component1 = null;
        component2 = null;
        if (gameObjects?.Length == 3)
        {
            component = gameObjects[0].GetComponent<T0>();
            component1 = gameObjects[1].GetComponent<T1>();
            component2 = gameObjects[2].GetComponent<T2>();
        }
        return component != null && component1 != null && component2 != null;
    }

    /// <summary>
    /// 初始化组件集
    /// </summary>
    /// <typeparam name="T0">组件类型0</typeparam>
    /// <typeparam name="T1">组件类型1</typeparam>
    /// <typeparam name="T2">组件类型2</typeparam>
    /// <typeparam name="T3">组件类型3</typeparam>
    /// <param name="component">T0类型组件的引用变量</param>
    /// <param name="component1">T1类型组件的引用变量</param>
    /// <param name="component2">T2类型组件的引用变量</param>
    /// <param name="component3">T3类型组件的引用变量</param>
    /// <param name="gameObjects">T0、T1、T2、T3类型组件所挂载的游戏对象</param>
    /// <returns>组件初始化结果值,若所有组件成功初始化则返回true,否则返回false</returns>
    public static bool InitComponents<T0, T1, T2, T3>(out T0 component, out T1 component1, out T2 component2, out T3 component3, params GameObject[] gameObjects)
    where T0 : Component where T1 : Component where T2 : Component where T3 : Component
    {
        component = null;
        component1 = null;
        component2 = null;
        component3 = null;
        if (gameObjects?.Length == 4)
        {
            component = gameObjects[0].GetComponent<T0>();
            component1 = gameObjects[1].GetComponent<T1>();
            component2 = gameObjects[2].GetComponent<T2>();
            component3 = gameObjects[3].GetComponent<T3>();
        }
        return component != null && component1 != null && component2 != null && component3 != null;
    }
}

测试代码

cs 复制代码
using UnityEngine;

public class Test : MonoBehaviour
{
    public GameObject[] GameObjects;
    private AudioListener audioListener;
    private SpriteRenderer spriteRenderer;
    private CircleCollider2D circleCollider2D;
    private Room[] rooms;
    private Room a;
    private Room b;
    private Room c;
    private Room d;
    private Sprite sprite;

    private void Awake()
    {
        // 组件链式初始化,适用于该情景"需要获取同一个游戏对象上较多的组件且针对不同组件存在不同的初始化逻辑"
        // new ComponentInitChain(gameObject).InitComponent(out audioListener)
        // .InitComponent(out spriteRenderer, c => sprite = c.sprite)
        // .InitComponent(out circleCollider2D, c => c.isTrigger = true);

        // 组件单步初始化,适用于该情景"需要获取同一个游戏对象上较少的组件,不需要对各组件的初始化进行代理"
        // bool res = new ComponentInitChain(gameObject).InitComponent(out audioListener, out spriteRenderer, out circleCollider2D);
        // Debug.Log(res);
        // Debug.Log(audioListener);
        // Debug.Log(spriteRenderer);
        // Debug.Log(circleCollider2D);

        // 获取不同游戏对象上的某种组件类型的组件合集
        // rooms = new Room[GameObjects.Length];
        // bool res = ComponentsInitChain.InitComponents(rooms, GameObjects);
        // Debug.Log(res);
        // for (int i = 0; i < rooms?.Length; i++)
        // {
        //     Debug.Log(rooms[i]);
        // }

        // 获取不同游戏对象上的相同类型组件
        // bool res = ComponentsInitChain.InitComponents(out a, out b, out c, out d, GameObjects);
        // Debug.Log(res);
        // Debug.Log(a);
        // Debug.Log(b);
        // Debug.Log(c);
        // Debug.Log(d);
    }
}

代码说明

ComponentInitChain适用于对单个游戏对象的组件进行初始化的情景,所以在实例化时需要传递对应的游戏对象,后续的组件初始化操作都针对该游戏对象。

ComponentsInitChain适用于对多个游戏对象的组件进行初始化的情景,相关方法都属于静态方法,所以调用方法时需要传递游戏对象。

测试代码中则针对四种常见的应用情况进行举例。

如果这篇文章对你有帮助,请给作者点个赞吧!

相关推荐
℡枫叶℡17 小时前
Unity - 图集大小与内存占用
unity·图集内存
_ZHOURUI_H_18 小时前
Unity MyFramework 用法说明(二十五):使用 AtlasManager 统一管理图集与 Sprite 引用
ui·unity·游戏引擎·unity3d·游戏开发
新手unity自用笔记19 小时前
Unity网络基础_2
unity·游戏引擎
音视频牛哥20 小时前
从数字孪生到机器人操控:Android Unity3D下RTMP/RTSP多路低延迟播放实践
android·unity·音视频·unity rtsp播放器·unity rtmp播放器·rtsp player·rtmp player
派葛穆21 小时前
Unity-原生 JsonUtility 实现 JSON 与TMP UI 交互实战
unity
chenzhou__1 天前
独立游戏开发日志 ①:从信号博弈到涂色对战——一次玩法重构始
笔记·后端·学习·unity·go·独立游戏
五仁烧饼1 天前
Unity Addressables 资源依赖治理:规则约束、自动修复与全链路防护
java·unity·游戏引擎
qq_370773092 天前
Unity游戏xLua逆向实战:从SO版本识别到Lua脚本热替换全链路
游戏·unity·lua
mxwin2 天前
Unity Shader 水面 Shader 教程 从 Gerstner 波浪到屏幕折射、焦散、泡沫的完整实现
unity·游戏引擎
噗噗夹的TA之旅2 天前
Shader 学习 21:自定义 Render Feature
学习·游戏·unity·c#·游戏引擎·图形渲染