unity3d
Optimisation
Recherche…
Remarques
- Si possible, désactivez les scripts sur les objets lorsqu'ils ne sont pas nécessaires. Par exemple, si vous avez un script sur un objet ennemi que les utilisateurs recherchent et déclenchent sur le joueur, envisagez de désactiver ce script lorsque l'ennemi est trop éloigné du joueur, par exemple.
Vérifications rapides et efficaces
Évitez les opérations inutiles et les appels de méthodes partout où vous le pouvez, en particulier dans une méthode appelée plusieurs fois par seconde, comme la Update
à Update
.
Contrôle de distance / distance
Utilisez sqrMagnitude
au lieu de l' magnitude
lorsque vous comparez les distances. Cela évite inutiles sqrt
opérations. Notez que lorsque vous utilisez sqrMagnitude
, le côté droit doit également être mis au carré.
if ((target.position - transform.position).sqrMagnitude < minDistance * minDistance))
Chèques de Limites
Les intersections d'objets peuvent être grossièrement vérifiées en vérifiant si leurs limites Collider
/ Renderer
croisent. La structure Bounds
a également une méthode pratique Intersects
qui permet de déterminer si deux bornes se croisent.
Bounds
nous aident également à calculer une approximation proche de la distance réelle (surface à surface) entre les objets (voir Bounds.SqrDistance
).
Mises en garde
La vérification des limites fonctionne très bien pour les objets convexes, mais les vérifications des limites sur les objets concaves peuvent entraîner des inexactitudes beaucoup plus importantes selon la forme de l'objet.
L'utilisation de Mesh.bounds
n'est pas recommandée car elle renvoie des limites d'espace locales. Utilisez MeshRenderer.bounds
place.
Puissance de coroutine
Usage
Si vous exécutez une opération longue qui repose sur l’API Unity non compatible avec les threads , utilisez Coroutines pour la diviser sur plusieurs cadres et garder votre application réactive.
Les coroutines permettent également d'effectuer des actions coûteuses à chaque nième image au lieu d'exécuter cette action à chaque image.
Fractionnement de routines de longue durée sur plusieurs images
Coroutines permet de distribuer des opérations de longue durée sur plusieurs cadres afin de maintenir le framerate de votre application.
Les routines qui peignent ou génèrent du terrain de manière procédurale ou génèrent du bruit sont des exemples qui peuvent nécessiter le traitement Coroutine.
for (int y = 0; y < heightmap.Height; y++)
{
for (int x = 0; x < heightmap.Width; x++)
{
// Generate pixel at (x, y)
// Assign pixel at (x, y)
// Process only 32768 pixels each frame
if ((y * heightmap.Height + x) % 32 * 1024) == 0)
yield return null; // Wait for next frame
}
}
Le code ci-dessus est un exemple facile à comprendre. Dans le code de production , il est préférable d'éviter le contrôle par pixel qui vérifie quand
yield return
lefor
yield return
(peut - être faire tous les 2-3 lignes) et d' effectuer une pré-calculfor
la longueur de boucle à l' avance.
Effectuer des actions coûteuses moins souvent
Les Coroutines vous aident à effectuer des actions coûteuses moins fréquemment, de sorte qu’elles ne sont pas aussi importantes que si elles étaient exécutées à chaque image.
En prenant l'exemple suivant directement à partir du manuel :
private void ProximityCheck()
{
for (int i = 0; i < enemies.Length; i++)
{
if (Vector3.Distance(transform.position, enemies[i].transform.position) < dangerDistance)
return true;
}
return false;
}
private IEnumerator ProximityCheckCoroutine()
{
while(true)
{
ProximityCheck();
yield return new WaitForSeconds(.1f);
}
}
Les tests de proximité peuvent être optimisés en utilisant l’ API CullingGroup .
Pièges courants
Une erreur courante des développeurs est d’accéder aux résultats ou aux effets secondaires des coroutines en dehors de la coroutine. Coroutines retourne le contrôle à l'appelant dès qu'un relevé de yield return
est rencontré et que le résultat ou l'effet secondaire peut ne pas encore être exécuté. Pour contourner les problèmes où vous devez utiliser le résultat / effet secondaire en dehors de la coroutine, cochez cette réponse .
Cordes
On pourrait faire valoir qu’il ya plus de porcs dans l’Unité que l’humble cordelette, mais c’est l’un des aspects les plus faciles à résoudre dès le départ.
Les opérations de type string créent des ordures
La plupart des opérations sur les chaînes génèrent des quantités infimes de déchets, mais si ces opérations sont appelées plusieurs fois au cours d'une seule mise à jour, elles se cumulent. Au fil du temps, cela déclenchera le nettoyage de la mémoire automatique, ce qui peut entraîner une pointe de processeur visible.
Cachez vos opérations de chaîne
Prenons l'exemple suivant.
string[] StringKeys = new string[] {
"Key0",
"Key1",
"Key2"
};
void Update()
{
for (var i = 0; i < 3; i++)
{
// Cached, no garbage generated
Debug.Log(StringKeys[i]);
}
for (var i = 0; i < 3; i++)
{
// Not cached, garbage every cycle
Debug.Log("Key" + i);
}
// The most memory-efficient way is to not create a cache at all and use literals or constants.
// However, it is not necessarily the most readable or beautiful way.
Debug.Log("Key0");
Debug.Log("Key1");
Debug.Log("Key2");
}
Cela peut sembler idiot et redondant, mais si vous travaillez avec Shaders, vous pourriez rencontrer de telles situations. Cacher les clés fera la différence.
Veuillez noter que les littéraux de chaîne et les constantes ne génèrent aucune erreur, car ils sont injectés de manière statique dans l'espace de pile du programme. Si vous générez des chaînes au moment de l'exécution et que vous générez les mêmes chaînes à chaque fois, comme dans l'exemple ci-dessus, la mise en cache sera certainement utile.
Pour les autres cas où la chaîne générée n'est pas la même à chaque fois, il n'y a pas d'autre alternative à la génération de ces chaînes. En tant que tel, le pic de mémoire avec la génération manuelle de chaînes à chaque fois est généralement négligeable, à moins que des dizaines de milliers de chaînes ne soient générées à la fois.
La plupart des opérations de chaîne sont des messages de débogage
Faire des opérations de chaîne pour les messages de débogage, c.-à-d. Debug.Log("Object Name: " + obj.name)
est Debug.Log("Object Name: " + obj.name)
et ne peut être évité pendant le développement. Il est toutefois important de veiller à ce que les messages de débogage non pertinents ne se retrouvent pas dans le produit publié.
L'une des méthodes consiste à utiliser l' attribut Conditionnel dans vos appels de débogage. Cela non seulement supprime les appels de méthode, mais aussi toutes les opérations de chaîne qui y entrent.
using UnityEngine;
using System.Collections;
public class ConditionalDebugExample: MonoBehaviour
{
IEnumerator Start()
{
while(true)
{
// This message will pop up in Editor but not in builds
Log("Elapsed: " + Time.timeSinceLevelLoad);
yield return new WaitForSeconds(1f);
}
}
[System.Diagnostics.Conditional("UNITY_EDITOR")]
void Log(string Message)
{
Debug.Log(Message);
}
}
Ceci est un exemple simplifié. Vous voudrez peut-être investir du temps dans la conception d'une routine de journalisation à part entière.
Comparaison de chaîne
Ceci est une optimisation mineure, mais cela vaut la peine d'être mentionné. La comparaison des chaînes est légèrement plus complexe qu'on pourrait le penser. Le système essaiera de prendre en compte les différences culturelles par défaut. Vous pouvez choisir d’utiliser une simple comparaison binaire, plus rapide.
// Faster string comparison
if (strA.Equals(strB, System.StringComparison.Ordinal)) {...}
// Compared to
if (strA == strB) {...}
// Less overhead
if (!string.IsNullOrEmpty(strA)) {...}
// Compared to
if (strA == "") {...}
// Faster lookups
Dictionary<string, int> myDic = new Dictionary<string, int>(System.StringComparer.Ordinal);
// Compared to
Dictionary<string, int> myDictionary = new Dictionary<string, int>();
Références du cache
Cacher les références pour éviter les appels coûteux, en particulier dans la fonction de mise à jour. Cela peut être fait en mettant en cache ces références au démarrage si elles sont disponibles ou lorsqu'elles sont disponibles et en recherchant null / bool flat pour éviter d'obtenir à nouveau la référence.
Exemples:
Références du composant de cache
changement
void Update()
{
var renderer = GetComponent<Renderer>();
renderer.material.SetColor("_Color", Color.green);
}
à
private Renderer myRenderer;
void Start()
{
myRenderer = GetComponent<Renderer>();
}
void Update()
{
myRenderer.material.SetColor("_Color", Color.green);
}
Références d'objet cache
changement
void Update()
{
var enemy = GameObject.Find("enemy");
enemy.transform.LookAt(new Vector3(0,0,0));
}
à
private Transform enemy;
void Start()
{
this.enemy = GameObject.Find("enemy").transform;
}
void Update()
{
enemy.LookAt(new Vector3(0, 0, 0));
}
De plus, cachez les appels coûteux comme les appels à Mathf dans la mesure du possible.
Évitez d'appeler des méthodes à l'aide de chaînes
Évitez d'appeler des méthodes en utilisant des chaînes pouvant accepter des méthodes. Cette approche utilisera la réflexion qui peut ralentir votre jeu, en particulier lorsqu'il est utilisé dans la fonction de mise à jour.
Exemples:
//Avoid StartCoroutine with method name
this.StartCoroutine("SampleCoroutine");
//Instead use the method directly
this.StartCoroutine(this.SampleCoroutine());
//Avoid send message
var enemy = GameObject.Find("enemy");
enemy.SendMessage("Die");
//Instead make direct call
var enemy = GameObject.Find("enemy") as Enemy;
enemy.Die();
Éviter les méthodes d'unité vide
Évitez les méthodes d'unité vide. En plus d’être un mauvais style de programmation, les scripts d’exécution impliquent une surcharge minime. Dans de nombreux cas, cela peut augmenter et affecter les performances.
void Update
{
}
void FixedUpdate
{
}