Événements d'unité. Fonctions événementielles. Événements de mise à jour régulière

Bonjour à tous. Aujourd'hui, je veux parler de la façon dont vous pouvez implémenter des événements de jeu, les présenter, ainsi que de la façon de créer un système d'événements de jeu tout à fait acceptable dans Unity en deux clics sans recourir à différentes UnityActions.

Note: Cet article n'est pas la vérité des vérités. Ceci n'est que mon idée et mon expérience.

Je n’ai pas le temps de faire des photos pour l’article, mais je dois le faire ! C'est pourquoi la photo vient de Google !

Laissez-moi vous donner un exemple avec un ascenseur. Qu'est-ce que l'ont sait à propos de lui? Il peut monter ou descendre en fonction de la pression sur le bouton. Cependant, il convient de rappeler
qu'il ouvre/ferme également les portes, joue du son et peut-être autre chose. J'ai depuis longtemps l'habitude de considérer beaucoup de choses de manière abstraite,
Par conséquent, je peux dire que les événements du jeu peuvent être divisés en deux éléments. Le premier est celui des gestionnaires. La seconde concerne les événements.
Les gestionnaires sont des scripts qui déclenchent des événements de jeu. J'ai divisé l'appel d'événement en plusieurs options :

  1. Linéaire. Cette option signifie qu'un nombre N d'événements sont exécutés après le suivant.
  2. Parallèle. Déclencher plus d'un événement sur un seul gestionnaire.
  3. Mixte. Il s'agit d'un mélange de la première et de la deuxième options.

Revenons à l'ascenseur. Vous pouvez immédiatement dire que l'ascenseur appartient à l'option n°3. Laisse-moi expliquer. Lorsque nous appuyons sur le bouton, l'événement de mouvement de l'ascenseur est déclenché. Ensuite, lorsqu'il se trouve à votre étage, les portes s'ouvrent et, par exemple, un son est émis. L'ouverture de la porte et le bruit se produisent en parallèle, mais après le mouvement. Vous pouvez immédiatement l'imaginer dans la mise en œuvre du jeu. Mais plus tard.

Pour que vous compreniez ce que j'entends par handlers, voici des exemples : Triggers, boutons enfoncés (touches du clavier), rayons (Raycast), etc.
Eh bien, sous les déclencheurs : jouer des sons, déplacer des objets, etc.

De manière abstraite, j'ai créé l'architecture suivante :

Cela semble assez simple, n'est-ce pas ? Même en termes logiciels, il n'est pas nécessaire de charger les convolutions (même s'il m'a été difficile de proposer une implémentation)

Voici à quoi ressemble la classe de base de tous les événements :

Utilisation d'UnityEngine ; // Système.Collections.Generics; classe abstraite publique CoreEvent : MonoBehaviour ( public CoreEvent nextEvent ; // Événement suivant // Liste publique listeÉvénements ; public abstrait void Action(); )

Dans les commentaires, j'ai indiqué une liste d'événements qui pourraient/devraient se déclencher. petit exemple :

Utilisation d'UnityEngine ; classe publique LookEvent : CoreEvent ( public override void Action() ( //if(nextEvent != null) nextEvent.Action(); //else print("Look"); ) )

Dans cet exemple, je déclenche uniquement l'événement suivant. Pour une liste/un tableau, utilisez une boucle. Si un événement devait se déclencher,
Nous ne précisons rien dans le champ nextEvent et c'est tout, car Dans l'implémentation, nous avons déjà pris en charge le traitement des valeurs nulles.

Un point concernant la mise en œuvre. À l’origine, la classe d’événements de base n’était pas censée être abstraite. Je vais vous expliquer pourquoi. Comme tu l'as vu
eux-mêmes, la condition pour la valeur nulle est dans l'un des événements. Autrement dit, une telle condition est faite pour chaque événement. En fait, cela n'est pas nécessaire si vous n'utilisez pas de traitement parallèle. Initialement, la classe n'était pas abstraite et la méthode Action() était virtuelle
ce qui a permis d'archiver la classe de base en l'écrivant une fois, puis de le faire comme ceci :

Public override void Action() ( base.Action(); //Une fonction)

Cependant, ici, c'est plus pratique pour tout le monde. J'ai décidé de le rendre abstrait car cela réduisait considérablement la quantité de code dans la classe d'événement de base + ceci,
D'un point de vue programmation, cela a plus de sens si vous connaissez la définition du mot-clé abstrait.

Classe publique HandlerEvents : MonoBehaviour (liste publique événements; // Spécifie la classe de base. public virtual void Launch() ( pour (byte countEvent = 0; countEvent< events.Count; countEvent++) { if (events != null) events.Action(); // Вызываем только те события, которые указаны в листе (не null). } } } public enum TriggerType { Enter, Stay, Exit } public class TriggerHandler: HandlerEvents { public TriggerType triggerType; private void OnTriggerEnter2D(Collider2D other) { if (triggerType == TriggerType.Enter && other.GetComponent()) Lancement(); ) private void OnTriggerExit2D(Collider2D autre) ( if (triggerType == TriggerType.Exit && other.GetComponent ()) Lancement(); ) privé void OnTriggerStay2D(Collider2D autre) ( if (triggerType == TriggerType.Stay && other.GetComponent ()) Lancement(); ) )

Un code assez universel, n'est-ce pas ? Il s'agit d'une approche très polyvalente, puisque vous pouvez passer un appel d'événement, qui appellera d'autres événements, et ainsi de suite jusqu'à ce que vous vous ennuyiez. J'ai créé un actif avec une technologie similaire et il est là depuis six mois, mais j'ai décidé de le partager ici car cette version est obsolète.

Cela semble être tout. Si vous avez des questions, posez-les !

Bientôt, je parlerai de nombreux principes intéressants que j'utilise et qui peuvent être utiles lorsque je travaille avec Unity.
Eh bien, il y a l'organisation du projet, la lecture à partir de XML, l'organisation du code, etc. Bientôt!

Les événements dans Unity3D sont divisés en trois grands groupes :

Événements déclenchés par des événements pétroliers (chargement de scène, sortie utilisateur)
Ce groupe d'événements se déroule de manière irrégulière

Événements appelés lors du dessin d'un cadre
Dans ce cas, tous les scripts utilisés sont appelés dans le cycle de dessin d'écran, ce qui signifie qu'ils affecteront directement les FPS (images par seconde). Par conséquent, vous devez ici travailler très soigneusement avec des fonctions qui nécessitent beaucoup de temps de traitement.

Événements déclenchés lors des calculs physiques
Et le dernier groupe. Pour calculer la physique, un thread distinct et indépendant est créé, dans lequel les événements sont appelés à un certain intervalle de temps. La taille de cet intervalle peut être configurée dans l'élément de menu : Édition -> Paramètres du projet -> Heure -> Pas de temps fixe.

Connaissant cette répartition, vous pouvez déjà prendre des décisions quant à l'endroit où il est préférable de placer quel code.
Cependant, tous les calculs effectués tant lors des calculs physiques que lors du rendu affectent la « réactivité » du jeu. Par conséquent, lors du développement d’une application, il est conseillé de mettre en œuvre les calculs les plus gourmands en ressources dans Coroutines.

Passons maintenant à la traduction de la section d'aide.

Ordre des fonctions d'événement
Dans Unity3D, un certain nombre d'événements sont exécutés dans un ordre spécifique. Nous décrirons cette procédure ci-dessous :

Éveillé: Cette fonction est toujours appelée avant le démarrage de toute fonction, ainsi qu'immédiatement après l'initialisation du préfabriqué.
Activé :(appelée si l'objet est actif) : Cette fonction n'est appelée qu'une fois l'objet activé.

Avant la première mise à jour du cadre

Commencer: Appelé avant de dessiner la première image uniquement si un script est défini.

Entre les images

SurApplicationPause : Cet événement est déclenché à la fin d'une trame lorsqu'une pause est détectée, effectivement entre les mises à jour normales de la trame. Après OnApplicationPause, un cadre supplémentaire est dessiné pour afficher la fenêtre affichée pendant la pause.

Procédure de mise à jour
Pour suivre la logique du jeu, les interactions et animations d'objets, la position de la caméra, etc., vous pouvez utiliser plusieurs événements différents. Le mécanisme général permettant d'accomplir la plupart des tâches réside dans la fonction Update(), mais il existe également d'autres fonctions.

Mise à jour fixe : FixedUpdate() est indépendant de Update() et peut être appelé plus ou moins souvent (généralement appelé moins souvent si le FPS est suffisamment élevé). Cet événement peut être déclenché plusieurs fois par image si le FPS est faible et peut ne pas être déclenché du tout entre les images si le FPS est élevé. Tous les calculs et mises à jour du moteur physique ont lieu immédiatement après FixUpdate(). Lorsque vous appliquez des calculs de mouvement dans FixedUpdate(), vous n'avez pas besoin de multiplier votre valeur par Time.deltaTime. En effet, FixedUpdate() est appelé à partir d'un temporisateur indépendant de la fréquence d'images.
Mise à jour: Update() est appelé une fois par image. C'est l'événement principal pour dessiner le cadre.
Mise à jour tardive : LateUpdate() est appelée une fois par image, une fois Update() terminé. Tous les calculs effectués dans Update() seront terminés lorsque LateUpdate() sera appelé. L'utilisation principale de LateUpdate() est généralement le suivi de caméra à la troisième personne. Si vous déplacez votre personnage dans l'événement Update(), vous pouvez alors déplacer la caméra et calculer son emplacement dans l'événement LateUpdate(). Cela garantira que le personnage a marché jusqu'au bout devant la caméra et a sécurisé sa position.

Rendu de la scène

SurPreCull : Appelé avant que la scène ne soit assemblée sur la caméra. L'assemblage détermine quels objets sont visibles par la caméra. OnPreCull est appelé uniquement si la scène est « coupée » d'objets invisibles.
OnDevientVisible / OnDevientInvisible : Appelé lorsqu'un objet devient visible/invisible pour n'importe quelle caméra.
SurWillRenderObject : Appelé une fois par caméra si l'objet est visible.
SurPréRendu : Appelé avant que la caméra ne commence à restituer la scène
SurRenderObject : Appelé lorsque tous les objets de la scène ont été dessinés. Vous pouvez utiliser les fonctions GL ou Graphics.DrawMeshNow pour créer vos propres dessins sur cette caméra.
SurPostRender : Appelé après le rendu de la scène sur la caméra.
OnRenderImage (version Pro uniquement) : Appelé après le rendu de la scène pour post-traiter l'image à l'écran.
Sur l'interface graphique : Appelé plusieurs fois par image en réponse aux événements d'interface. Les événements de position et de remplissage de couleur sont traités en premier, suivis par les événements de saisie clavier/souris.
OnDrawGizmos : Utilisé pour dessiner le Gizmo sur scène.

Coroutines
En règle générale, un appel de coroutine est effectué après le retour de la fonction Update(). Une coroutine est une fonction qui peut suspendre l'exécution (rendement) jusqu'à ce qu'elle soit exécutée. Différentes utilisations des Coroutines :

rendement: la coroutine continuera après toutes les fonctions Update() appelées dans l'image suivante.
rendement WaitForSeconds(2) : Continuer après le délai spécifié lorsque toutes les fonctions Update() ont déjà été appelées dans le cadre
rendement WaitForFixedUpdate() : Continue lorsque toutes les fonctions FixUpdate() ont déjà été appelées
rendement WWW : Continue lorsque le chargement du contenu WWW est terminé.
rendement StartCoroutine (MyFunc): Associations de coroutine, l'appel de coroutine attendra la fin de la fonction MyFunc.

Destruction d'objets

SurDestroy : Cette fonction est appelée sur la dernière image de l'existence d'un objet (l'objet peut être détruit en réponse à Object.Destroy ou à la fermeture de la scène).

A la sortie
Ces fonctions sont appelées pour tous les objets actifs de la scène :

SurApplicationQuit : Cette fonction est appelée sur tous les objets du jeu avant la fermeture de l'application. Dans l'éditeur, cela se produit lorsque l'utilisateur arrête PlayMode. Dans le lecteur Web, cela se produit lorsque le lecteur Web est fermé.
ActivéDésactivé : Cette fonction est appelée lorsqu'un objet est désactivé ou devient inactif.

Ainsi, l'ordre suivant d'exécution du script se produit :

Tous les événements Éveillé
Tous les événements de démarrage
boucle (avec incréments en variable de temps delta)
-toutes les fonctions FixeUpdate
- développement du moteur physique
- déclencher des événements OnEnter/Exit/Stay
-événements de collision OnEnter/Exit/Stay
Transformation du corps rigide, selon transform.position et rotation
OnMouseDown/OnMouseUp autres événements d'entrée
Tous les événements Update()
Animation, mélange et transformation
Tous les événements LateUpdate
Le rendu

Conseil
Si vous exécutez des coroutines dans LateUpdate, elles seront également appelées après LateUpdate juste avant le rendu.
Les coroutines sont exécutées après toutes les fonctions Update().

Lorsque vous travaillez avec le moteur de jeu Unity3D, l'un des premiers concepts que vous devez apprendre est l'ordre dans lequel les événements sont appelés dans le jeu et le traitement de ces mêmes événements.

Bien qu'il existe de nombreuses informations en russe sur ce moteur, je n'ai pas trouvé de description correcte des événements. Mais j'ai trouvé l'article en anglais Ordre d'exécution des fonctions d'événement dans l'aide d'Unity3D.

Préface du traducteur

Mais, avant de commencer les informations officielles, je tiens à noter que les événements dans Unity3D sont divisés en quatre grands groupes :

  1. Événements appelés lors du dessin d'un cadre
    Dans ce cas, tous les scripts utilisés sont appelés dans le cycle de dessin d'écran, ce qui signifie qu'ils affecteront directement les FPS (images par seconde). Par conséquent, vous devez ici travailler très soigneusement avec des fonctions qui nécessitent beaucoup de temps de traitement.
  2. Événements déclenchés lors des calculs physiques
    Pour calculer la physique, un thread distinct et indépendant est créé, dans lequel les événements sont appelés à un certain intervalle de temps. La taille de cet intervalle peut être configurée dans l'élément de menu : Édition -> Paramètres du projet -> Heure -> Pas de temps fixe.
  3. Coroutines (Coroutines).
    Et le dernier groupe. Si c'est très éloigné, ils peuvent alors être comparés à des processus distincts, mais comme Unity3D ne permet pas la création de threads séparés, il s'agit d'une sorte de compromis. Il permet d'interrompre les calculs, de donner des ressources au thread principal, puis de reprendre la partie suivante des calculs. Un exemple frappant est le recalcul constant du coût des marchandises dans différents points de vente. Il fonctionnera tout seul, ne ralentira pas le processus de jeu et les prix en magasin seront toujours à jour.

Connaissant cette répartition, vous pouvez déjà prendre des décisions quant à l'endroit où il est préférable de placer quel code.

Cependant, tous les calculs effectués tant lors des calculs physiques que lors du rendu affectent la « réactivité » du jeu. Par conséquent, lors du développement d'une application, n'oubliez pas de diviser les calculs les plus gourmands en ressources en étapes et de les organiser en coroutines.

Passons maintenant à la traduction de la section d'aide.

Ordre des fonctions d'événement

Dans Unity3D, un certain nombre d'événements sont exécutés dans un ordre spécifique. Nous décrirons cette procédure ci-dessous :

Charger la scène pour la première fois

Ces fonctions sont appelées au démarrage de la scène (une fois pour chaque objet du cadre).

  • Éveillé: Cette fonction est toujours appelée avant le démarrage de toute fonction, ainsi qu'immédiatement après l'initialisation du préfabriqué.
  • Activé :(appelée si l'objet est actif) : Cette fonction n'est appelée qu'une fois l'objet activé.

Avant la première mise à jour du cadre

  • Commencer: Appelé avant de dessiner la première image uniquement si un script est défini.

Entre les images

  • SurApplicationPause : Cet événement est déclenché à la fin d'une trame lorsqu'une pause est détectée, effectivement entre les mises à jour normales de la trame. Après OnApplicationPause, un cadre supplémentaire est dessiné pour afficher la fenêtre affichée pendant la pause.

Procédure de mise à jour

Pour suivre la logique du jeu, les interactions et animations d'objets, la position de la caméra, etc., vous pouvez utiliser plusieurs événements différents. Le mécanisme général permettant d'accomplir la plupart des tâches réside dans la fonction Update(), mais il existe également d'autres fonctions.

  • Mise à jour fixe : FixedUpdate() est indépendant de Update() et peut être appelé plus ou moins souvent (généralement appelé moins souvent si le FPS est suffisamment élevé). Cet événement peut être déclenché plusieurs fois par image si le FPS est faible et peut ne pas être déclenché du tout entre les images si le FPS est élevé. Tous les calculs et mises à jour du moteur physique ont lieu immédiatement après FixUpdate(). Lorsque vous appliquez des calculs de mouvement dans FixedUpdate(), vous n'avez pas besoin de multiplier votre valeur par Time.deltaTime. En effet, FixedUpdate() est appelé à partir d'un temporisateur indépendant de la fréquence d'images.
  • Mise à jour: Update() est appelé une fois par image. C'est l'événement principal pour dessiner le cadre.
  • Mise à jour tardive : LateUpdate() est appelée une fois par image, une fois Update() terminé. Tous les calculs effectués dans Update() seront terminés lorsque LateUpdate() sera appelé. L'utilisation principale de LateUpdate() est généralement le suivi de caméra à la troisième personne. Si vous déplacez votre personnage dans l'événement Update(), vous pouvez alors déplacer la caméra et calculer son emplacement dans l'événement LateUpdate(). Cela garantira que le personnage a marché jusqu'au bout devant la caméra et a sécurisé sa position.

Rendu de la scène

  • SurPreCull : Appelé avant que la scène ne soit assemblée sur la caméra. L'assemblage détermine quels objets sont visibles par la caméra. OnPreCull est appelé uniquement si la scène est « coupée » d'objets invisibles.
  • OnDevientVisible / OnDevientInvisible : Appelé lorsqu'un objet devient visible/invisible pour n'importe quelle caméra.
  • SurWillRenderObject : Appelé une fois par caméra si l'objet est visible.
  • SurPréRendu : Appelé avant que la caméra ne commence à restituer la scène
  • SurRenderObject : Appelé lorsque tous les objets de la scène ont été dessinés. Vous pouvez utiliser les fonctions GL ou Graphics.DrawMeshNow pour créer vos propres dessins sur cette caméra.
  • SurPostRender : Appelé après le rendu de la scène sur la caméra.
  • SurRenderImage(Version Pro uniquement) : Appelé après le rendu de la scène, pour post-traiter l'image à l'écran.
  • Sur l'interface graphique : Appelé plusieurs fois par image en réponse aux événements d'interface. Les événements de position et de remplissage de couleur sont traités en premier, suivis par les événements de saisie clavier/souris.
  • OnDrawGizmos : Utilisé pour dessiner le Gizmo sur scène.

Coroutines

En règle générale, un appel de coroutine est effectué après le retour de la fonction Update(). Une coroutine est une fonction qui peut suspendre l'exécution (rendement) jusqu'à ce qu'elle soit exécutée. Différentes utilisations des Coroutines :

  • rendement: la coroutine continuera après toutes les fonctions Update() appelées dans l'image suivante.
  • rendement WaitForSeconds(2) : Continuer après le délai spécifié lorsque toutes les fonctions Update() ont déjà été appelées dans le cadre
  • rendement WaitForFixedUpdate() : Continue lorsque toutes les fonctions FixUpdate() ont déjà été appelées
  • rendement StartCoroutine (MyFunc): Associations de coroutine, l'appel de coroutine attendra la fin de la fonction MyFunc.

Destruction d'objets

  • SurDestroy : Cette fonction est appelée sur la dernière image de l'existence d'un objet (l'objet peut être détruit en réponse à Object.Destroy ou à la fermeture de la scène).

A la sortie

Ces fonctions sont appelées pour tous les objets actifs de la scène :

  • SurApplicationQuit : Cette fonction est appelée sur tous les objets du jeu avant la fermeture de l'application. Dans l'éditeur, cela se produit lorsque l'utilisateur arrête PlayMode. Dans le lecteur Web, cela se produit lorsque le lecteur Web est fermé.
  • ActivéDésactivé : Cette fonction est appelée lorsqu'un objet est désactivé ou devient inactif.

Ainsi, une fois terminés, les événements sont appelés dans l'ordre suivant :

  • Tous les événements Éveillé
  • Tous les événements de démarrage
  • boucle (avec incréments en variable de temps delta)
    • Toutes les fonctionnalités de FixUpdate
    • tester le moteur physique
    • événements déclencheurs OnEnter/Exit/Stay
    • Événements de collision OnEnter/Exit/Stay
  • Transformation du corps rigide, selon transform.position et rotation
  • OnMouseDown/OnMouseUp autres événements d'entrée
  • Tous les événements Update()
  • Animation, mélange et transformation
  • Tous les événements LateUpdate
  • Le rendu

Si vous exécutez des coroutines dans LateUpdate, elles seront également appelées après LateUpdate juste avant le rendu.

Les coroutines sont exécutées après toutes les fonctions Update().

P.S. ajout de l'utilisateur Leopotam

Coroutine n'est qu'un morceau de code exécuté sur le thread principal. C'est très important à comprendre, car simplement mettre un calcul lourd dans une coroutine et supposer que tout ira bien est fondamentalement faux ; les calculs obstrueront simplement le flux de la même manière que s'ils étaient effectués dans Update ou ailleurs dans le standard. méthodes. Il est nécessaire de diviser les calculs en itérations pour que lors d'une nouvelle itération, le processus continue. L'intérêt des coroutines est d'automatiser l'appel de ces itérations à chaque cycle de rendu.

Par exemple:

IEnumerator FindBozons() ( var isFound = false ; var colliderSectionID = 0 ; var colliderSectionCount = 10 ; while (! isFound) ( // Traitez une seule section à la fois pour réduire la charge isFound = ProcessDataFromSection(colliderSectionID) ; colliderSectionID = (colliderSectionID ++ ) % colliderSectionCount ; rendement retour nul ; ) // Nous achetons des yachts/navires // Fin de la coroutine) void Start() ( StartCoroutine(FindBozons() ) ; )

Le mécanisme de coroutine assurera la sauvegarde automatique de l'état du contexte d'exécution de la fonction et le retour au point d'interruption (yield).


Bonne journée!

Aujourd'hui, je voudrais parler brièvement de l'utilisation d'événements pour éliminer les déchets et fournir des fonctionnalités complexes et dynamiques à votre code. Il est à noter que cet article s’adresse davantage aux débutants qui souhaitent en savoir plus sur les systèmes événementiels.

Nous analyserons toute cette question à l'aide de l'exemple d'un système de contrôle sonore. Ce système nous permettra d'activer/désactiver la musique et les sons dans les paramètres du jeu.

Avant de créer une classe de gestionnaire qui gérera les paramètres de son et de musique, nous allons créer une simple classe sérialisable. Il sera utilisé comme modèle de données à enregistrer en JSON.

Utilisation de System.Collections ; en utilisant System.Collections.Generic ; en utilisant UnityEngine ; //================================================= ============ // AudioSettingsModel // Modèle @usage pour les paramètres audio // // Développé par CodeBits Interactive // ​​​​https://cdbits.net/ //===== ================= ======================= classe publique AudioSettingsModel ( public bool music = true; // Drapeau responsable de la musique public bool sounds = true ; // Drapeau responsable des sons)
Vous pouvez maintenant commencer à écrire une classe de manager. Nous l'installerons dès la première étape. Ce gestionnaire sera un objet global et ne sera pas supprimé lors du passage de scène en scène.

Utilisation de System.Collections ; en utilisant System.Collections.Generic ; en utilisant UnityEngine ; en utilisant System.IO ; //================================================= ============ // Gestionnaire audio // @usage fonctionne avec les paramètres audio // // Développé par CodeBits Interactive // ​​​​https://cdbits.net/ //==== ================= ======================== classe publique AudioManager : MonoBehaviour( // Public paramètres public static AudioManager instance = null; // Instance du gestionnaire public static AudioSettingsModel settings = null; // Modèle de paramètres audio private static string _settings_path = ""; // Chemin d'accès au fichier de paramètres audio // Initialisation du gestionnaire void Awake( ))( // Définir le chemin d'enregistrement des paramètres _settings_path = Application.persistentDataPath + "/audioSettings.gdf" ; // Vérifier si l'instance de notre manager est définie if (instance == null)( // L'instance n'est pas définie instance = this; // Définir l'objet courant sur l'instance ) // Définir le paramètre qui indique // que cet objet ne doit pas être supprimé lors du déchargement // le niveau DontDestroyOnLoad(gameObject); // Initialiser les paramètres de notre gestionnaire InitializeSettings (); ) // Initialiser le gestionnaire private void InitializeSettings())( // Si le modèle de paramètres n'est pas spécifié if (settings == null) settings = new AudioSettingsModel(); // Créer un nouveau modèle if (File.Exists(_settings_path) )( // S'il existe un fichier avec les paramètres loadSettings(); // Charger le fichier de paramètres audio ) ) // Charger les paramètres audio public void loadSettings())( string _data = File.ReadAllText(_settings_path); // Lire tout texte des paramètres du fichier = JsonUtility.FromJson (_données); // Désérialisez-le dans le modèle actuel) // Enregistrez les paramètres audio public void saveSettings())( string _json_data = JsonUtility.ToJson(settings); // Sérialisez les paramètres actuels du modèle File.WriteAllText(_settings_path, _json_data); // Enregistrer dans notre fichier ) // Créer des délégués pour notre événement, qui seront ensuite // utilisés pour suivre les modifications des paramètres audio public délégué void AudioSettingsChanged(); // Ajout d'un nouvel événement public délégué AudioSettingsChanged OnAudioSettingsChanged; // Créer un événement basé sur celui-ci // Activer/désactiver les sons public void toggleSounds(bool activé)( settings.sounds = activé; // Modifier les paramètres sonores dans le modèle actuel saveSettings(_settings_path, settings); // Enregistrer les paramètres si (OnAudioSettingsChanged ! = null) OnAudioSettingsChanged(); // Déclenche notre événement ) // Activer/désactiver la musique public void toggleMusic(bool activé)( settings.music = activé; // Modifier les paramètres de musique dans le modèle actuel saveSettings(_settings_path, settings); // Enregistrer les paramètres if (OnAudioSettingsChanged != null) OnAudioSettingsChanged(); // Appeler notre événement ) )
Maintenant que le gestionnaire est prêt, vous pouvez créer un objet vide dans votre scène initiale et le nommer par ex. "_AUDIO_MANAGER", puis ajoutez-y notre gestionnaire de classe. Cela peut être fait simplement en appelant le menu d'ajout de composant sur l'objet et en sélectionnant "Gestionnaires de jeux" => "Gestionnaire audio".

Après cela, nous devons écrire un composant que nous attacherons à chaque objet avec AudioSource.

Utilisation de System.Collections ; en utilisant System.Collections.Generic ; en utilisant UnityEngine ; //================================================= ============ // Audio Muter // @usage on/off des sources audio sur les objets // // Développé par CodeBits Interactive // ​​​​https://cdbits.net/ //= ================= =========================== classe publique AudioMuter : MonoBehaviour ( // Paramètres publics du composant public bool is_music = false; // Cet indicateur indique clairement à notre classe si l'AudioSource est du son ou de la musique. // Paramètres privés private AudioSource _as; // AudioSource private float _base_volume = 1F; // Volume de base AudioSource // Initialise l'objet void Start())( // Récupère le composant AudioSource et son volume initial _as = this.gameObject.GetComponent (); // Récupère le composant _base_volume = _as.volume; // Récupère le volume de base // Nous ajoutons ici un écouteur qui exécutera la méthode _audioSettingsChanged // lorsque les paramètres de musique/son auront été modifiés AudioManager.instance.OnAudioSettingsChanged += _audioSettingsChanged; // Installer // Eh bien, au début, nous devons vérifier l'état actuel des sons/musiques _audioSettingsChanged(); ) // Lorsqu'un objet est détruit void OnDestroy())( AudioManager.instance.OnAudioSettingsChanged -= _audioSettingsChanged; // Détruire l'écouteur ) // Cette méthode est utilisée pour activer/désactiver le volume de AudioSource private void _audioSettingsChanged() ( si (is_music) _as.volume = (AudioManager.settings.music) ? _base_volume : 0F ; si (!is_music) _as.volume = (AudioManager.settings.sounds) ? _base_volume : 0F ; ) )
De cette façon, nous pouvons contrôler les sons/musiques du jeu. Cet exemple n'indique en aucun cas comment le faire correctement, mais démontre uniquement le fonctionnement du système d'événements et des écouteurs dans Unity3D.

Et enfin, je voudrais parler de ce que nous utilisons maintenant. Dans l'exemple ci-dessous, un délégué a été déclaré à partir duquel un écouteur a été créé :

Délégué public void AudioSettingsChanged(); événement public AudioSettingsChanged OnAudioSettingsChanged ;
Vous pouvez configurer l'écouteur pour qu'il s'exécute sous certaines conditions et lui enchaîner des méthodes spécifiques qui seront exécutées lorsque ces conditions sont remplies.

Et avec l'aide des délégués, sur la base desquels nous avons créé l'écouteur, vous pouvez créer des fonctions de rappel. Cela peut être particulièrement utile pour les méthodes asynchrones (par exemple, lors de l'envoi de requêtes POST asynchrones).

J'espère que ma petite expérience en la matière vous sera utile et que vous pourrez appliquer cet exemple à vos projets. Je me ferai également un plaisir de répondre à vos questions (si quelqu'un ne comprend pas quelque chose).

Vous pouvez aider et transférer des fonds pour le développement du site

Les scripts dans Unity ne ressemblent pas à l'idée traditionnelle d'un programme, où le code s'exécute en continu en boucle jusqu'à ce qu'il termine sa tâche. Au lieu de cela, Unity transfère périodiquement le contrôle au script lorsque certaines fonctions qui y sont déclarées sont appelées. Une fois l’exécution de la fonction terminée, le contrôle revient à Unity. Ces fonctions sont appelées fonctions d'événement car ils sont activés par Unity en réponse aux événements qui se produisent pendant le jeu. Unity utilise un schéma de dénomination pour déterminer quelle fonction appeler pour un événement spécifique. Par exemple, vous avez déjà vu la fonction Update (appelée avant un changement d'image) et la fonction Start (appelée juste avant la première image d'un objet). Il existe de nombreuses autres fonctions d'événement disponibles dans Unity ; une liste complète avec des informations supplémentaires sur leur utilisation peut être trouvée sur la page de documentation de la classe MonoBehaviour. Voici quelques-uns des événements les plus importants et les plus fréquents.

Événements de mise à jour régulière

Un jeu est une sorte d’animation dans laquelle des images sont générées à la volée. Un concept clé dans la programmation de jeux consiste à modifier la position, l'état et le comportement des objets dans le jeu juste avant qu'une image ne soit dessinée. Ce type de code dans Unity est généralement placé dans la fonction Mise à jour. La mise à jour est appelée avant que l'image ne soit dessinée et avant que les animations ne soient calculées.

Void Update() ( distance flottante = vitesse * Time.deltaTime * Input.GetAxis("Horizontal"); transform.Translate(Vector3.right * distance); )

Le moteur physique se met également à jour à intervalles de temps fixes, de la même manière que le rendu des images. Une fonction d'événement FixeUpdate distincte est appelée juste avant chaque mise à jour des données physiques. Parce que Étant donné que la physique et le cadre ne sont pas mis à jour au même rythme, vous obtiendrez des résultats plus précis du code physique si vous le placez dans une fonction FixedUpdate plutôt que dans une mise à jour.

Void FixedUpdate() ( Vector3 force = transform.forward * driveForce * Input.GetAxis("Vertical"); rigidbody.AddForce(force); )

Il est aussi parfois utile de pouvoir apporter des modifications supplémentaires à un moment où les fonctions Update et FixedUpdate ont fonctionné pour tous les objets de la scène et que toutes les animations ont été calculées. À titre d'exemple, la caméra doit rester verrouillée sur l'objet cible ; les ajustements de l’orientation de la caméra doivent être effectués une fois que l’objet cible a bougé. Un autre exemple est lorsque le code du script doit remplacer l'effet d'une animation (par exemple, faire tourner la tête d'un personnage vers un objet cible dans la scène). Dans ces types de situations, vous pouvez utiliser la fonction LateUpdate.

Void LateUpdate() ( Camera.main.transform.LookAt(target.transform); )

Événements d'initialisation

Il est souvent utile de pouvoir appeler le code d'initialisation avant toute mise à jour en cours de jeu. La fonction Start est appelée avant la mise à jour de la première image ou physique de l'objet. La fonction Awake est appelée pour chaque objet de la scène lorsque la scène est chargée. Veuillez noter que même si les fonctions Start et Awake sont appelées dans des ordres différents pour différents objets, tous les Awake seront exécutés avant l'appel du premier Start. Cela signifie que le code de la fonction Start peut utiliser tout ce qui a été fait dans la phase Awake.

Événements de l'interface graphique

Unity dispose d'un système permettant de restituer les commandes de l'interface graphique au-dessus de tout ce qui se passe dans la scène et de répondre aux clics sur ces éléments. Ce code est géré légèrement différemment d'une mise à jour de frame normale, il doit donc être placé dans une fonction OnGUI qui sera appelée périodiquement.

Void OnGUI() ( GUI.Label(labelRect, "Game Over"); )

Vous pouvez également définir des événements de souris qui se déclenchent sur un GameObject situé dans la scène. Cela peut être utilisé pour viser des armes ou afficher des informations sur les personnages sous le curseur de la souris. Il existe un ensemble de fonctions d'événement OnMouseXXX (par exemple OnMouseOver, OnMouseDown) qui permettent au script de répondre aux actions de la souris de l'utilisateur. Par exemple, si le bouton de la souris est enfoncé alors que le curseur de la souris est sur un certain objet, alors si la fonction OnMouseDown est présente dans le script de cet objet, elle sera appelée.

Événements de physique

Le moteur physique signalera les collisions avec un objet en appelant des fonctions d'événement dans le script de cet objet. Les fonctions