【问题标题】:Attach camera to scriptable object in unity将相机统一连接到可编写脚本的对象
【发布时间】:2019-01-17 06:41:05
【问题描述】:

我是 ScriptableObjects 的新手,我有一个菜鸟问题。 我读过可编写脚本的对象用于存储数据,甚至可以用于存储单个变量。所以这就是我所做的: 我创建了一个这样的脚本:

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

[CreateAssetMenu(fileName = "Data", menuName = "ScriptableObjects/MainCamera", 
order = 1)]
public class MainCamera : ScriptableObject
{
    public Camera Camera;
}

然后我在我的 Assets 文件夹中创建了一个可编写脚本的对象,如下所述:https://docs.unity3d.com/Manual/class-ScriptableObject.html

现在我想将主摄像头分配给检查器中的那个摄像头变量。 但是,选择菜单只显示“无”,但没有摄像头。

如何将相机分配给脚本对象中的相机变量?

【问题讨论】:

    标签: c# unity3d


    【解决方案1】:

    您可以不直接将场景引用附加到 ScriptableObjects 或实际上任何资产。

    但是你可以反过来:给相机 ScriptableObject 引用并让它告诉它自己对 ScriptableObject 的引用:

    // This attribute makes this classes messages be executed also in editmode
    // (= also of not in playmode)
    [ExecuteInEditModo]
    
    // Assure there is a Camera component
    [RequireComponent(typeof(Camera))]
    public class CameraSetter : MonoBehaviour
    {
        [SerializeField] private MainCamera mainCameraAsset;
    
        // Called on initialize
        // With [ExecuteInEditModo] also called on recompile
        private void Awake ()
        {
            mainCameraAsset.Camera = GetComponent<Camera>();
        }
    }
    

    并在mainCameraAsset 中引用您的MainCamera 实例。


    您是否有理由不使用Camera.main 而不是ScriptableObject?


    不同场景的地图

    如果您想归档类似“管理器资产”之类的内容,按照 cmets 中的要求为每个场景存储不同的 Camera 参考(希望我理解正确),我会将您的 MainCamera 更改为类似

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    [CreateAssetMenu(fileName = "Data", menuName = "ScriptableObjects/MainCamera", 
    order = 1)]
    public class MainCamera : ScriptableObject
    {
        public List<SceneCameraPair> SceneCameraPairs = new List<SceneCameraPair>();
    
        public Dictionary<string, Camera> sceneToCamera = new Dictionary<string, Camera>();
    
        public void AddPair(SceneCameraPair pair)
        {
            if(SceneCameraPairs.Contains(pair)) return;
    
            SceneCameraPairs.Add(pair);
            sceneToCamera[pair.scene.path] = pair.camera;
        }
    
        public void ResetPairs()
        {
            SceneCameraPairs.Clear();
            sceneToCamera.Clear();
        }
    }
    
    [System.Serializable]
    public class SceneCameraPair
    {
        public Scene scene;
        public Camera camera;
    }
    

    在设置器中使用SceneManager.GetActiveScene

    // This attribute makes this classes messages be executed also in editmode
    // (= also of not in playmode)
    [ExecuteInEditModo]
    
    // Assure there is a Camera component
    [RequireComponent(typeof(Camera))]
    public class CameraSetter : MonoBehaviour
    {
        [SerializeField] private MainCamera mainCameraAsset;
    
        // Called on initialize
        // With [ExecuteInEditModo] also called on recompile
        private void Awake ()
        {
            mainCameraAsset.AddPair(SceneManager.GetActiveScene,  GetComponent<Camera>();
        }
    }
    

    在稍后的场景中,您可以将列表与FirstOrDefault(不会引发异常,但如果找不到项目返回null)和Scene.path(因为场景名称可能相同)一起使用而且您不能直接比较scene,因为它的实例与引用的实例不同),例如

    var camera = mainCameraReference.SceneCameraPairs.FirstOrDefault(pair => pair.scene.path == ScaneManager.GetActiveScene().path);
    

    或字典之类的

     var camera = mainCameraReference.sceneToCamera[ScaneManager.GetActiveScene().path];
    

    各种类型

    为了能够存储不同类型的各种引用(假设每种类型只有一个),您可以执行类似的操作,例如

    [CreateAssetMenu(fileName = "Data", menuName = "ScriptableObjects/Data", order = 1)]
    public class References : ScriptableObject
    {
        public Camera mainCamera;
        public CharacterController controller;
        public Transform transform;
    
        // fix for the generic methods
        // a bit dirty maybe but should work
        public void Set(Component component)
        {
            if(component.GetType() == typeof(Camera))
            {
                mainCamera = (Camera) component;
            } 
            else if(component.GetType() == typeof(CharacterController))
            {
                controller = (CharacterController) component;
            }
            else if(component.GetType() == typeof(Transform))
            {
                transform = (Transform) component;
            }
        }
    
        public void Set(Camera camera)
        {
            mainCamera = camera;
        }
    
        public void Set(CharacterController characterController )
        {
            controller = characterController ;
        }
    
        public void Set(Transform characterTransform)
        {
            transform = characterTransform;
        }
    
        // or simply all at once
        public void Set(Camera camera, CharacterController characterController, Transform characterTransform)
        {
            mainCamera = camera;
            controller = characterController;
            transform = characterTransform;
        }
    
        // etc
    }
    

    你可以拥有一个像这样的基本 setter 类

    public abstract class SetterBase<T> : MonoBehaviour where T : Component
    {
        // unfortunately you can not serialize generics in 
        // the inspector so for now we stick with only one single ScriptableObject
        public References references;
    
        privtae void Awake()
        {
            SetReference<T>();
        }
    
        private void SetReference<T>() where T : Component
        {
            var component = GetComponent<T>();
            references.Set(component);
        }
    }
    

    现在您可以继承References 中存在的每种所需/类型的实现

    public CameraSetter : SetterBase<Camera>
    {
        // doesn't have to do anything else ... but could
    }
    

    和

    public TransformSetter : SetterBase<Transform>
    {
        // doesn't have to do anything else ... but could
    }
    

    等

    或者
    (这就是为什么我为所有事情添加了一个二传手)你可以让一个经理来处理所有事情

    public class ReferenceSetter : MonoBehaviour
    {
        public References references;
    
        // Reference those in the inspector as usual
        public Camera camera;
        public Transform transform;
        public CharacterController controller;
    
        private void Awake()
        {
            references.Set(camera, controller, transform);
        }
    }
    

    【讨论】:

    • 所以我无法在场景中附加任何东西,需要通过脚本获取参考?这是否意味着我也不能将角色控制器附加到 ScriptableObject 上?除了目前的实验之外,没有真正的原因。
    • 是的,您必须以某种方式为您想从场景中获取到ScriptableObjects 的任何内容编写一个设置器。或者使用一些依赖注入,例如zenject
    • 我是否将相机保存为财产?或者我将如何实现?
    • @Thrindil 你到底是什么意思?
    • 谢谢。只是想让你知道,我必须先编辑 SetterBase 类,然后才能让它工作,所以声明是 public abstract class SetterBase : MonoBehaviour where T : Component 而不仅仅是 public abstract class SetterBase : 单一行为
    猜你喜欢
    • 1970-01-01
    • 2021-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-22
    • 2022-08-15
    • 2021-12-07
    • 2010-12-14
    相关资源
    最近更新 更多