You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity单例MonoBehaviour类中如何通过[SerializeField]获取Camera?

解决SingletonMono自动创建对象时的Camera赋值问题

你的核心矛盾在于:自动创建的单例GameObject无法通过Inspector给[SerializeField]字段赋值,同时想避免手动挂载脚本的操作。以下是三种实用解决方案:


方案1:通过预制体初始化单例

提前在预制体中配置好Camera等参数,让单例自动加载预制体而非创建空对象,完美保留Inspector赋值的便利性。

修改SingletonMono基类

添加预制体加载逻辑,允许子类指定预制体路径:

public abstract class SingletonMono<T>: MonoBehaviour where T: MonoBehaviour
{
    private static T _instance;
    protected static bool ApplicationIsQuitting;
    protected static bool IsGlobal;

    static SingletonMono()
    {
        ApplicationIsQuitting = false;
        IsGlobal = false;
    }

    // 子类重写此方法返回预制体路径
    protected virtual string GetPrefabPath() => string.Empty;

    public static T Instance
    {
        get
        {
            if (ApplicationIsQuitting)
            {
                Debug.LogWarning($"Application is quitting, you cant operate a singleton mono object: [{typeof(T)}]");
                return _instance;
            }
            
            _instance = FindObjectOfType<T>();
            if (FindObjectsOfType<T>().Length > 1)
            {
                Debug.LogError($"the singleton type [{typeof(T)}] has more than one instances");
                return _instance;
            }
            
            if (_instance != null) return _instance;
            
            GameObject obj = null;
            // 尝试加载子类指定的预制体
            var prefabPath = ((SingletonMono<T>)Activator.CreateInstance(typeof(T), true)).GetPrefabPath();
            if (!string.IsNullOrEmpty(prefabPath))
            {
                obj = Resources.Load<GameObject>(prefabPath);
                if (obj != null)
                {
                    obj = Instantiate(obj);
                    obj.name = typeof(T).ToString();
                }
            }
            
            // 预制体不存在时创建空对象
            obj ??= new GameObject(typeof(T).ToString());
            _instance = obj.GetComponent<T>() ?? obj.AddComponent<T>();
            
            if(IsGlobal) DontDestroyOnLoad(obj);
            return _instance;
        }
    }

    public void OnApplicationQuit()
    {
        ApplicationIsQuitting = true;
    }
}

修改StageManager子类

重写GetPrefabPath方法指定预制体位置:

public class StageManager: SingletonMono<StageManager>
{
    [SerializeField] private float spaceOffset;
    [SerializeField] private Camera mainCam;

    // 假设预制体放在Resources/StageManagerPrefab路径下
    protected override string GetPrefabPath() => "StageManagerPrefab";

    public Vector3 GetWorldPositionByScreenPoint(Vector2 pos)
    {
        var z = -mainCam.transform.position.z;
        return mainCam.ScreenToWorldPoint(new Vector3(pos.x, pos.y, z));
    }
}

操作步骤:创建StageManager预制体,在Inspector中给mainCam赋值,将预制体放入Resources目录即可。


方案2:代码自动获取Camera(无需Inspector赋值)

如果只需使用主相机或特定标签的相机,直接通过代码获取,完全跳过手动赋值环节。

修改StageManager类

public class StageManager: SingletonMono<StageManager>
{
    [SerializeField] private float spaceOffset;
    private Camera mainCam;

    private void Awake()
    {
        // 方式1:获取标签为"MainCamera"的主相机(会缓存结果,性能优)
        mainCam = Camera.main;
        
        // 方式2:获取指定标签的相机(适合多相机场景)
        // mainCam = GameObject.FindWithTag("GameCamera").GetComponent<Camera>();
        
        // 方式3:获取场景中第一个Camera组件(适合单相机场景)
        // mainCam = FindObjectOfType<Camera>();
    }

    public Vector3 GetWorldPositionByScreenPoint(Vector2 pos)
    {
        if (mainCam == null)
        {
            Debug.LogError("Main Camera not found!");
            return Vector3.zero;
        }
        var z = -mainCam.transform.position.z;
        return mainCam.ScreenToWorldPoint(new Vector3(pos.x, pos.y, z));
    }
}

注意:使用Camera.main时确保目标相机的标签设为"MainCamera"。


方案3:允许场景中存在预设实例(自动检测)

优化单例逻辑,优先使用场景中已有的实例,只有当场景中无实例时才自动创建。这样你可以在场景中放置一个挂载了StageManager的GameObject,提前在Inspector中赋值Camera,单例会自动复用该实例。

修改SingletonMono基类

添加Awake阶段的实例检测逻辑:

public abstract class SingletonMono<T>: MonoBehaviour where T: MonoBehaviour
{
    private static T _instance;
    protected static bool ApplicationIsQuitting;
    protected static bool IsGlobal;

    static SingletonMono()
    {
        ApplicationIsQuitting = false;
        IsGlobal = false;
    }

    protected virtual void Awake()
    {
        if (_instance == null)
        {
            _instance = this as T;
            if (IsGlobal) DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
            Debug.LogWarning($"Duplicate singleton instance of {typeof(T)} destroyed.");
        }
    }

    public static T Instance
    {
        get
        {
            if (ApplicationIsQuitting)
            {
                Debug.LogWarning($"Application is quitting, you cant operate a singleton mono object: [{typeof(T)}]");
                return _instance;
            }
            
            if (_instance == null)
            {
                _instance = FindObjectOfType<T>();
                if (_instance == null)
                {
                    var obj = new GameObject(typeof(T).ToString());
                    _instance = obj.AddComponent<T>();
                    if(IsGlobal) DontDestroyOnLoad(obj);
                }
            }
            
            if (FindObjectsOfType<T>().Length > 1)
            {
                Debug.LogError($"the singleton type [{typeof(T)}] has more than one instances");
            }
            
            return _instance;
        }
    }

    public void OnApplicationQuit()
    {
        ApplicationIsQuitting = true;
    }
}

操作步骤:在场景中创建一个GameObject,挂载StageManager,在Inspector中给mainCam赋值即可。游戏启动时单例会自动识别并复用这个实例,不会创建新对象。


内容的提问来源于stack exchange,提问作者Wiosier

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 04:50:18