在Unity中将相机附加到可编写对象上

4

我是ScriptableObjects的新手,有一个初学者问题。 我已经了解到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

现在,我想将主摄像机分配给检查器中的该摄像机变量。然而,选择菜单只显示“none”,没有相机。

如何将相机分配给我的可编写脚本的相机变量?

1个回答

2

你不能直接将场景引用附加到ScriptableObject或任何资产上。

但是你可以采取相反的方式:将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>();
    }
}

请引用MainCamera实例中的mainCameraAsset


你为什么不使用Camera.main而使用ScriptableObject


为不同场景创建地图

如果您想要类似于存储每个场景不同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;
}

and in the setter use 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);
    }
}

是的,你需要编写一个setter来获取从场景传递到ScriptableObject的任何内容。或者使用一些依赖注入框架,例如zenject - derHugo
@Thrindil 你具体是什么意思? - derHugo
你想为每个场景创建不同的相机映射吗?我希望我理解得正确 - 我已经更新了我的答案。否则,只需使用相同的setter,但创建各种ScriptableObjects,例如Scene1_mainCameraScene1_menuCameraScene2_camera等。如果您正在谈论不同的组件类型...是的,您需要为每种类型创建一个setter。 - derHugo
哦,非常抱歉,这不是我要找的东西。 通过“10个场景引用”,我的意思是10个不同的场景对象,类似于以下内容:public Camera Camera; public CharacterController CharacterController; public Transform CharacterTransform; 等。 - Thrindil
1
谢谢。只是想让你知道,在我能够使其正常工作之前,我不得不编辑SetterBase<T>类,因此声明为 public abstract class SetterBase<T> : MonoBehaviour where T : Component 而不仅仅是public abstract class SetterBase<T> : MonoBehaviour。 - Thrindil
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接