Conseils Unity et problèmes courants

Remarque : Cet article sera mis à jour et révisé pendant une longue période.

trouver des objets rapidement

Sélectionnez l'objet que vous devez rechercher dans la hiérarchie et F dans la vue Scène, vous pouvez rapidement trouver l'objet dont vous avez besoin
Veuillez ajouter une description de l'image


Alignez rapidement les objets

Après avoir sélectionné l'objet dans la vue Jeu, appuyez et maintenez V, et vous pouvez voir que le centre du mouvement a changé à la position de la souris. Essayez maintenant de déplacer les deux cubes ensemble de manière transparente.
Veuillez ajouter une description de l'image


BMFont utilise

Un gros plugin ouvert est utilisé .
Comment ça fonctionne : Après avoir exporté .fnt et .png à l'aide de BMFont, créez CustomFont et Material dans Unity. Ouvrez les plug-ins ci-dessus et remplacez les fichiers requis à tour de rôle.
Remarque : Si les informations sur la police sont perdues après le redémarrage de Unity , vous pouvez ajouter EditorUtility.SetDirty(targetFont); à la fin du script BMFontEditor.cs pour le résoudre.


Rigidbody et collisionneur

Le corps rigide confère au gameObject des propriétés physiques et le collisionneur confère au gameObject des propriétés de collision.


classe abstraite

Les classes abstraites ne peuvent pas être sérialisées. Les fonctions virtuelles peuvent être sérialisées sans être définies dans des classes abstraites.


ScreenToWorldPoint

Si la position ne change pas après avoir appelé la conversion Camera.ScreenToWorldPoint , vous devrez peut-être faire attention à la valeur z de la position de conversion vers le bas .
Si la caméra est en mode orthographique, la position transformée ne tient pas compte de la valeur z.
Mais la caméra est en mode perspective, alors la valeur z de la position convertie doit être modifiée comme suit :

point = cam.ScreenToWorldPoint(new Vector3(mousePos.x, mousePos.y, cam.nearClipPlane));

Animateur

Lors de la lecture d'une animation avec une condition de transition sur un fil, vous pouvez basculer le flux entre deux actions .
Lorsque vous utilisez animator.Play et animator.CrossFade pour changer d'animation, il n'y a pas de continuité entre les deux actions . Play consiste à couper l'action suivante durement, et CrossFade consiste à passer lentement à l'action suivante dans le temps spécifié par les paramètres.


Ajout d'objets et de modèles pour coopérer avec des actions

Il convient de noter que l'objet ajouté ne peut pas avoir un corps rigide, sinon des choses inattendues se produiront.
Par exemple, lorsqu'un briquet est ajouté à la main du modèle, lorsqu'il coopère avec l'action. Le corps rigide du briquet doit être temporairement caché.


La navigation

Paramètres de la colonne d'ancrage :
le rayon de l'agent définit la distance entre le centre de l'agent et un mur ou un rebord.
La hauteur de l'agent définit la profondeur à laquelle l'agent peut descendre.
Max Slope définit la pente de la pente raide sur laquelle l'agent peut marcher.
La hauteur de marche définit la hauteur d'un obstacle sur lequel l'agent peut marcher.
DropHeight : hauteur de chute
JumpDistance : hauteur de saut

Précautions:

  • Les obstacles NavMeshObstacle doivent cocher Carve pour être efficaces
  • Là où le proxy ne peut pas atteindre, il est inutile même si Off Mesh Link est défini.
  • Lorsque l'objet en mouvement définit le Collider, si la largeur du chemin qui doit passer est inférieure à la taille du Collider, l'objet en mouvement ne peut pas passer à ce moment . La solution consiste à modifier les limites du collisionneur ou à supprimer le collisionneur (si vous n'avez pas besoin d'utiliser la collision)

Saisir

Input.GetMouseButton : Appelé en continu lorsque la souris est enfoncée Input.GetMouseButtonDown : Appelé
une seule fois lorsque la souris est enfoncée Input.GetMouseButtonUp : Appelé une seule fois lorsque la souris est relâchée .

Les paramètres sont tous les mêmes : 0 pour le bouton gauche, 1 pour le bouton droit et 2 pour le bouton central.


Conditions de transition des animateurs

Il existe plusieurs types de conditions de transition : Bool, Int, Float et Tririger. Ici, nous ne parlons que de Bool et de Triger.
Si nous utilisons Bool comme condition de transition , après l'avoir défini sur true, s'il n'est pas défini sur false la prochaine fois lors du passage à d'autres actions, cette condition sera toujours vraie.
Avec Triger , ce problème n'existe pas. Tout ce que nous devons faire est ResetTrigger("Last Trigger") avant SetTrriger. Sinon, le personnage se contractera lors de la commutation.


Mettre le programme en pause pendant son exécution

Ajoutez simplement le code suivant là où vous devez vous arrêter. L'éditeur entrera dans un état de pause :

  Debug.Break();

Aspect de préservation de l'image

Cochez la case Conserver l'aspect. Cela rend l'image aussi grande que possible dans la transformation Rect sans être compressée ou étirée.

Décoché :
Pas activé
Après vérification :
insérez la description de l'image ici
Image d'origine :
insérez la description de l'image ici


affichage du modèle

Comme le montre la figure ci-dessous, le modèle affiché dans le panneau de hiérarchie est le deuxième dans la figure ci-dessous, et le modèle est en lecture seule. Lorsque nous essayons de supprimer le contenu, une fenêtre contextuelle indique qu'il ne peut pas être supprimé.
Pour éditer, un préfabriqué doit être créé. C'est-à-dire le premier illustré dans la figure ci-dessous, à quel point la suppression deviendra effective.
insérez la description de l'image ici


SkinnedMeshRenderer

Un modèle se compose d'un maillage contenant des triangles qui est "rendu" par un Mesh Renderer, qui rend le maillage visible. Un Skinned Mesh Renderer est un type spécial de Mesh Renderer qui permet au mesh de changer de forme en fonction de la position et de la rotation de tous les os du modèle .
La façon dont les animations sont formées est la suivante : le composant Animator sur l'objet de jeu parent du personnage modifiera la rotation de tous les composants de transformation des objets de jeu squelettiques, et ces changements se produiront ensemble pour animer le personnage.


Le rôle de Has Exit Time

HasExitTime a deux fonctions :
la première est qu'après vérification, il n'est pas nécessaire d'ajouter des conditions pour convertir. Une fois l'animation jouée, elle passera automatiquement à l'action suivante.
La seconde est d'utiliser la condition pour basculer, si l'option animation n'est pas cochée, elle basculera immédiatement sur une autre. Mais si elle est cochée, elle basculera après la lecture de l'animation, c'est-à-dire que l'utilisation des conditions pour basculer ne prendra pas effet immédiatement.


convention de nommage

Variable membre publique/variable locale : myParam
Variable membre non publique : m_MyParam


Groupe de canevas

Les groupes de canevas fournissent un contrôle centralisé sur certains aspects d'un groupe entier d'éléments d'interface utilisateur sans avoir à gérer chaque élément individuellement. Les propriétés d'un groupe de canevas affectent l'objet de jeu sur lequel il se trouve ainsi que tous les objets enfants.
Les utilisations typiques des groupes de canevas sont :

  1. Fondu vers l'intérieur ou vers l'extérieur de la fenêtre entière en ajoutant un groupe de canevas sur l'objet de jeu de la fenêtre et en contrôlant sa propriété alpha.
  2. Rendez l'ensemble du groupe de contrôles non interactif ("grisé") en ajoutant le groupe de canevas à l'objet de jeu parent et en définissant sa propriété Interactable sur false.
  3. Faites en sorte qu'un ou plusieurs éléments de l'interface utilisateur ne bloquent pas les événements de la souris en plaçant un composant Canvas Group sur l'élément de l'interface utilisateur ou sur l'un de ses éléments parents et en définissant sa propriété Block Raycasts sur false.

Mathf.Environ

Compare deux valeurs à virgule flottante et renvoie true si elles sont similaires.
Par exemple, (1.0 == 10.0 / 10.0) ne renverra pas vrai à chaque fois. Environ() Compare deux nombres à virgule flottante et renvoie vrai s'ils se trouvent dans la plus petite plage l'un de l'autre.


Input.GetAxis

Unity dispose d'un gestionnaire d'entrées (Edit-ProjectSetting-InputManager) pour définir divers boutons et axes qui peuvent être trouvés par leur nom. Par exemple, il existe un axe appelé Horizontal, représenté par les touches A et D, et les touches gauche et droite. Ainsi, avec cette vérification, l'ordinateur du joueur peut décider si le personnage doit se déplacer vers la gauche ou vers la droite.
Input.GetAxis ne peut surveiller que les entrées du clavier et de la manette de jeu, ce qui n'a aucun sens pour les plates-formes mobiles.

   float horizontal = Input.GetAxis("Horizontal");
   float vertival = Input.GetAxis("Vertical");

État d'animation ajouter StateMachineBehaviour

Dans l'interface Animator, sélectionnez l'animation qui doit ajouter un script, puis sélectionnez AddBehaviour dans l'inspecteur pour ajouter le script StateMachineBehaviour.
Le corps rigide confère au gameObject des propriétés physiques et le collisionneur confère au gameObject des propriétés de collision.  Si le gameObject n'a qu'un corps rigide et aucun collisionneur, alors le gameObject continuera de tomber.


Problème de téléchargement du plugin Spine

Si le fichier téléchargé sur le site officiel de Spine n'est pas .unitypackage , mais un fichier se terminant par **.gz**. Essayez de changer de navigateur pour télécharger. Je peux télécharger le .unitypackage après avoir changé de navigateur Google.
insérez la description de l'image ici

Problème d'importation des ressources de la colonne vertébrale

Remplacez le suffixe d'atlas par .txt et faites-le glisser dans Unity pour générer automatiquement d'autres fichiers associés.


L'animation de la colonne vertébrale n'est pas terminée, rejouez le problème

Les deux lignes de code doivent être ajoutées

   spine.Skeleton.SetToSetupPose();
   spine.AnimationState.ClearTracks();

Problème de répétition de l'animation du modèle

Sélectionnez l'animation dans le modèle, sélectionnez Animation dans l'inspecteur et cochez LoopTime ci-dessous. Enfin, vous devez cliquer sur Appliquer pour prendre effet.
insérez la description de l'image ici
insérez la description de l'image ici


Utilisation de Sprite Atlas

Définir le mode Sprite Packer

Edit---->ProjectSetting------->Editor--------->Mode
insérez la description de l'image ici
Signification du paramètre Mode :
(1) désactivé n'active pas
(2) activé pour les builds (legacy sprite packer) : packaging (pour la méthode de packaging de sprite packer
) (3) toujours activé (legacy sprite packer) : toujours activé (pour la méthode de packaging de sprite packer
) (4) activé pour les builds : activé pour le packaging (pour la méthode sprite Atlas
(5) toujours activé (pour la méthode de packaging du sprite Atlas )

Sprite Atlas est la méthode d'emballage d'atlas après la version 2017. Sprite Atlas a considérablement amélioré les performances et la convivialité de l' ancienne version du système d'emballage d'atlas Sprite Packer .
Ainsi, l'auteur utilise uniquement spriteAtlas pour démontrer ci-dessous.

Créer un atlas de sprites

Créez le sprite Atlas comme suit :
insérez la description de l'image ici

Rejoindre spriteAtlas

spriteAtlas prend en charge l'ajout via des dossiers, il est donc recommandé d'ajouter via des dossiers, afin que vous n'ayez pas à les ajouter un par un. Après l'ajout, vous pouvez cliquer sur PackPreview pour voir l'effet.
insérez la description de l'image ici

Voir l'effet

Ici, nous pouvons revenir à la configuration du mode Sprite Packer ci-dessus, modifier SpritePacker dans les deux sens pour activer les versions ou toujours activé , puis cliquer sur Stats pour voir l'effet

insérez la description de l'image ici


Optimisation de l'animation du modèle

Méthode de compression du fichier : sélectionnez le fichier .fbx contenant l'action, puis sélectionnez Animation, et modifiez Anim.Compression.
insérez la description de l'image ici
Description de l'option :
Désactiver Désactiver la compression
Réduction des images clés Réduire les images clés inutiles
Compression d'optimisation optimale, l'officiel choisira la méthode de compression optimale pour la compression, il est recommandé de choisir cette


Réduire l'impact de la récupération de place en mémoire (GC) sur les performances

Chaîne

  1. En C#, les chaînes sont des types référence , et non des types valeur. Nous devons réduire les opérations inutiles de création ou de modification de chaînes . La méthode de modification d'une chaîne renvoie en fait un nouvel objet String , et la chaîne d'origine reste en mémoire en attendant d'être recyclée , donc lorsque la chaîne est longue ou que l'opération est fréquente, beaucoup de ressources seront consommées . Si vous devez créer des chaînes lors de l'exécution, utilisez la classe StringBuilder .
  2. Essayez d' éviter d'analyser des fichiers de données composés de chaînes telles que JSON et XML, de stocker des données dans ScriptableObjects ou de les enregistrer dans des formats tels que MessagePack ou Protobuf .

Appels de fonction d'unité

  1. Cacher les références de tableau pour éviter l'allocation de mémoire des tableaux pendant les boucles.
  2. Essayez d'utiliser des fonctions qui ne génèrent pas de ramasse-miettes. Par exemple, utilisez GameObject.CompareTag au lieu de comparer manuellement les chaînes à l'aide de GameObject.tag ( car le retour d'une nouvelle chaîne générera des données parasites ).

Boxe

Évitez de transmettre des variables de type valeur aux variables de type référence, car cela entraînerait la création par le système d'un objet temporaire et la conversion du type valeur en un type d'objet dans les coulisses (comme int i = 123; object o = i ), résultant dans la demande de collecte des ordures. Essayez d'utiliser le remplacement de type correct pour transmettre le type de valeur souhaité . Les génériques peuvent également être utilisés pour le remplacement de type.

Coroutines

Alors que yield n'entraîne pas de récupération de place, la création d'un objet WaitForSeconds le fait . Nous pouvons mettre en cache et réutiliser l'objet WaitForSeconds sans avoir à le recréer en rendement.

    WaitForSeconds waitSec = new WaitForSeconds(0.01f);
    IEnumerator TestWaitSecond()
    {
    
    
        yield return waitSec;
    }

Réduire la quantité de code par image

Il y a beaucoup de code qui n'est pas destiné à s'exécuter sur chaque image, et cette logique inutile peut être complètement supprimée dans Update, LateUpdate et FixedUpdate. Ces fonctions d'événement peuvent enregistrer le code qui doit être mis à jour à chaque trame, et toute logique qui n'a pas besoin d'être mise à jour à chaque trame n'a pas besoin d'y être placée.
Si vous devez utiliser Update, pensez à exécuter votre code toutes les n images.

private int interval = 3;
void Update()
{
    
    
    if (Time.frameCount % interval == 0)
    {
    
    
        ExampleExpensiveFunction();
    }
}

Évitez d'ajouter une logique lourde dans Start/Awake

Lorsque la première scène est chargée, chaque objet appellera les fonctions suivantes : Awake, OnEnable, Start. Nous devons éviter d'exécuter une logique lourde dans ces fonctions
jusqu'à ce que l'application ait fini de rendre la première image . Sinon, le temps de chargement de votre application sera étonnamment long .


Évitez de rejoindre des événements vides

Même les MonoBehaviours vides consomment des ressources, nous devons donc supprimer les méthodes Update et LateUpdate vides.
Si vous souhaitez tester avec ces méthodes, utilisez une directive de prétraitement :

#if UNITY_EDITOR
void Update()
{
    
    
}
#endif

De cette façon, les tests de mise à jour dans l'éditeur n'ont pas d'impact négatif sur les performances des builds.


Supprimer l'instruction Debug Log

Les instructions de journal (en particulier dans Update, LateUpdate et FixedUpdate) ralentissent les performances, nous devons donc désactiver les instructions de journal avant de construire . Vous pouvez facilement désactiver le journal de débogage en écrivant un attribut conditionnel avec une directive de prétraitement. Par exemple, une classe personnalisée comme celle-ci :

public static class Logging
{
    
    
    [System.Diagnostics.Conditional("ENABLE_LOG")]
    static public void Log(object message)
    {
    
    
        UnityEngine.Debug.Log(message);
    }
} 

Utilisez des hachages, évitez les chaînes

Le code Unity sous-jacent n'utilise pas de chaînes pour accéder aux propriétés Animator, Material et Shader. Pour plus d'efficacité, tous les noms de propriété sont hachés dans des ID de propriété , qui sont utilisés comme noms de propriété réels. Lorsque vous utilisez les méthodes Set ou Get
sur Animator, Material ou Shader , nous pouvons utiliser des valeurs entières au lieu de chaînes . Ce dernier doit également être haché une fois et n'est pas aussi simple que la valeur entière. Utilisez Animator.StringToHash pour convertir les noms de propriété Animator et Shader.PropertyToID pour convertir les noms de propriété Material et Shader .

    public static int IS_WALKING;
    private void Awake()
    {
    
    
        IS_WALKING = Animator.StringToHash("IsWalking");
    }
    void FixedUpdate()
    {
    
    
       //使用ID修改条件
       m_Animator.SetBool(IS_WALKING, isWaking);
    }

Choisir la bonne structure de données

Étant donné que la structure de données peut être itérée des milliers de fois par image, sa structure a un impact important sur les performances. Si vous ne savez pas si la collecte de données doit être représentée par une liste, un tableau ou un dictionnaire, vous pouvez vous référer au guide de structure de données MSDN de C# pour choisir la structure appropriée.

insérez la description de l'image ici


Évitez d'ajouter des composants au moment de l'exécution

Appeler AddComponent au moment de l'exécution prendra un certain coût de fonctionnement, et Unity doit vérifier si le composant a des duplications ou des dépendances .


Mise en cache des GameObjects et des composants

L'appel de GameObject.Find, GameObject.GetComponent et Camera.main (versions inférieures à 2020.2) générera une charge d'exécution importante. Ces méthodes ne conviennent donc pas à l'appel dans Update, mais doivent être appelées et mises en cache dans Start .

private Renderer myRenderer;
void Start()
{
    
    
    myRenderer = GetComponent<Renderer>();
}

void Update()
{
    
    
    ExampleFunction(myRenderer);
} 

Utilisation de ScriptableObjects (objets programmables)

Les valeurs constantes ou les informations de configuration peuvent être stockées dans un ScriptableObject, pas nécessairement dans un MonoBehaviour. ScriptableObject est accessible par l'ensemble du projet, une fois défini, il peut être appliqué globalement au projet.

déclaration:

using UnityEngine;

[CreateAssetMenu(fileName = "Data", menuName = "ScriptableObjects/SpawnManagerScriptableObject", order = 1)]
public class SpawnManagerScriptableObject : ScriptableObject
{
    
    
    public string prefabName;

    public int numberOfPrefabsToCreate;
    public Vector3[] spawnPoints;
}

utiliser:

using UnityEngine;

public class Spawner : MonoBehaviour
{
    
    
    // 要实例化的游戏对象。
    public GameObject entityToSpawn;

    //上面定义的 ScriptableObject 的一个实例。
    public SpawnManagerScriptableObject spawnManagerValues;

    //这将附加到创建的实体的名称,并在创建每个实体时递增。
    int instanceNumber = 1;

    void Start()
    {
    
    
        SpawnEntities();
    }

    void SpawnEntities()
    {
    
    
        int currentSpawnPointIndex = 0;

        for (int i = 0; i < spawnManagerValues.numberOfPrefabsToCreate; i++)
        {
    
    
            //在当前生成点处创建预制件的实例。
            GameObject currentEntity = Instantiate(entityToSpawn, spawnManagerValues.spawnPoints[currentSpawnPointIndex], Quaternion.identity);

            //将实例化实体的名称设置为 ScriptableObject 中定义的字符串,然后为其附加一个唯一编号。
            currentEntity.name = spawnManagerValues.prefabName + instanceNumber;

            // 移动到下一个生成点索引。如果超出范围,则回到起始点。
            currentSpawnPointIndex = (currentSpawnPointIndex + 1) % spawnManagerValues.spawnPoints.Length;

            instanceNumber++;
        }
    }
}

Emballage du package Android 64 bits

Paramètres de construction -> Paramètres du lecteur -> Lecteur -> Autres paramètres,
modifiez le backend de script en IL2CPP et vérifiez ARM64 pour les architectures cibles.
(comme le montre l'image)

insérez la description de l'image ici
Après avoir généré l'apk, ouvrez le dossier lib avec le logiciel de décompression, si c'est arm64-v8a, c'est 64 bits :
insérez la description de l'image ici

Je suppose que tu aimes

Origine blog.csdn.net/qq_28644183/article/details/125487290
conseillé
Classement