Astuces et techniques variées pour Unity

Gestion de l'ordre des éléments dans UGUI

Pour modifier l'ordre d'affichage des éléments enfant dans l'interface utilisateur Unity (UGUI), on peut ajuster leur position hiérarchique. Le code ci-dessous montre comment déplacer un élément vers le haut ou le bas dans la liste des frères et sœurs.

int nombreEnfants = transform.childCount;
elementCible.transform.SetSiblingIndex(nombreEnfants - 1); // Déplacer en haut de la liste
transform.SetAsLastSibling(); // Placer en haut
transform.SetAsFirstSibling(); // Placer en bas

Actualisation des données en mode éditeur

Pour mettre à jour des valeurs en temps réel dans l'éditeur Unity sans exécuter le jeu, utilisez la méthode OnValidate. Cela permet de modifier des propriétés comme l'échelle d'un objet immédiatement.

[SerializeField]
float facteurEchelle = 0f;
[SerializeField]
Image imageCible;

private void OnValidate()
{
    imageCible.transform.localScale = new Vector3(facteurEchelle, facteurEchelle, facteurEchelle);
}

Trier un dictionnaire par valeurs

Les dictionnaires en C# peuvent être triés en fonction de leurs valeurs à l'aide de LINQ. L'exemple suivant illustre un tri ascendant et la récupération du premier élément.

void TrierDictionnaire(ref Dictionary<int, int> dictionnaire)
{
    Dictionary<int, int> dictionnaireTrie = dictionnaire.OrderBy(paire => paire.Value)
        .ToDictionary(paire => paire.Key, paire => paire.Value);
    dictionnaire = dictionnaireTrie;
}

// Obtenir le premier élément d'un dictionnaire
var premierElement = dictionnaire.FirstOrDefault();
if (premierElement.Key != null)
{
    Debug.Log("Clé: " + premierElement.Key + ", Valeur: " + premierElement.Value);
}

Parcours bidirectionnel d'un tableau

Pour parcourir un tableau depuis les extrémités vers le centre, un algorithme à deux pointeurs peut être utilisé. Cela permet d'itérer sur les éléments de manière symétrique.

int[] tableau = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int debut = 0;
int fin = tableau.Length - 1;
bool continuer = true;

while (continuer)
{
    Debug.Log("Élément début: " + tableau[debut]);
    debut++;
    Debug.Log("Élément fin: " + tableau[fin]);
    fin--;

    if (debut >= fin)
    {
        continuer = false;
    }
}

Trouver l'élément le plus proche d'une valeur donnée dans un tableau

En utilisant LINQ, il est possible de chercher l'élément d'un tableau le plus proche d'une valeur cible, en triant par différence absolue.

float[] valeurs = { -0.9f, 0.6f, 0.4f, -0.3f, -0.8f, 1.6f };
float valeurCible = -1f;
float elementProche = valeurs.OrderBy(v => Math.Abs(v - valeurCible)).ThenBy(v => v).First();
Debug.Log("Élément le plus proche: " + elementProche);

Définir la résolution de l'écran

La résolution de l'application peut être forcée à des dimensions spécifiques pour un plein écran exclusif.

Screen.SetResolution(1920, 1080, FullScreenMode.ExclusiveFullScreen);

Gestion des événements de clic et de pression

Pour détecter des interactions comme les clics simples, doubles ou les pressions longues, on peut utiliser des déclencheurs d'événements dans Unity. Le code suivant gère ces scénarios en enregistrant les temps et les comptages.

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;

public class GestionnaireClics : MonoBehaviour
{
    [SerializeField] GameObject cibleClic;
    float dureeMaxClic = 0.2f;
    int compteurClics = 0;
    bool estEnfonce = false;
    float tempsDebutEnfoncement = 0f;

    void Start()
    {
        AjouterEcouteur(cibleClic, EventTriggerType.PointerDown, (BaseEventData data) =>
        {
            estEnfonce = true;
            tempsDebutEnfoncement = Time.realtimeSinceStartup;
        });
        AjouterEcouteur(cibleClic, EventTriggerType.PointerUp, (BaseEventData data) =>
        {
            estEnfonce = false;
        });
        AjouterEcouteur(cibleClic, EventTriggerType.PointerClick, (BaseEventData data) =>
        {
            compteurClics++;
        });
    }

    void Update()
    {
        if (compteurClics > 0)
        {
            dureeMaxClic -= Time.deltaTime;
            if (dureeMaxClic <= 0f)
            {
                if (compteurClics >= 2) Debug.Log("Double clic");
                else if (compteurClics == 1 && Time.realtimeSinceStartup - tempsDebutEnfoncement <= 1f)
                    Debug.Log("Clic simple");
                compteurClics = 0;
                dureeMaxClic = 0.2f;
            }
        }
        if (estEnfonce && Time.realtimeSinceStartup - tempsDebutEnfoncement > 2f)
        {
            Debug.Log("Appui long");
            compteurClics = 0;
            estEnfonce = false;
        }
    }

    void AjouterEcouteur(GameObject obj, EventTriggerType type, UnityAction<BaseEventData> action)
    {
        EventTrigger declencheur = obj.GetComponent<EventTrigger>();
        if (declencheur == null) declencheur = obj.AddComponent<EventTrigger>();
        if (declencheur.triggers == null) declencheur.triggers = new System.Collections.Generic.List<EventTrigger.Entry>();
        EventTrigger.Entry entree = new EventTrigger.Entry();
        entree.eventID = type;
        entree.callback.AddListener(action);
        declencheur.triggers.Add(entree);
    }
}

Activation automatique d'un InputField

Pour rendre un champ de saisie actif immédiatement, appelez la méthode ActivateInputField.

champSaisie.ActivateInputField();

Interpolation linéaire pour le mouvement

L'interpolation peut être utilisée pour déplacer un objet de manière fluide entre deux positions. L'exemple ci-dessous utilise Mathf.Lerp pour un déplacement horizontal.

float vitesse = 0.2f;
float tempsDepart = 1f;
float positionDebutX = 50f;
float positionFinX = 1000f;

void FixedUpdate()
{
    float progression = Mathf.Lerp(positionDebutX, positionFinX, (Time.time - tempsDepart) * vitesse);
    transform.position = new Vector3(progression, 0, 0);
}

Attributs et annotations dans Unity

Unity offre plusieurs attributs pour personnaliser l'affichage dans l'inspecteur ou ajouter des contraintes aux composants.

  • [Tooltip("Description")] : Ajoute une info-bulle dans l'inspecteur.
  • [Header("Section")] : Crée une section dans l'inspecteur.
  • [Multiline(5)] : Définit un champ multiligne avec 5 lignes.
  • [RequireComponent(typeof(TypeComposant))] : Force la présence d'un composant spécifique.

Formatage de nombres et de dates

La classe string.Format permet de formater des valeurs numériques et temporelles selon des modèles spécifiques.

string textePourcentage = string.Format("{0:p2}", 0.8961); // Résultat: 89.61%
string texteDate = string.Format("{0:D4}-{1:D2}-{2:D2} {3:D2}:{4:D2}:{5:D2}", 2020, 6, 18, 12, 13, 56); // Résultat: 2020-06-18 12:13:56
string texteNombre = string.Format("{0:N0}", 56789); // Résultat: 56,789

Résolution des problèmes d'encodage des caractères

Lors de la publication d'applications avec des caractères spéciaux, il peut être nécessaire d'inclure des bibliothèques DLL spécifiques dans le répertoire Plugins du projet Unity pour assurer un encodage correct.

Utilisation de l'ImageEditor

Pour accéder aux fonctionnalités d'édition d'images dans l'éditeur Unity, copiez le fichier UnityEditor.UI.dll depuis le répertoire d'installation de Unity vers le dossier Assets du projet.

Génération de nombres aléatoires uniques

Pour obtenir une série de nombres aléatoires sans répétition dans un intervalle donné, un algorithme de permutation peut être employé.

int[] ObtenirNombresAleatoires(int plafond, int quantite)
{
    int[] sequence = new int[plafond];
    for (int i = 0; i < plafond; i++) sequence[i] = i;
    int[] resultat = new int[quantite];
    int limite = plafond - 1;

    for (int i = 0; i < quantite; i++)
    {
        int index = Random.Range(0, limite + 1);
        resultat[i] = sequence[index];
        sequence[index] = sequence[limite];
        limite--;
    }
    return resultat;
}

Fermeture de processus externes

Pour terminer des applications tierces comme un navigateur, on peut identifier et arrêter les processus correspondants via le nom du processus.

void TerminerProcessus(string nomProcessus)
{
    foreach (Process processus in Process.GetProcesses())
    {
        try
        {
            if (!processus.HasExited && processus.ProcessName == nomProcessus)
                processus.Kill();
        }
        catch (System.InvalidOperationException ex)
        {
            Debug.LogError(ex);
        }
    }
}

Navigation directe dans un Scroll View

Pour faire défiler instantanément un Scroll View à un point spécifique, modifiez les propriétés horizontalNormalizedPosition ou verticalNormalizedPosition.

scrollRect.horizontalNormalizedPosition = 0f; // Aller au début
scrollRect.horizontalNormalizedPosition = 1f; // Aller à la fin

Modification de la transparence d'un SpriteRenderer

L'alpha d'un sprite peut être ajusté en modifiant la couleur via la propriété color, où le quatrième paramètre contrôle la transparence (valeur entre 0 et 1).

spriteRenderer.color = new Color(1f, 1f, 1f, 0.5f); // 50% de transparence

Affichage du nom d'une variable

Pour obtenir le nom d'une variable sous forme de chaîne, on peut utiliser des expressions lambda et la réflexion.

using System;
using System.Linq.Expressions;

public static class NomsVariables
{
    public static string ObtenirNom<T>(Expression<Func<T>> expression)
    {
        MemberExpression corps = (MemberExpression)expression.Body;
        return corps.Member.Name;
    }
}

// Utilisation
string maVariable = "test";
string nom = NomsVariables.ObtenirNom(() => maVariable);

Accès à des propriétés via réflexion

La réflexion permet de lire ou modifier des propriétés d'objets à partir de noms sous forme de chaînes.

public class Donnees
{
    public int identifiant = 100;
}

void Exemple()
{
    Donnees donnees = new Donnees();
    string nomPropriete = "identifiant";
    int valeur = (int)donnees.GetType().GetField(nomPropriete).GetValue(donnees);
    Debug.Log("Valeur: " + valeur);
}

Calcul d'angles entre objets

Pour calculer l'angle entre deux points et orienter un objet vers une cible, on peut utiliser des opérations vectorielles.

float ObtenirAngle(Vector3 pointA, Vector3 pointB)
{
    Vector3 direction = pointB - pointA;
    float angle = Mathf.Atan2(direction.x, direction.y) * Mathf.Rad2Deg;
    return angle;
}

// Pour orienter un objet vers une cible
Vector3 directionCible = cible.position - transform.position;
transform.rotation = Quaternion.LookRotation(directionCible);

Mise à l'échelle proportionnelle d'images

Pour redimensionner une image tout en conservant son rapport d'aspect, on peut calculer un facteur d'échelle basé sur les dimensions du conteneur.

Vector2 AjusterTailleImage(float largeurImage, float hauteurImage, float largeurConteneur, float hauteurConteneur)
{
    float ratioConteneur = largeurConteneur / hauteurConteneur;
    float ratioImage = largeurImage / hauteurImage;
    float facteur = ratioConteneur < ratioImage ? largeurConteneur / largeurImage : hauteurConteneur / hauteurImage;
    return new Vector2(largeurImage * facteur, hauteurImage * facteur);
}

Contrôle de particules

Les systèmes de particules dans Unity peuvent être configurés pour déclencher des actions lors de leur arrêt, comme la destruction de l'objet.

using UnityEngine;

public class ControleurParticules : MonoBehaviour
{
    public delegate void CallbackArret();
    public event CallbackArret SurArretParticules;

    void Start()
    {
        ParticleSystem particules = GetComponent<ParticleSystem>();
        var modulePrincipal = particules.main;
        modulePrincipal.loop = false;
        modulePrincipal.stopAction = ParticleSystemStopAction.Callback;
        particules.Play();
    }

    void OnParticleSystemStopped()
    {
        SurArretParticules?.Invoke();
        Destroy(gameObject);
    }
}

Intégration de System.Drawing.dll

Pour utiliser des fonctionnalités de dessin avancées, importez le fichier System.Drawing.dll depuis l'installation de Unity dans le dossier Plugins et configurez le niveau de compatibilité API sur .NET 4.X dans les paramètres du projet.

Détection par rayons (version 3D)

La détection par rayons permet d'identifier des objets dans la scène à partir d'une position et d'une direction, utile pour les interactions tactiles ou le ciblage.

void LancerRayon(Vector2 positionEcran)
{
    Ray rayon = cameraPrincipale.ScreenPointToRay(positionEcran);
    RaycastHit[] impacts = Physics.RaycastAll(rayon);
    foreach (RaycastHit impact in impacts)
    {
        if (impact.collider.gameObject.CompareTag("Cible"))
        {
            Debug.Log("Objet touché: " + impact.collider.gameObject.name);
        }
    }
}

Orientation vers la direction du mouvement

Pour aligner un objet avec sa direction de déplacement, notamment lors de l'utilisation de la physique, on peut interpoler la rotation.

void FixedUpdate()
{
    if (corpsRigide.velocity != Vector3.zero)
    {
        Quaternion rotationSouhaitee = Quaternion.LookRotation(corpsRigide.velocity);
        transform.rotation = Quaternion.Slerp(transform.rotation, rotationSouhaitee, Time.deltaTime);
    }
}

Prévention de la pénétration physique

Pour éviter les problèmes d'objets traversant les surfaces, ajustez la vitesse de dépenetration maximale dans les paramètres physiques.

Physics.defaultMaxDepenetrationVelocity = float.PositiveInfinity;

Numérotation dans des boucles imbriquées

Lors du parcours d'une grille à deux dimensions, un index unique peut être calculé pour chaque cellule.

for (int x = 0; x < largeurGrille; x++)
{
    for (int y = 0; y < hauteurGrille; y++)
    {
        int index = x * hauteurGrille + y;
        Debug.Log("Index: " + index);
    }
}

Détection par rayons 2D

En environnement 2D, des rayons peuvent être utilisés pour détecter des collisions dans un plan spécifique.

void RayonDetection2D(Vector2 origine, Vector2 direction)
{
    Ray2D rayon = new Ray2D(origine, direction);
    RaycastHit2D impact = Physics2D.Raycast(origine, direction);
    if (impact.collider != null && impact.collider.CompareTag("Sol"))
    {
        Debug.Log("Impact avec le sol");
    }
}

Effet de recul

Appliquer une force de recul à un objet lors d'une collision peut créer un effet de repoussement basé sur la direction entre les objets.

void OnCollisionEnter2D(Collision2D collision)
{
    if (collision.gameObject.CompareTag("Joueur"))
    {
        Vector3 directionRecul = (transform.position - collision.transform.position).normalized;
        float force = 1000f / Vector3.Distance(transform.position, collision.transform.position);
        corpsRigide.AddForce(directionRecul * force);
    }
}

Vérification de la visibilité à l'écran

Pour déterminer si un objet est visible par la caméra, on peut calculer sa position en coordonnées de vue et vérifier son alignement avec la caméra.

bool EstVisible(Vector3 positionMonde)
{
    Vector3 positionVue = Camera.main.WorldToViewportPoint(positionMonde);
    Vector3 directionVersObjet = (positionMonde - Camera.main.transform.position).normalized;
    float alignement = Vector3.Dot(Camera.main.transform.forward, directionVersObjet);
    return alignement > 0 && positionVue.x >= 0 && positionVue.x <= 1 && positionVue.y >= 0 && positionVue.y <= 1;
}

Nettoyage des textures de rendu

Les textures de rendu doivent être libérées explicitement pour éviter les fuites de mémoire.

textureRendu.Release();

Ajout d'une caméra à la pile de rendu

Dans le pipeline de rendu universel (URP), des caméras supplémentaires peuvent être ajoutées à la pile de la caméra principale.

Camera.main.GetUniversalAdditionalCameraData().cameraStack.Add(camSecondaire);

Maintien de l'écran actif sur mobile

Pour empêcher l'écran de se verrouiller sur les appareils mobiles, modifiez le paramètre de temporisation du sommeil.

#if UNITY_ANDROID || UNITY_IOS
Screen.sleepTimeout = SleepTimeout.NeverSleep;
#endif

Fermeture de l'application

Pour quitter l'application programmable, utilisez les fonctions appropriées, en tenant compte de l'environnement d'exécution.

Application.Quit(); // Fonctionne après publication
Process.GetCurrentProcess().Kill(); // Fonctionne aussi dans l'éditeur

Étiquettes: Unity C# UGUI LINQ Physique

Publié le 10 juillet à 05h51