Какой самый оптимальный способ общения между скриптами в Unity - PullRequest
0 голосов
/ 16 сентября 2018

Скажем, например, что у меня есть GameObject A с прикрепленным скриптом Manager, который при запуске порождает x количество GameObjects с прикрепленным скриптом B.

Другой GameObject со сценарием C должен что-то делать, когда GameObject со сценарием B говорит об этом.

Итак, вопрос в том, как лучше всего общаться этим трем?

Очевидно, сценарий B мог бы просто вызывать сценарий C, однако мне кажется, что этому методу не хватает структуры и организации.

Сценарий A также может иметь ссылку на сценарий C, а сценарий B может указывать сценарию A действовать на сценарий C.

Я чувствую, что есть какое-то правило, которому я должен следовать, однако я еще не сталкивался с ним. Любая помощь очень ценится!

Ответы [ 4 ]

0 голосов
/ 22 сентября 2018

Делегаты и События в основном используются для общения, как писал Программист.

Для лучшей структуры и организации я бы предложил использовать шаблон MVC или любой другой шаблон проектирования, который вам нравится.Здесь вы можете найти отличный пример реализации MVC для Unity3D с простой, но мощной системой уведомлений:

Unity с MVC от Эдуардо Диаса да Коста

В этом примере вы надеваетене нужно использовать делегатов / мероприятия для общения, и вы все хорошо организовываете.

Некоторые коммуникационные функции, используемые в цитируемом учебнике в случае, если ссылка устарела:

1.

// Iterates all Controllers and delegates the notification data
   // This method can easily be found because every class is “BounceElement” and has an “app” 
   // instance.
   public void Notify(string p_event_path, Object p_target, params object[] p_data)
   {
      BounceController[] controller_list = GetAllControllers();
      foreach(BounceController c in controller_list)
      {
         c.OnNotification(p_event_path,p_target,p_data);
      }
   }

   // Fetches all scene Controllers.
   public BounceController[] GetAllControllers() { /* ... */ }

2.

 // This class will give static access to the events strings.
class BounceNotification
{
   static public string BallHitGround = “ball.hit.ground”;
   static public string GameComplete  = “game.complete”;
   /* ...  */
   static public string GameStart     = “game.start”;
   static public string SceneLoad     = “scene.load”;
   /* ... */
}

3.

 // Handles the ball hit event
   public void OnNotification(string p_event_path,Object p_target,params object[] p_data)
   {
      switch(p_event_path)
      {
         case BounceNotification.BallHitGround:
            app.model.bounces++;
            Debug.Log(“Bounce ”+app.model.bounce);
            if(app.model.bounces >= app.model.winCondition)
            {
               app.view.ball.enabled = false;
               app.view.ball.GetComponent<RigidBody>().isKinematic=true; // stops the ball
               // Notify itself and other controllers possibly interested in the event
               app.Notify(BounceNotification.GameComplete,this);            
            }
         break;

         case BounceNotification.GameComplete:
            Debug.Log(“Victory!!”);
         break;
      } 
   }

4.

   // Callback called upon collision.
   void OnCollisionEnter() { app.Notify(BounceNotification.BallHitGround,this); }

Конечно, вы все равно можете реализовать MVC и использовать Делегаты и События.Это просто, чтобы показать другой способ ведения дел.

0 голосов
/ 17 сентября 2018

Очевидно, сценарий B мог бы просто вызывать сценарий C, однако мне кажется, что этому методу не хватает структуры и организации.

True.Для этого используется функция GameObject.SendMessage.К сожалению, это медленно, и я бы не рекомендовал это, но стоит упомянуть.

Если у вас есть много объектов, которые должны будут взаимодействовать с другими объектами, реализуйте менеджер событий с помощью event и delegate.Это правильный способ сделать это.Вы можете найти полную EventManager реализацию здесь .

С ее помощью вы можете зарегистрировать любое количество функций для события с помощью:

EventManager.StartListening("jump", someFunction);

Отмените регистрацию любой функции из события с помощью:

EventManager.StopListening("jump", someFunction);

Оттуда вы можете вызвать событие для любого объекта, прослушивающего ее:

EventManager.TriggerEvent("jump");
0 голосов
/ 17 сентября 2018

Вы также можете использовать не специфичные для Unity Action делегаты.Мне нравится использовать статический класс для этого, но вы также можете реализовать его в одном из существующих классов (если вы используете static члены и методы)

Например,

public static class MyEvents 
{
    public static event Action SomeEvent;

    public static void InvokeSomeEvent()
    {
         // Make sure event is only invoked if someone is listening
         if (SomeEvent == null) return;

        SomeEvent.Invoke();
    }
}

Это делает ваши классы полностью независимыми (ну, ладно, они разделяют класс MyEvents) и легко модульным.

В сценарии C добавьте "слушатель", например

private void Start()
{
    // It is save to remove a listener also if it wasn't there yet
    // This makes sure you are not listening twice by accident
    MyEvents.SomeEvent -= OnSomeEvent;


    // Add the listener for that event
   MyEvents.SomeEvent += OnSomeEvent;
}

private void OnSomeEvent ()
{
    // Do something if SomeEvent is invoked
}

Затем где-нибудьв сценарии B просто позвоните

MyEvents.InvokeSomeEvent();

Так что класс B не должен знать или заботиться о том, кто слушает это событие;он просто вызывает его и заботится о своем собственном деле.

С другой стороны C или (любой другой класс, в который вы добавляете прослушиватель для события) не должен знать / заботиться о том, откуда пришел вызовот;он просто обрабатывает его и выполняет свои функции.

Обратите внимание, однако, что это также усложняет отладку, поскольку уже не так просто определить, откуда взялся этот вызов;)


Примечание: Вы также можете добавить параметры в Action, например,

public static event Action<int> SomeParameterEvent;

. В этом случае, конечно, все методы должны также реализовать этот параметр

public static InvokeSomeParameterEvent(int value)
{
    if(SomeParameterAction == null) return;

    SomeParameterEvent.Invoke(value);
}

In C (слушатель) вы также должны получить параметры

// name can be changed
private void OnSomeParameterEvent(int value)
{
    //...
}

И, конечно, также вызвать его с параметром в B

MyEvents.InvokeSomeParameterEvent(someInt);

И чем вы можете взять его дажедалее и вместо значения или ссылки передайте полный метод delegate в качестве параметра.Смотрите примеры здесь

0 голосов
/ 16 сентября 2018

Если A уже имеет ссылку на скрипт C, он может передать эту ссылку B, когда он будет создан.Таким образом, B может общаться с C, не проходя через A.

, т.е.

Script A:
// variables
     public ScriptC c;

// methods
void SpawnB(){
    // spawn B
    B.setC(c); // B's variable for script C is passed in from A
}

Script B:
// variables
     ScriptC c;
// methods
void setC(ScriptC v){
    c = v;
}

Что-то в этом роде.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...