• C#程序员整理的Unity 3D笔记(十三):Unity 3D基于组件的思想


    如果你接触过《设计模式》、软件架构的编程思想,就会知道优秀的设计准则:“组合优于继承的”。

          这句话很简短,但开始学习OOP的时候,真切的是—-不太好理解(以我个人当初学习为例)。

    OOP的继承思想

    image

    在设计主角(Player)的时候,为了能够复用A、B、C的功能,我开始把A、B、C按照继承来写,多了一些VirutalOverrideProtected等修饰符,功能没有任何问题,就是有些别扭。如Start、Update方法,只能在A中采用模板方法处理,万一B、C、Player中直接用了Start、Update方法,会导致奇奇怪怪的问题;同时在继承的基类中,无形之间多了一些包袱,对于Player不得不使用A、B、C的函数、变量(非private的)。

    整个关系变为了:

    • Player is a A
    • Player is a B
    • Player is a C

    心理上疙疙瘩瘩的,总觉得有点别扭。

    OOP的组合思想

    image

    以前使用组合思想较多的是构建树、树叶模型,例如电信中的网元模型。这种思想,属于Unity 3D的核心思想–组件。在Player、A、B、C中可自由使用Start、Update函数(请不考虑执行顺序,脚本组件的先后顺序外部可调整,但是意义不大),最重要的是,关系理顺了—主角变成更积极、主动。

    • Player have a A
    • Player have a B
    • Player have a C
      在Unity 3D中,可复用的几乎全部为封装为了组件,eg: transform、rigibody、render、camera、***.cs脚本;为了配合方便的使用非内置的组件,可使用gameObject.AddComponent<T>()、gameObject.GetComponent<T>()来添加、获得组件(一般是自定义的脚本)。

    这里我举一个实际的例子,在《Unity 3D手机游戏开发》第二版的“太空射击游戏”中,有一个需求,需要给游戏中可复用的GameObject添加自动销毁的功能(通过时间计时器,或者触发器添加),代码很简单,不到100行,要添加的GameObject有5、6个,虽然工作量不大,但总不能每个都拷贝一遍代码吧。

    开始我是按照OOP继承做的,

    image

    看了几天,很不爽,后来重构为如下图:

    image

    这样使得自动销毁组件的功能发挥的更加灵活、机动,即不必拘泥于静态的继承思想来实现。

    从这个重构过程中,我学到Unity 3D组件思想的闪闪发光……

    附录:完整的自动销毁组件代码:

    public class AutoDestoryComponent : MonoBehaviour 

        #region ICanCache 
        public ParticleSystem[] m_pss = null; 
        public int m_life = 1; //3条命 
        public float m_AutoDeadTime = 3;//3s自动销毁

        private int m_life_Base = 3; //3条命【恢复用】 
        private float m_AutoDeadTime_Base = 3;//3s自动销毁【恢复用】【-1:表示不自动销毁,如Enemy】

        void Update() 
        { 
            //需要自动销毁 
            if (m_AutoDeadTime_Base >= 0) 
            { 
                m_AutoDeadTime -= Time.deltaTime;

                if (m_AutoDeadTime <= 0) 
                { 
                    InnerDead(); 
                    return; 
                } 
            }

            if (m_life <= 0) 
            { 
                InnerDead(); 
            } 
        }

        /// <summary> 
        /// 设置自动销毁数据 
        /// </summary> 
        /// <param name="life_base">默认生命值</param> 
        /// <param name="autoDeadTime_base">-1不自动销毁;其他数据代表销毁时间(单位s)</param> 
        public void SetBasePara(int life_base = 1, float autoDeadTime_base = -1) 
        { 
            m_AutoDeadTime = m_AutoDeadTime_Base = autoDeadTime_base; 
            m_life = m_life_Base = life_base; 
        }

        //是否启用 
        public bool IsUse { get; set; } 
        //死后位置 
        public Vector3 DeathPosition 
        { 
            get 
            { 
                return new Vector3(2000, 2000, 2000); 
            } 
        }

        //复活 
        public void Init(Vector3 position, Quaternion rotation) 
        { 
            transform.gameObject.SetActive(true); 
            transform.position = position; 
            transform.rotation = rotation; 
            IsUse = true; 
            foreach (ParticleSystem item in m_pss) 
            { 
                item.Play(true); 
            }

            //有些绕 
            m_life = m_life_Base; 
            m_AutoDeadTime = m_AutoDeadTime_Base; 
        }

        private void InnerDead() 
        { 
            IsUse = false; 
            transform.position = DeathPosition; 
            foreach (ParticleSystem item in m_pss) 
            { 
                item.Stop(true); 
            }

            this.gameObject.SetActive(false); 
        } 
        #endregion 
    }

    包括系统自带的Audio、Transform、Camera、Image、Button等等。GameObject是一个容器,没有Image的GameObject,只要新建一个空的GameObject,添加Image Component极为Image GameObject对象的。

    也即是在Unity3D中,很少用GameObject.ID的概念,而是用GameObject.Tag、GameObject.name来区分不同的GameObject,且Tag、name不唯一。

    结论:在Unity3D中,万事万物都是Component。

  • 相关阅读:
    PHP获取汉字拼音首字母
    记录,待总结5
    HDU2833 WuKong Floyd
    搜索
    记录,待总结4
    HDU3350 #define is unsafe 栈的应用
    指针与引用的混合使用总结
    多源最短路径 Floyd
    引用总结
    函数返回值总结
  • 原文地址:https://www.cnblogs.com/xifarm/p/csharp_learnunity3d_component.html
Copyright © 2020-2023  润新知