Unity_飞机大战_防止单例随场景销毁和跨场景两个物体脚本问题_自动加载物体挂载脚本的两种方式

时间:2022-10-19 22:57:46

防止单例随场景销毁和跨场景两个物体脚本问题

通过泛型封装单例类 

NormalSingleton.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//标准单例类
public class NormalSingleton<T> where T : class, new()
{
    private static T _single;

    public static T Singel
    {
        get
        {
            if (_single == null)
            {
                T t = new T();
                if (t is MonoBehaviour)
                {
                    Debug.LogError("Mono类请使用MonoSingleTon!");
                    return null;
                }

                _single = t;
            }

            return _single;
        }
    }
}
MonoSingleton.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//Mono单例类
public class MonoSingleton<T> : MonoBehaviour where T : MonoBehaviour
{
    private static T _singleton;

    public static T SingleTon
    {
        get
        {
            if (_singleton == null)
            {
                _singleton = FindObjectOfType<T>();
                if (_singleton == null)
                {
                    Debug.LogError("场景中未找到类的对象,类名为:"+typeof(T).Name);
                }
            }
            return _singleton;
        }
    }

    private void Awake()
    {
        if (_singleton == null)
        {
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
        }
    }
}

通过物体路径 自动挂载脚本(游戏预制物体名字 和 脚本名字得一样)(方式一) 

ILoader接口 给ResourceLoder 和 ABLoader实现

ILoader.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public interface ILoader 
{
    /// <summary>
    /// Resource方法加载物体
    /// </summary>
    /// <param name="path">加载物体的路径</param>
    /// <param name="parent">指定父物体</param>
    /// <returns></returns>
    GameObject LoadPrefab(string path, Transform parent = null);
}

Resource加载器

ResourceLoader.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ResourceLoader : ILoader
{
   /// <summary>
   /// Resource方法加载物体
   /// </summary>
   /// <param name="path">加载物体的路径</param>
   /// <param name="parent">指定父物体</param>
   /// <returns></returns>
   public GameObject LoadPrefab(string path,Transform parent = null)
   {
      GameObject go = Resources.Load<GameObject>(path);
      GameObject go2 = Object.Instantiate(go, parent);
      return go2;
   }
   
   
}

AB包加载器 (没有实现 共理清逻辑使用)

ABLoader.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ABLoader : ILoader
{
    public GameObject LoadPrefab(string path, Transform parent = null)
    {
        throw new System.NotImplementedException();
    }
}

LoadManager场景加载器

LoadManager.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//加载管理器
public class LoadManager : NormalSingleton<LoadManager>
{   
    [SerializeField]
    private ILoader iLoader;

    public LoadManager()
    {   
        //Resource加载方式
        iLoader = new ResourceLoader();
        //AB包加载方式
        // iLoader = new ABLoader();
    }
    
    //自动挂载脚本加载物体方式一(通过脚本与游戏物体同名)
    public GameObject LoadPrefab(string path, Transform parent = null)
    {
        GameObject go = iLoader.LoadPrefab(path, parent);
        //通过物体名 自动挂载脚本(游戏物体名字 和 脚本名字得一样)
        Type type = Type.GetType(go.name.Remove(go.name.Length - 7));
        go.AddComponent(type);
        return go;
    }
    
    //自动挂载脚本加载物体方式二(通过反射特性)
    // public GameObject LoadPrefab(string path, Transform parent = null)
    // {
    //     GameObject go = iLoader.LoadPrefab(path, parent);
    //     Type type = BindUtil.GetType(path);
    //     go.AddComponent(type);
    //     return go;
    // }
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//加载管理器
public class LoadManager : NormalSingleton<LoadManager>
{   
    [SerializeField]
    private ILoader iLoader;

    public LoadManager()
    {   
        //Resource加载方式
        iLoader = new ResourceLoader();
        //AB包加载方式
        // iLoader = new ABLoader();
    }
    
    //自动挂载脚本加载物体方式一(通过脚本与游戏物体同名)
    public GameObject LoadPrefab(string path, Transform parent = null)
    {
        GameObject go = iLoader.LoadPrefab(path, parent);
        //通过物体名 自动挂载脚本(游戏物体名字 和 脚本名字得一样)
        Type type = Type.GetType(go.name.Remove(go.name.Length - 7));
        go.AddComponent(type);
        return go;
    }
    
    //自动挂载脚本加载物体方式二(通过反射特性)
    // public GameObject LoadPrefab(string path, Transform parent = null)
    // {
    //     GameObject go = iLoader.LoadPrefab(path, parent);
    //     Type type = BindUtil.GetType(path);
    //     go.AddComponent(type);
    //     return go;
    // }
}

启动游戏入口 需要挂在在游戏物体上

    using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//启动游戏入口
public class LaunchGame : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {   
        //通过反射特性 加载方式二
        // InitCustermonAttribute initCustermonAttribute = new InitCustermonAttribute();
        // initCustermonAttribute.init();
        
        LoadManager.Singel.LoadPrefab("Prefab/StartView",transform);
    }
    
}

 启动入口挂载在Cavas上 加载的预制体 会做为其子物体

Unity_飞机大战_防止单例随场景销毁和跨场景两个物体脚本问题_自动加载物体挂载脚本的两种方式

运行效果:

Unity_飞机大战_防止单例随场景销毁和跨场景两个物体脚本问题_自动加载物体挂载脚本的两种方式

通过游戏物体路径 反射特性 自动加载预制体和脚本(方式二)

绑定物体特性

BindPrefab.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//绑定物体特性
[AttributeUsage(AttributeTargets.Class)]
public class BindPrefab : Attribute
{
    public string Path { get; private set;}

    public BindPrefab(string path)
    {
        Path = path;
    }
}

加载物体同名脚本 通过路径反射

StartView.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//加载物体同名脚本 通过路径反射
[BindPrefab("Prefab/StartView")]
public class StartView : MonoBehaviour
{
  
}

绑定工具类 

BindUtil.cs 

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;


//绑定工具类
public class BindUtil : MonoBehaviour
{
    public static Dictionary<string, Type> _Dictionary = new Dictionary<string, Type>();

    public static void Bind(string path,Type type)
    {
        if (!_Dictionary.ContainsKey(path))
        {
            _Dictionary.Add(path,type);
        }
        else
        {
            Debug.LogError("当前数据已存在路径: "+path);
        }
    }

    public static Type GetType(string path)
    {
        if (_Dictionary.ContainsKey(path))
        {
            return _Dictionary[path];
        }
        else
        {
            Debug.LogError("数据未包含路径:"+path);
            return null;
        }
    }
}

初始化自定义特性

InitCustermonAttribute.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;

//初始化自定义特性
public class InitCustermonAttribute : MonoBehaviour
{
   public void init()
   {
      Assembly assembly = Assembly.GetAssembly(typeof(BindPrefab));
      Type[] types = assembly.GetExportedTypes();

      foreach (Type type in types)
      {
         foreach (Attribute attribute in Attribute.GetCustomAttributes(type,true))
         {
            if (attribute is BindPrefab)
            {
               BindPrefab data = attribute as BindPrefab;
               BindUtil.Bind(data.Path,type);
            }
         }
      }
   }
}

加载管理器

LoadManager.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//加载管理器
public class LoadManager : NormalSingleton<LoadManager>
{   
    [SerializeField]
    private ILoader iLoader;

    public LoadManager()
    {   
        //Resource加载方式
        iLoader = new ResourceLoader();
        //AB包加载方式
        // iLoader = new ABLoader();
    }
    
    //自动挂载脚本加载物体方式一(通过脚本与游戏物体同名)
    // public GameObject LoadPrefab(string path, Transform parent = null)
    // {
    //     GameObject go = iLoader.LoadPrefab(path, parent);
    //     //通过物体名 自动挂载脚本(游戏物体名字 和 脚本名字得一样)
    //     Type type = Type.GetType(go.name.Remove(go.name.Length - 7));
    //     go.AddComponent(type);
    //     return go;
    // }
    
    //自动挂载脚本加载物体方式二(通过反射特性)
    public GameObject LoadPrefab(string path, Transform parent = null)
    {
        GameObject go = iLoader.LoadPrefab(path, parent);
        Type type = BindUtil.GetType(path);
        go.AddComponent(type);
        return go;
    }
}

启动游戏入口

LaunchGame.cs
    using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//启动游戏入口
public class LaunchGame : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {   
        //通过反射特性 加载方式二
        InitCustermonAttribute initCustermonAttribute = new InitCustermonAttribute();
        initCustermonAttribute.init();
        
        LoadManager.Singel.LoadPrefab("Prefab/StartView",transform);
    }
    
}

运行效果:

Unity_飞机大战_防止单例随场景销毁和跨场景两个物体脚本问题_自动加载物体挂载脚本的两种方式

加载器结构目录

Unity_飞机大战_防止单例随场景销毁和跨场景两个物体脚本问题_自动加载物体挂载脚本的两种方式