Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
2
Question by unity_byWtCFyBQS9BWg · Nov 12, 2020 at 04:33 PM · enemyfindgameobjectswithtagclosestkill player

Game lagging while looking for closest Enemy

Code of handling input making look for closest enemy:

 if (Input.GetMouseButtonDown(0) && !hitDelay)
 {
     AttackAnim();

     if (closestEnemy())
     {
         if (Vector3.Distance(transform.position, closestEnemy().transform.position) <= 3)
         {
             if (closestEnemy().tag == "Enemy")
             {
                 Debug.Log("Hitting enemy");
                 enemy = closestEnemy().gameObject.GetComponent<EnemyScript>();
                 enemy.Hit();
                 if (enemy.life <= 0)
                     DestroyClosestEnemy();
             }
             else if (closestEnemy().tag != "Player" && closestEnemy().tag != "Untagged")
             {
                 Debug.Log("Destroying " + closestEnemy().tag);
                 DestroyClosestEnemy();
                 wood++;
                 Debug.Log("You have " + wood + " woods");
             }
             else
             {
                 ;
             }
         }
     }
     StartCoroutine(HitCoroutine());
 }

code of function

 private GameObject closestEnemy()
 {
     GameObject[] Objects = Object.FindObjectsOfType<GameObject>().Where(f => (f.tag == "Enemy" || f.tag == "Tree") && Vector3.Distance(f.transform.position, transform.position) <= 3).ToArray();
     
     GameObject closestEnemy = null;
     bool first = true;
     foreach(var obj in Objects)
     {
         float Distance = Vector3.Distance(transform.position, obj.transform.position);
         if (first)
         {
             closestEnemy = obj;
             first = false;
         }
         if (Distance < Vector3.Distance(transform.position, closestEnemy.transform.position))
         {
             closestEnemy = obj;
         }
     }
     return closestEnemy;
 }
Comment
Add comment · Show 1
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image andrew-lukasik · Nov 13, 2020 at 12:29 AM 0
Share

Calling your closestEnemy()method is super costly. Always cache those results in a field and reuse them

var closest = closestEnemy();

2 Replies

· Add your reply
  • Sort: 
avatar image
1

Answer by jackmw94 · Nov 12, 2020 at 08:11 PM

Oooh this is a good one. First of all welcome to optimisation!

The main process of optimisation goes like this:
*Profile
*Find choke points
*Fix those
*Repeat

You’ll find the Unity profiler in Windows->Analysis. Run this while the game is playing and then pause the editor during or after the area you want to improve. You should be seeing a load of unsightly spikes which show your frame time. Click on a point at which the frame time is high and then have a look at the hierarchy in the lower half of your screen (you might have to switch to it from timeline).

I expect you’ll see this script and function occupying a fair bit of the frame time. If you can see the function taking up a lot of time but want to know more info about where exactly within that function you can add profiler samples. These are pairs of calls to Profiler.BeginSample(“sample name”) and Profiler.EndSample wrapped around the code you want details for. These will then show up within the function showing you how the profile for that region.


Please do do this as this is a great opportunity to figure out how it works so you can solve your issues quickly next time!

BUT I expect the root of your problem is Object.FindObjectsOfType.Where.... If you have a scene that’s anything more than trivially simple, doing this every frame is going to be expensive as hell. The good news is that there is a load of extra work here that can be pretty easily cut down on! To start with, you could maintain a collection of enemies that are alive and ready to be attacked. Some kind of enemy manager could add them when they spawn and remove them when they die. This would prevent you from A) getting every object in the world every frame and B) from using expensive LINQ code with that Where call. I’m sure there are more optimisations you can make that are specific to your setup, maybe it doesn’t need the closest but just 'near enough' if they’re likely to be clustered? Up to you!

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Eno-Khaon · Nov 12, 2020 at 09:27 PM 2
Share

Speaking of optimization, there are numerous other adjustments you could make to improve efficiency.

For one, your closestEnemy() function is clearly a large processing load, but then you also call it three to four times per loop (counting the Debug.Log()).

However, a more concentrated approach can be taken to this, anyway. Your player clearly has a maximum attack range defined, so let's make use of that. You can give your player character a child Transform with a SphereCollider (with radius of attack range) attached to it, set as a "Trigger". Then, when an enemy touches that trigger, it will call OnTriggerEnter(Collider) on your player's script.

 List<EnemyScript> nearbyEnemies = new List<EnemyScript>();

 void OnTriggerEnter(Collider other)
 {
     if(other.CompareTag("enemy")) // more efficient than tag == string
     {
         EnemyScript enemy = other.gameObject.GetComponent<EnemyScript>();
         if(enemy != null && !nearbyEnemies.Contains(enemy))
         {
             nearbyEnemies.Add(enemy);
         }
     }
 }

 void OnTriggerExit(Collider other)
 {
     if(other.CompareTag("enemy"))
     {
         EnemyScript enemy = other.gameObject.GetComponent<EnemyScript>();
         if(enemy != null && nearbyEnemies.Contains(enemy))
         {
             nearbyEnemies.Remove(enemy);
         }
     }
 }

 EnemyScript GetClosestEnemy()
 {
     float closestSqr$$anonymous$$ag = float.Infinity;
     EnemyScript closestEnemy = null;
     
     for(int i = 0; i < nearbyEnemies.Count; i++)
     {
         float currentSqr$$anonymous$$ag = (transform.position - nearbyEnemies).sqr$$anonymous$$agnitude;
         // Square $$anonymous$$agnitude avoids an unnecessary square root
         if(currentSqr$$anonymous$$ag < closestSqr$$anonymous$$ag)
         {
             closestEnemy = nearbyEnemies[i];
             closestSqr$$anonymous$$ag = currentSqr$$anonymous$$ag;
         }
     }
     return closestEnemy; // if null is returned, there are no enemies nearby
 }

 void Update()
 {
     // ...
     // When attacking...
     EnemyScript closestEnemy = GetClosestEnemy();
     if(closestEnemy != null)
     {
         // Do something
     }
 }
avatar image
0

Answer by andrew-lukasik · Nov 13, 2020 at 12:26 AM


60FPS 101, Rule #1:


Never, ever, never use FindObjectsOfType and FindGameObjectsWithTag (tags is general) outside Awake or Start. Period.


How to replace them? Use arrays and System.Collection.Generic.List lists instead.

Code below shows how you can do that and without physics being involved.


Player.cs

 using System.Collections.Generic;
 using UnityEngine;
 [DisallowMultipleComponent]
 public class Player : MonoBehaviour
 {
     public static List<Player> Instances = new List<Player>();
     public float _reach = 3f;
     public int wood = 0;
     void OnEnable () => Player.Instances.Add( this );
     void OnDisable () => Player.Instances.Remove( this );
     void Update ()
     {
         if( Input.GetMouseButtonDown(0) )
         {
             if( this.FindNearest( Enemy.Visible , _reach , out Enemy enemy , out float enemyDist ) )
             {
                 enemy.Hit();
             }
             else if( this.FindNearest( Destructible.Visible , _reach , out Destructible destructible , out float destructibleDist ) )
             {
                 if( destructible.type==DestructibleType.Wood )
                 {
                     wood++;
                     Debug.Log($"You have {wood} wood");
                 }
                 destructible.Die();
             }
         }
     }
     #if UNITY_EDITOR
     void OnDrawGizmosSelected ()
     {
         Gizmos.color = Color.cyan;
         Gizmos.DrawWireSphere( transform.position , _reach );
     }
     #endif
 }


Enemy.cs

 using System.Collections.Generic;
 using UnityEngine;
 [DisallowMultipleComponent]
 [RequireComponent( typeof(SkinnedMeshRenderer) )]
 public class Enemy : MonoBehaviour
 {
     public static List<Enemy> Instances = new List<Enemy>();
     public static List<Enemy> Visible = new List<Enemy>();
     [SerializeField] float _life = 3f;
     void OnEnable () => Enemy.Instances.Add( this );
     void OnDisable () => Enemy.Instances.Remove( this );
     void OnBecameVisible () => Enemy.Visible.Add( this );
     void OnBecameInvisible () => Enemy.Visible.Remove( this );
     public void Hit ()
     {
         Debug.Log($"{this.name} hit");
 
         /* hit code */
         
         if( _life>0f )
         {
             _life -= 1.6f;
             if( _life<=0f ) Die();
         }
     }
     public void Die ()
     {
         _life = 0f;
 
         /* death code */
 
         Debug.Log($"{this.name} died");
         this.gameObject.SetActive( false );
     }
 }


Destructible.cs

 using System.Collections.Generic;
 using UnityEngine;
 [DisallowMultipleComponent]
 [RequireComponent( typeof(MeshFilter) )]
 [RequireComponent( typeof(MeshRenderer) )]
 public class Destructible : MonoBehaviour
 {
     public static List<Destructible> Visible = new List<Destructible>();
     public DestructibleType type = DestructibleType.Undefined;
     void OnBecameVisible () => Destructible.Visible.Add( this );
     void OnBecameInvisible () => Destructible.Visible.Remove( this );
     public void Die ()
     {
         /* death code */
         
         Debug.Log($"{this.name} destroyed");
         this.gameObject.SetActive( false );
     }
 }
 
 public enum DestructibleType
 {
     Undefined   = 0 ,
     Wood        = 1 ,
     Stone       = 2
 }


ComponentExtentionMethods.cs

 using System.Collections.Generic;
 using UnityEngine;
 public static class ComponentExtentionMethods
 {
     public static bool FindNearest <T,K>
     (
         this T me ,
         List<K> others ,
         float maxSearchDistance ,
         out K nearest ,
         out float nearestDist
     )
         where T : Component
         where K : Component
     {
         Vector3 myPosition = me.transform.position;
         float maxSearchDistanceSq = maxSearchDistance * maxSearchDistance;
         float nearestDistSq = float.MaxValue;
         bool hit = false;
         nearest = null;
         int len = others.Count;
         for( int i=0 ; i<len ; i++ )
         {
             K next = others[i];
             float nextDistSq = Vector3.SqrMagnitude( next.transform.position - myPosition );
             // if( closer && below search threshold && not me )
             if( nextDistSq<nearestDistSq && nextDistSq<maxSearchDistanceSq && nextDistSq>0f )
             {
                 nearest = next;
                 nearestDistSq = nextDistSq;
                 hit = true;
             }
         }
         nearestDist = hit ? Mathf.Sqrt(nearestDistSq) : float.MaxValue;
         return hit;
     }
     public static bool FindNearest <T,K>
     (
         this T me ,
         List<K> others ,
         float maxSearchDistance ,
         out K nearest
     )
         where T : Component
         where K : Component
     {
         return FindNearest( me:me , others:others , maxSearchDistance:maxSearchDistance , out nearest , out float nearestDist  );
     }
     public static bool FindNearest <T,K>
     (
         this T me ,
         K[] others ,
         float maxSearchDistance ,
         out K nearest ,
         out float nearestDist
     )
         where T : Component
         where K : Component
     {
         Vector3 myPosition = me.transform.position;
         float maxSearchDistanceSq = maxSearchDistance * maxSearchDistance;
         float nearestDistSq = float.MaxValue;
         bool hit = false;
         nearest = null;
         int len = others.Length;
         for( int i=0 ; i<len ; i++ )
         {
             K next = others[i];
             float nextDistSq = Vector3.SqrMagnitude( next.transform.position - myPosition );
             // if( closer && below search threshold && not me )
             if( nextDistSq<nearestDistSq && nextDistSq<maxSearchDistanceSq && nextDistSq>0f )
             {
                 nearest = next;
                 nearestDistSq = nextDistSq;
                 hit = true;
             }
         }
         nearestDist = hit ? Mathf.Sqrt(nearestDistSq) : float.MaxValue;
         return hit;
     }
     public static bool FindNearest <T,K>
     (
         this T me ,
         K[] others ,
         float maxSearchDistance ,
         out K nearest
     )
         where T : Component
         where K : Component
     {
         return FindNearest( me:me , others:others , maxSearchDistance:maxSearchDistance , out nearest , out float nearestDist  );
     }
 }
Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

145 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Find Closest GameObject in a players vicinity 1 Answer

FindClosestEnemy gives Error CS0165: Use of unassigned local variable 'closest' 2 Answers

I'm finding closest with GameObject.FindGameObjectsWithTag("Enemy"). How can I use this code (or anything else) to find nearest enemy game object in multiplayer (unet)? 1 Answer

how to find the closest (and second closest) object with tag - one object 4 Answers

AI script troubles 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges