一、SetActive的NullReferenceException
众数周知,让一个物体隐藏,使用的是SetActive(false),比起移到视野外,又要重新移回来计算坐标;或者直接改透明度,考虑到2D对象不一定有render给你改,这算是万能通用的方法,也是最普遍的方法,但是,在使用中,常常遇到隐藏又显示的情况,此时就不是简简单单地一句SetActive(true)能搞定的,如下代码:
using UnityEngine;
using System.Collections;
public class Script : MonoBehaviour
{
//错误的写法
void Start()
{
GameObject.Find("Text1").SetActive(false);
}
void Update()
{
//一开始,想先让Text1隐藏……
//做了一些事件之后,我想让其重新显示……
GameObject.Find("Text1").SetActive(true);
}
}
这叫姿势不对,会报如下的错误:
因为SetActive(false)之后会带来一种副作用,GameObject.Find("XX");将再也找不到这个对象,
所以在SetActive(false)使用之前,我们一定要留个种,不然真的和直接销毁这个物体没区别~
正确写法是这样的,比如这里是Text的隐藏与显示:
using UnityEngine;
using System.Collections;
public class Script : MonoBehaviour
{
//正确的写法
GameObject Text1;
void Awake()
{
Text1 = GameObject.Find("Text1");//你必须先在Awake()函数找到这个对象,并保留其实例(基本和指针的意思)
}
void Start()
{
Text1.SetActive(false);//然后在Start()基于其属性,分2步初始化,比较麻烦。
}
void Update()
{
//一开始,想先让Text1隐藏……
//做了一些事件之后,我想让其重新显示……
Text1.SetActive(true);
}
}
二、LoadLevel的初始化
使用Application.LoadLevel("某某场景"),当然Unity5.x已经取消掉了,不过在大部分项目还是Unity4.x的前提下,这方法还是非常常见。
首先这方法一上来就是一个坑,在Applicaiton有个loadedLevel分分钟让你混淆,尤其是你写程序写到飘飘欲仙,迷迷糊糊的时候,不小心用了loadedLevel是一辈子都在报错的。
这还是小问题,如果这东西配合Time.timeScale = 0;使用,那就又有一个坑。
例如,我布置了一个场景,此场景中有个刚体球会自然下落,然后落到y<0的位置会自动停止,同时弹出版本,提示“游戏结束”,当然我们肯定要设置一个按钮,给用户重新开始,显示起来的代码,自然而然如下:
using UnityEngine;
using System.Collections;
public class Script : MonoBehaviour
{
//初始化的时候我需要将Text1这东西隐藏……
GameObject Text1;
void Awake()
{
Text1 = GameObject.Find("Text1");
}
void Start()
{
Text1.SetActive(false);
}
void Update()
{
//一段代码、一段处理、一段事情之后……
//……
//比如用户Gameover之类的,我需要让这个场景停止一个
//if(游戏失败){
if (GameObject.Find("Sphere").transform.position.y < 0)
{
Time.timeScale = 0;
Text1.SetActive(true);
}
//}
}
//这或许是你的提示窗里面的一个按钮的onclick事件
public void onRestart()
{
Application.LoadLevel("Canvas");
}
}
但具体的运行效果却是如下的:
此游戏的球,在开始的时候,原本是自然而然地落下的,但是,点击button之后,执行Application.LoadLevel("Canvas"),重新载入本关,它就不会开始自动掉了。
甚至有些版本的Unity3D,那个文字不会在初始化的时候隐藏。喂!不是说Application.LoadLevel("Canvas")所有东西会重新还原的吗?而且Start、Awake这些初始化函数也照样会执行一遍的吗?你官方文档都这样写了!我的球的刚体属性怎么没了?
其实Application.LoadLevel一点问题都没有,你错就错在用了Time.timeScale = 0;布置了一个时间结界,告诉用户一些信息之后,在用户点击“重新开始”游戏,没重新将这个时间结界打开而已,你在Awake()函数再补个Time.timeScale = 1;如下代码:
using UnityEngine;
using System.Collections;
public class Script : MonoBehaviour
{
//初始化的时候我需要将Text1这东西隐藏……
GameObject Text1;
void Awake()
{
Time.timeScale = 1;//记得重新打开时间结界哦,由于有Application.LoadLevel的存在
Text1 = GameObject.Find("Text1");
}
void Start()
{
Text1.SetActive(false);
}
void Update()
{
//一段代码、一段处理、一段事情之后……
//……
//比如用户Gameover之类的,我需要让这个场景停止一个
//if(游戏失败){
if (GameObject.Find("Sphere").transform.position.y < 0)
{
Time.timeScale = 0;//关了时间结界
Text1.SetActive(true);
}
//}
}
//这或许是你的提示窗里面的一个按钮的onclick事件
public void onRestart()
{
Application.LoadLevel("Canvas");
}
}
就药到病除了:
这是在使用了LoadLevel的时候,初始化时候容易忽视的问题!