在Unity中尚未实例化的其他场景中访问GameObject

时间:2018-12-14 09:15:02

标签: unity3d gameobject

我已经检查了Stack Overflow和Internet上的其他问题here,但找不到类似的内容。

我有一个“设置场景”,我想在其他场景中切换特定的音频片段(例如,我只想从游戏场景(场景7)的设置场景(场景2)中关闭倒计时计时器的声音)。

在带有倒数计时器的游戏场景中,我连接了一个AudioClip,我想从“设置场景”中访问它并发送timerHandler.ToggleMuteTimer(false)

以下是一些代码:

public class TimerSoundHandler : MonoBehaviour {

public GameObject audioOnIcon;
public GameObject audioOffIcon;
private TimerHandler timerHandler;

void Start()
{
    //timerHandler = GameObject.FindWithTag("TimerTAG").GetComponent<TimerHandler>();        
    //timerHandler = GameObject.FindGameObjectWithTag("TimerTAG");  
    timerHandler = FindObjectOfType<TimerHandler>();   
    SetSoundState();
}


public void ToggleSound()
{
    if (PlayerPrefs.GetInt("TimerMuted", 0) == 0)
    {
        PlayerPrefs.SetInt("TimerMuted", 1);
    }
    else
    {
        PlayerPrefs.SetInt("TimerMuted", 0);
    }

    SetSoundState();
}

public void SetSoundState()
{
    if (PlayerPrefs.GetInt("TimerMuted", 0) == 0)
    {
        UnmuteSound();
    }
    else
    {
        MuteSound();
    }
}

private void UnmuteSound()
{
    //timerHandler.ToggleMuteTimer(true);
    audioOnIcon.SetActive(true);
    audioOffIcon.SetActive(false);
}

private void MuteSound()
{
    //timerHandler.ToggleMuteTimer(false);
    audioOnIcon.SetActive(false);
    audioOffIcon.SetActive(true);
}

}

这是TimerHandler脚本:

    public class TimerHandler : MonoBehaviour {

    public Text timerText;
    public AudioClip timerClipSound;

    [Range(0,3600)]
    [SerializeField]
    private float totalTime = 300f;
    private bool timeUp = false;
    private AudioSource audioSource;


    private void Awake()
    {
        audioSource = GetComponent<AudioSource>();
    }

    private void Update()
    {
        if (timeUp)
            return;

        totalTime -= Time.deltaTime;

        string minutes = ((int)totalTime / 60).ToString("00");
        string seconds = (totalTime % 60).ToString("00");

        timerText.text = minutes + ":" + seconds;


        if (totalTime <= 0)
        {
            timeUp = true;
            audioSource.mute = true;
        }
    }

    public void ToggleMuteTimer(bool value)
    {
        audioSource.mute = value;
    }
}

有什么想法可以访问和编辑尚未实例化的游戏对象?

1 个答案:

答案 0 :(得分:1)

您无法访问尚未创建(实例化)的对象。

一种标准的解决方案是在生成的(TimerHandler)类中包含代码,以通知其控制器类(TimerSoundHandler)该对象现在在场景中。

我通常会使用OnEnable / OnDisable对方法,可能还会使用静态方法。

这对于维护您要跟踪的对象的最新列表很有用


活动侦听器列表

TimeHandlerController.cs

static List<TimeHandler> activeHandlers; 

public static RegisterTimer(TimeHandler source)
{
  if (activeHandlers==null) activeHandlers = new List<TimeHandler>(); 
   activeHandlers.Add(source);
}

public static UnRegisterTimer(TimeHandler source)
{
   activeHandlers.Remove(source); //if (activeHandlers.Contains(source))  
}

void DoStuffWithTimeHandlers()
{
  foreach(TimeHandler th in activeHandlers)
    th.DoStuff();
}

TimerHandler.cs

void OnEnable()
{
    TimeHandlerController.RegisterTimer(this);
}

void OnDisable()
{
    TimeHandlerController.UnRegisterTimer(this);
}

public void DoStuff() 
{ 
 // Do your stuff 
}

此模式可让您可靠地处理多个受控对象,以便控制器始终具有需要控制的活动对象的最新列表(无需搜索场景)。

一个更简单的解决方案,如果您不需要跟踪所有TimeHandler,则使用每个对象订阅的事件(又称Observer模式),它的代码要少得多,但是您松开了积极的听众,这通常很有用。例如,使用事件调试异常更加困难,因为事件调用会丢失堆栈跟踪。

事件订阅

TimeHandlerController.cs

public static System.Action tick; // event

void Tick()
{
  if (tick!=null) // exception if no handlers otherwise
    tick.Invoke();
}

TimeHandler.cs

void OnEnable()
{
    TimeHandlerController.tick+=DoStuff;
}

void OnDisable()
{
    TimeHandlerController.tick-=DoStuff;
}

public void DoStuff() 
{ 
 // Do your stuff 
}

在订阅事件的情况下,订阅者的类型无关紧要(只要方法签名匹配),在列表的情况下,您需要知道对象的基类才能在其上调用方法,或(通常最好)指定一个接口(超出问题范围)