C# Unity как обратится к переменным класса из другого скрипта
У меня есть главный класс Game привязанный к Main Camera, в нем хранятся переменные, которые я должен читать/редактировать из других скриптов.
Как это реализовать? В Unity и ООП новичок, в интернете не смог найти ответов.
Пример:
Файл 1:
public class Game : MonoBehaviour
{
ArrayList antibodies = new ArrayList();
// другой код
}
Второй файл:
public class Skin : MonoBehaviour
{
void OnTriggerEnter2D(Collider2D other){
if(other.gameObject.tag == "antibody "){
(тут как-то достать antibodies).Remove(other);
}
}
}
Ответы (1 шт):
Во первых, у поля antibodies не задан модификатор доступа. По умолчанию он задается как private, а значит вы никак не можете получить к нему доступ извне.
Вообще получить доступ можно разными способами и зависят они от Ваших целей, которые мне неизвестны.
Например:
- Сделать поле
public static antibodies. Тогда к нему можно обратится через имя класса:Game.antibodies.Remove(other). Но, обычно, это плохая практика. Вы открываете это поле для доступа из любого места в программе, что превращает код в лапшу. - Сделать класс
Gameсинглтоном и полеpublic antibodies. Что аналогично первому варианту, наверняка превратит Ваш код в лапшу. - Поле
antibodiesделаемpublic, а объектGameищем с помощьюObject.FindObjectOfTypeилиGameObject.FindWithTag. Но это плохая практика. Создаёт не явные зависимости и ухудшает производетельность (особенно если объектов на сцене много). - Держать ссылку на компонент
Gameвнутри компонентаSkin, и опять же, полеantibodiesделаемpublic. Тогда получаем доступ как_game.antibodies.Remove(other)где_gameссылка на компонент типаGame. (Как назначить эту ссылку это уже совсем другая история. Например, при создании и инициализации ваших 'antibody'. Либо настроить префаб на сцене и прокинуть ссылку в редакторе в поле помеченноеSerializelField. Вариантов много). В общем, это самый нормальный вариант работы с зависимостями.
Наверняка есть ещё варианты, но не зная задач не приходят в голову. Остановимся на самом нормальном в общем случае.
public class Game : MonoBehaviour
{
public List<Antibody> antibodies = new List<Antibody>();
// другой код
}
public class Skin : MonoBehaviour
{
[SerializeField] private Game _game;
public void Init(Game game)
{
_game = game;
}
private void OnTriggerEnter2D(Collider2D other)
{
if(other.TryGetComponent<Antibody>(out var antibody))
_game.antibodies.Remove(antibody);
}
}
При инстанциировании объекта Skin вызываем метод Init и передаём ссылку на Game. Либо кидаем ссылку вручную в компонент объекта на сцене в редакторе.
Немного не по теме:
- ArrayList лучше заменить на List из System.Collection.Generic. Generic типы очень полезны за счет статической типизации. Кроме того позволят избежать неявных упаковок и распаковок.
- вместо проверки объекта Unity по тегу, лучше проверять по наличию компонента. Например на объекты
antibodyдобавить скриптAntibody. Даже если он будет пустой. Так как вы не можете гарантировать что объект с тегом имеет на себе нужные компоненты. К тому же, у одного объекта может быть только один тег, а компонентов может быть сколько угодно. Что если вам понадобится пометить объект двумя "тегами"? Да и от опечаток никто не застрахован. Очень легко ошибиться в написании тега в коде, а вот при ошибке в написании имени типа Вам подскажет среда разработки или компилятор. Соответствено и список у вас будетList<Antibody> - очень похоже, что Ваш класс
Gameсобирается стать глобальным монолитным классом "менеджером" с кучей функционала, зависимостей и ответственностью. Тоже плохая практика. Ответственность следует разделять на более мелкие классы, и давать соответствующие им имена. Имена классовGame,Manager,Controller,Helperи подобные вот прям вообще не дают представления о том для чего они нужны, и обычно являются индикаторами таких вот классов-монолитов.