【组件初始化链条】简化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适用于对多个游戏对象的组件进行初始化的情景,相关方法都属于静态方法,所以调用方法时需要传递游戏对象。

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

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

相关推荐
90后小陈老师9 小时前
Unity教学 项目2 2D闯关游戏
游戏·unity·游戏引擎
噗噗夹的TA之旅10 小时前
Unity Shader 学习20:URP LitForwardPass PBR 解析
学习·unity·游戏引擎·图形渲染·技术美术
nnsix10 小时前
Unity ReferenceFinder插件 多选资源查找bug解决
unity·游戏引擎·bug
gzroy11 小时前
Unity Shader Graph实现全息瞄准器
unity·游戏引擎
90后小陈老师14 小时前
Unity教学 基础介绍
unity·游戏引擎
90后小陈老师14 小时前
Unity教学 项目3 3D坦克大战
3d·unity·游戏引擎
秦奈16 小时前
Unity复习学习随笔(五):Unity基础
学习·unity·游戏引擎
nnsix17 小时前
Unity ReferenceFinder插件 窗口中选择资源时 同步选择Assets下的资源
java·unity·游戏引擎
麷飞花18 小时前
unity3d scene窗口选中物体, 在 hierarchy高光显示
unity·editor·unity3d·u3d·hierarchy
ۓ明哲ڪ19 小时前
Unity功能——关闭脚本自动编译(Unity2021.3)
unity·游戏引擎