首页 > 解决方案 > 将相机统一连接到可编写脚本的对象

问题描述

我是 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

解决方案


不能直接将场景引用附加到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>();
    }
}

并在 中引用您的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;
}

并在设置器中使用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
}

比你可以有一个基本的二传手类

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
}

ETC

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

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);
    }
}

推荐阅读