Автор Тема: Классы C# в Unity3d  (Прочитано 23297 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Январь 18, 2014, 07:42:28 am
Прочитано 23297 раз

Mimi Neko

  • Администратор
  • Старожил форума

  • Оффлайн
  • *****

  • 2456
  • Репутация:
    153
    • Просмотр профиля
Классы C# в Unity3d

Основной родительский класс в Unity3d - MonoBehaviour

Его название напрямую идёт от программной среды "Mono", на которой и работает программная часть движка Unity3d.
Все скрипты, создаваемые в Unity3d, по умолчанию создаются как дочерние классы от родительского класса MonoBehaviour:

public class Main : MonoBehaviour{

     //тело класса

}
- Создали наш дочерний класс Main.
Важное условие при этом - название скрипта, должно совпадать с названием созданого нами дочернего класса, вот так:  Main.cs
Важными достоинствами таких скриптов с дочерними от MonoBehaviour классами в Unity3d, являются:

- Исключительная возможность прикрепления таких скриптов, к объектам Unity3d в качестве компонентов.
- Возможность доступа к переменным, спискам и массивам такого класса, с помощью панели редактора Unity3d.
- Поддержка собственных методов, а фактически событий вызовов Unity3d, таких как методы Start(),  OnGUI(), Update(), OnCollision**** и многих других.

Лучшее предназначение таких классов - непосредственное обслуживание объектов, и само главное тело проекта (движёк проекта или игры).  Такой подход, поможет полностью исключить рассредотачивание обработки собственных методов Unity3d, по разным скриптам, и они будут происходить лишь в одном.
Тоесть, к примеру, Update() должен быть лишь в одном скрипте, и OnGui() тоже, но не обязательно в одном и том же.

А вот для множества дополнительных исполнительных методов и функций, иногда удобнее использовать "свободные классы" C#.
« Последнее редактирование: Январь 19, 2014, 13:27:53 pm от Mimi Neko »

Январь 18, 2014, 08:06:14 am
Ответ #1

Mimi Neko

  • Администратор
  • Старожил форума

  • Оффлайн
  • *****

  • 2456
  • Репутация:
    153
    • Просмотр профиля
"Свободные классы" C#:

Доступная переменная, недоступного класса

Скрипты в Unity3d, вовсе не обязательно должны быть прикреплены к какому-либо объекту.
Вот пример скрипта со "свободным классом", не имеющим родителей, абсолютно самостоятельным, и работающим только по мере необходимости, при обращении к нему как к классу:

Main.cs
public class Main{

public int test = 134;

public static string fn(int a=0){

return "qwerty"+a;
}
}

 - Просто кладём этот скрипт куда-либо в проекте, и всё.

Но к этому классу, напрямую обратиться мы не сможем, так как не имеем доступа:
 
Цитировать
An object reference is required to access non-static member `object.GetType()'
Цитировать
Для доступа к нестатическим членам, требуется ссылка на объект 'object.GetType()'

Так же точно, не имеем доступа и к его публичным переменным, и к публичным методам.

Делаем переменную доступной из других скриптов

Но если при объявлении переменной, указать что она статическая (тоесть, принадлежащая именно этому классу), как она становится доступной из любого другого скрипта:

Main.cs
public class Main{

public static int test = 134;

public static string fn(int a=0){

return "qwerty"+a;
}
}

И теперь без проблем получаем доступ к этой переменной, из любого другого скрипта:
Debug.Log(Main.GetType());
Main.test = 777;
Debug.Log(Main.test);
Main.test ++;
Debug.Log(Main.test);
При этом, сам класс Main, по прежнему скрыт от всех, не видим и недоступен. Это иногда может быть удобно и полезно.

Так же можно в любом скрипте C#, получить переменную из независимого класса JS, только для этого, скрипт JS, обязательно  должен находится в папке Plugins:

Main.js:
#pragma strict
public static var test:int = 5;
« Последнее редактирование: Август 02, 2014, 17:55:12 pm от Mimi Neko »

Январь 18, 2014, 08:50:09 am
Ответ #2

Mimi Neko

  • Администратор
  • Старожил форума

  • Оффлайн
  • *****

  • 2456
  • Репутация:
    153
    • Просмотр профиля
Экземпляры класса

Другая очень полезная возможность - создание экземпляров класса.
В таком случае, сам класс можно рассматривать как некий шаблон,  с которого мы можем создать любое количество его копий, заполнять их данными, получать из них данные, вызывать их методы, а сами эти копии, можно удобно инициализировать, и хранить в массивах, списках, хэш таблицах.

Вот наш класс-шаблон, скрытый от всех, и недоступный:

Main.cs
public class Main{

public int test ;

public string fn(int a=0){

return "qwerty"+a;
}
}

К нему нет доступа. Нет доступа к его переменным и методам.
Но мы можем создать его экземпляр (назависимую копию):
Main m = new Main();
А теперь, работать с ней:
m.test = 777;
Debug.Log(m.test);

Debug.Log(m.fn(12));

Стоит отметить, что экземпляр m унаследовал у класса Main не только всю его структуру, но и значения его переменных. Это тоже весьма полезно.
« Последнее редактирование: Июнь 24, 2015, 00:28:53 am от Mimi Neko »

Январь 18, 2014, 10:23:38 am
Ответ #3

Mimi Neko

  • Администратор
  • Старожил форума

  • Оффлайн
  • *****

  • 2456
  • Репутация:
    153
    • Просмотр профиля
Конструкторы класса

Конструктор класса - одноимённая классу функция, выполняющая какую-либо внутреннюю инициализацию, при создании экземпляра класса.


конструктор по умолчанию:
public class Main{

public int test;

              //конструктор без параметров (базовый):
public Main(){

test = 12;
}
}
- он не принимает параметров,  и просто проводит инициализацию определённых переменных класса, при создании экземпляра класса без параметров:

Main m = new Main();
Debug.Log(m.test);
- Видно, что при создании экземпляра, переменная test была предварительно установлена в значение 12.

Конструктор с параметрами
Конструктор с параметрами, позволяет нам создавать экземпляр класса, с заданным значением переменных, что тоже весьма удобно, и может использоваться как при создании итемов, так и при создании любых игровых объектов и персонажей, имеющих много параметров и статов.

Конструктор с параметрами:
public class Main{

public int test;

              //конструктор без параметров (базовый):
public Main(){

test = 0;
}

              //конструктор с параметрами:
public Main(int t){

test = t;
}

}

И теперь, мы можем создавать экземпляр класса как без аргуметов (задействуется конструктор без параметров), так и с аргументами (задействуется конструктор с аргументами):
Main m = new Main();
Debug.Log(m.test);

Main m1 = new Main(13);
Debug.Log(m1.test);

 - В логе увидим 0, и затем 13.
А если бы в классе небыло конструктора без параметров, то при попытке создания экземпляра без параметров Main m = new Main();
мы бы получили ошибку, как и если бы пытались создать экземпляр с параметрами, не имея в нём конструктора, способного эти параметры принять.
« Последнее редактирование: Август 08, 2014, 15:55:04 pm от Mimi Neko »

Январь 18, 2014, 11:04:20 am
Ответ #4

Mimi Neko

  • Администратор
  • Старожил форума

  • Оффлайн
  • *****

  • 2456
  • Репутация:
    153
    • Просмотр профиля
Статические переменные в экземплярах класса

Если в классе есть переменная, объявленая статической, то она не будет доступна в экземплярах этого класса, но будет присутствовать в каждом экземпляре, и будет общей для класса, и всех его экземпляров.
Зато к ней будет полный доступ, через имя класса.
Таким образом, мы можем через родительский класс, производить изменение нужных нам параметров, стразу во всех его экземплярах, или просто производить исходную предустановку класса:
public class Main{

public static int test = 0;

public int fn(int x=0){

test += x;
return test;
}
}

Maint.test = 777;
Main m = new Main();
Debug.Log(m.fn(1));
Результат: 778

или:
Main m = new Main();
Maint.test = 777;
Debug.Log(m.fn(1));
Результат: 778

или:
Main m = new Main();
Maint.test = 777;
Debug.Log(m.fn(1));
Main m2 = new Main();
Debug.Log(m2.fn(1));
Результат: 778, 779

 Ещё раз обращу внимание на то, что это даёт возможность, через обращение к родительскому классу, вносить изменения, сразу во все его дочерние экземпляры!
Это весьма мощный метод. представте, что есть метод, создающий мобов в игре.  его экземпляры - это и есть сами мобы.
И нам нужно управлять рейтами дропа, сразу всех мобов, либо каким-то ещё их параметром, к примеру, их сложностью (силой, количеством хп, уроном, скоростью).  И это мы можем легко сделать, не перебирая всех мобов, а просто обратившись к их родительскому классу, и изменив как нам нужно, определённые переменные-коэффициенты.
« Последнее редактирование: Январь 18, 2014, 11:15:21 am от Mimi Neko »

Январь 18, 2014, 14:27:16 pm
Ответ #5

Mimi Neko

  • Администратор
  • Старожил форума

  • Оффлайн
  • *****

  • 2456
  • Репутация:
    153
    • Просмотр профиля
Инкапсуляция

Вот класс, с доступной любому скрипту извне, переменной test:
public class Main{

public static int test = 0;

}


Считается, что инкапсуляция делает обращение с переменными экземпляров классов, более безопасным.
Инкапсуляция - это методы сокрытия переменных, и механизмов их обработки.

Вот пример инкапсуляции:
public class Main{

private int test;
public int Test{

    get{return test;}
                  set{ test = value;}
              }
}

 - Как видим, сама переменная test, тут абсолютно скрыта и недоступна извне.
А вся работа с ней, происходит через некий "интерфейс", с именем Test,
который имеет для этого два метода - get и set
get обрабатывает выдачу данных из переменной, а set - ввод данных в переменную извне.
принятое через set значение, будет доступно через свойство интерфейса - value
Внутри этих функций, может происходить проверка и обработка данных.

Само же обращение к переменной из других скриптов, останется таким же простым:
Обращение через экземпляр класса:
Main m = new Main();
m.Test = 1;
Debug.Log(m.Test);
= 13
Причём, по умолчанию, значение переменной будет = 0, даже если ей небыло присвоено значение.

Так же возможно обращаться и напрямую, через имя класса, но для этого, придётся интерфейс объявить статическим.
А поскольку статические функции внутри класса не имеют доступ к нестатическим переменным - придётся объявить статической и саму переменную:
public class Main{

private static int test;
public static int Test{

    get{return test;}
                  set{ test = value;}
              }
}

Main.Test = 13;
Debug.Log(Main.Test);
= 13

Интерфейс может иметь и лишь одну из этих функций, только get, или только set, и соответственно будет служить либо только для чтения, либо только для записи.
Так же для обслуживания данной переменной, могут быть созданы 2 и более интерфейсов, по разному обрабатывающие или проверяющие данные. К примеру, один интерфейс для ввода значения, а другой - для вывода. Или  один для ввода значения типа int, а другой - для ввода типа float.

К сожалению (а может и нет), Unity3d не позволяет пытаться передать не соотвесттвующий тип данных, через такой интерфейс, в отличии от традиционного C#.  Если бы это работало, то с помощью set, можно было бы исключать ошибки, на лету преобразовывая данные в нужный тип, либо никак не реагируя на невозможность их преобразования.

Вот что-то навроде этого (не работает в Unity3d) :
public class Main{

private string test;
public string Test{

    get{return test;}
                  set{
                         test = value;
                         test = Convert.ToString(value);
                  }
}

Main m = new Main();
m.Test = 15;
Debug.Log(m.Test);
Цитировать
Cannot implicitly convert type `int' to `string'
Не неявное преобразование типа `int', `string'

И даже исхитриться, применив public var Test{, к сожалению тоже не удалось...

Если кто-то знает способ это под Unity3d реализовать - пишите, очень было бы интересно.
« Последнее редактирование: Январь 19, 2014, 10:13:52 am от Mimi Neko »

Январь 18, 2014, 14:32:52 pm
Ответ #6

Mimi Neko

  • Администратор
  • Старожил форума

  • Оффлайн
  • *****

  • 2456
  • Репутация:
    153
    • Просмотр профиля
Интересные статьи и справочные материалы, по теме классов в C#:

http://msdn.microsoft.com/ru-ru/Library/0b0thckt
http://msdn.microsoft.com/ru-ru/Library/ms173115
http://ru.wikipedia.org/wiki/Инкапсуляция_(программирование)
http://src-code.net/predpochitajte-staticheskie-klassy-chleny-nestaticheskim/
http://www.michaelgarforth.com/archives/2011/04/sane-data-encapsulation/

<a href="http://www.youtube.com/watch?v=j0RPWG3Reno" target="_blank">http://www.youtube.com/watch?v=j0RPWG3Reno</a>

Если вам есть чем интересным  дополнить - пожалуйста пишите, дополняйте.

Январь 19, 2014, 13:46:26 pm
Ответ #7

Mimi Neko

  • Администратор
  • Старожил форума

  • Оффлайн
  • *****

  • 2456
  • Репутация:
    153
    • Просмотр профиля
Доступ к классам, дочерним от основного родительсткого класса MonoBehaviour:

Нужно отметить, что классы-потомки от MonoBehaviour, по сути обладают теми же свойствами, что и свободные классы.
Они как бы совмещают в себе, возможности и MonoBehaviour и свободных классов.
Это может быть так же очень полезно.
Вот скрипт с классом, который может так же как обычный прстой свободный класс, лежать где-либо в проекте, и к нему можно обращаться по имени класса, ну и конечно же, он может быть прикреплён к любому объекту.

И при этом, в обоих случаях, мы имеем простой доступ к его переменным и методам, так же как и со свободными классами:

Main.cs
public class Main : MonoBehaviour {

    public static string test = "xxx";
}

Доступ к переменной класса, через имя класса:
Main.test = "234";
Debug.Log(Main.test);


Доступ к переменной класса, через создание экземпляра класса:

Main.cs
public class Main : MonoBehaviour {

    public string test = "xxx";
}


Main m = new Main();
m.test = "123";
Debug.Log(m.test);


 - Фактически в первом примере, у нас получился глобальный класс с глобальной переменной, с которой мы можем работать, в любом другом скрипте.

Ноябрь 27, 2014, 03:29:03 am
Ответ #8

Mimi Neko

  • Администратор
  • Старожил форума

  • Оффлайн
  • *****

  • 2456
  • Репутация:
    153
    • Просмотр профиля
Хитрый финт ушами, с наследованием классов в Unity3d:

этот скрипт просто валяется где-то в проекте, ни к чему не прикреплен:
using UnityEngine;

public class mainTestClass : MonoBehaviour{

public int test = 133;
}

А этот скрипт висит на объекте:
using UnityEngine;

public class test1 : mainTestClass {

void Start(){

Debug.Log(test);
}

}

и он не только имеет доступ к переменной test первого скрипта, но даже отображает её состояние в инспекторе.



Так же возможно использование модификаторов доступа к данной переменной, и конструктора:

using UnityEngine;

public class mainTestClass : MonoBehaviour{

public int test{get; set;}

public mainTestClass(){

test = 135;

}

void Awake(){

test++;//136

}

void Start(){

test += 100;//а это действие выполнится уже позже вызова Start у наследника

}

}
Но при использовании модификатора доступа, переменная перестанет отображаться в инспекторе.
« Последнее редактирование: Ноябрь 27, 2014, 04:15:53 am от Mimi Neko »