Название. где-либо еще в программе нет упоминаний о спрайтерендерерах или массиве
SpriteRenderer[,] Renderer = new SpriteRenderer[8,8];
void Awake()
{
if (D)
{
D = false;
for (int x = 0; x <= 7; x++)
{
for (int y = 0; y <= 7; y++)
{
sq = Instantiate(square, globals.MovePos(x,y), Quaternion.identity);
Renderer[x,y] = new SpriteRenderer();
Renderer[x,y] = sq.GetComponent<SpriteRenderer>();
print(Renderer[x,y] == null);//false every time
}
}
}
}
void OnMouseDown()
{
if (stats.p != null)
{
stats.p.FindRange();
for(int x = 0; x <= 7; x++)
{
for(int y = 0; y <= 7; y++)
{
print(Renderer[x,y] == null); // true everytime
}
}
}
}
}
}
Что должно произойти, так это то, что он каждый раз печатает false.
Первое, что вам нужно понять, это то, что Unity был написан на C++, и весь наш код C# — это не что иное, как оболочка для собственной логики Unity C++. Почти все объекты и методы Unity C# являются зеркалом собственного представления C++. Это поможет вам понять следующие строки.
SpriteRenderer
наследуется от UnityEngine.Object
класса, как и почти любой другой класс в пространстве имен UnityEngine
. И UnityEngine.Object
имеет переопределение для оператора сравнения (==
). Вот из декомпилированного кода:
public class Object
{
...
public static bool operator ==(Object x, Object y) => Object.CompareBaseObjects(x, y);
private static bool CompareBaseObjects(Object lhs, Object rhs)
{
bool flag1 = (object) lhs == null;
bool flag2 = (object) rhs == null;
if (flag2 & flag1)
return true;
if (flag2)
return !Object.IsNativeObjectAlive(lhs);
return flag1 ? !Object.IsNativeObjectAlive(rhs) : lhs.m_InstanceID == rhs.m_InstanceID;
}
private static bool IsNativeObjectAlive(Object o)
{
if (o.GetCachedPtr() != IntPtr.Zero)
return true;
return !(o is MonoBehaviour) && !(o is ScriptableObject) && Object.DoesObjectWithInstanceIDExist(o.GetInstanceID());
}
internal static extern bool DoesObjectWithInstanceIDExist(int instanceID);
}
Как видите, для случаев, когда какой-либо из параметров равен null
, существует определенная логика. В этом случае прямого сравнения не происходит, но происходит проверка состояния нативных объектов. Если собственный указатель объекта равен IntPtr.Zero
, то есть является нулевым указателем (0x0), или указатель не является нулевым указателем, но указывает на уничтоженный объект, то результатом сравнения будет true
. DoesObjectWithInstanceIDExist
— нативная функция, поэтому мы не можем проверить, какая логика у нее под капотом, но, думаю, семантика этой функции довольно ясна.
До сих пор, если нативное представление вашего SpriteRenderer
уничтожено, то сравнение с null
вернет true
. Это может быть вызвано вызовом метода Destroy(...)
, изменением сцены, уничтожением ее родителя и т. д.
Поэтому всегда нужно помнить, что ваша переменная НЕ null
. Невозможно изменить значения переменных без прямого доступа к ним или их классам. Вы можете использовать Renderer[x,y] is null
или System.Object.ReferenceEquals(Renderer[x,y], null)
, чтобы убедиться, что значение вашей переменной действительно равно null
. А если это не null
, вы все равно можете вызывать его методы. Опять же, все методы Unity имеют внутреннюю проверку состояния объекта и выдают исключение при вызове в таком состоянии. НО если у вас есть собственный класс, унаследованный от MonoBehavior
и у него есть общедоступный метод, вы можете вызвать этот метод, даже если сравнение yourObject == null
возвращает true
. Поскольку уничтожение базового объекта C++ не приводит к уничтожению вашего объекта C#, оно просто заставляет оператор ==
работать по-другому.
Когда sq или любой из его родительских узлов будет уничтожен, компоненты станут нулевыми.