In my game I spawn in zombies that are meant to chase the player when the player gets in the radius. The following code is what does that but my problem is that there are over 100 zombies meaning that this script is running over 100 times each frame which causes insane lag. I am getting around 1.7FPS but when I commented out the script my FPS shot up to 40 FPS. Is there another way to run this function?
(Sorry if it is bad/messy code I am a noob)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ZK_attack : MonoBehaviour
{
public Transform Player;
public float MoveSpeed = 3.5f;
public float InRadius = 4.0f;
public float AttackRange = 1.0f;
private Animator anim;
private void Start()
{
anim = GetComponent<Animator>();
}
void Update()
{
Player = GameObject.Find("Player").transform;
transform.LookAt(Player);
if (Vector3.Distance(transform.position, Player.position) <= InRadius)
{
transform.position += transform.forward * MoveSpeed * Time.deltaTime;
anim.SetBool("inRadius", true);
if (Vector3.Distance(transform.position, Player.position) <= AttackRange)
{
anim.SetBool("AttackingPlayer", true);
}
}
if (Vector3.Distance(transform.position, Player.position) >= InRadius)
{
anim.SetBool("inRadius", false);
}
if (Vector3.Distance(transform.position, Player.position) >= AttackRange)
{
anim.SetBool("AttackingPlayer", false);
}
}
}
1. Do not call GameObject.Find
every frame (i.e in Update
). cache it once in OnEnable
/ Start
2. Do not call Vector3.Distance
3 times every frame. Use (transform.position - Player.position).sqrMagnitude
once and store the value in a variable
3. Use hash parameters for your animator Unity - Scripting API: Animator.StringToHash and use those hashes in place of your string keys when calling anim.SetBool
4. Consider using a single “master” script which loops through “dumb” zombies (i.e not holding any script) see here: 10000 Update() calls | Unity Blog
This may not solve all your problems, but it will definitely increase your FPS.
Otherwise, you may need to use DOTS: DOTS - Unity’s Data-Oriented Technology Stack
public Transform Player;
private static int inRadiusHash = Animator.StringToHash("inRadius");
private static int attackPlayerHash = Animator.StringToHash("AttackingPlayer");
private void Start()
{
anim = GetComponent<Animator>();
Player = GameObject.Find("Player").transform;
}
void Update()
{
float sqrDistanceFromPlayer = (transform.position - Player.position).sqrDistance;
// Need InRadius * InRadius because we've computed a SQUARE distance,
// or just increaseInRadius in the inspector to avoid the multiplication, i.e put 100 instead of 10 for instance
if(sqrDistance <= InRadius * InRadius)
{
transform.position += transform.forward * MoveSpeed * Time.deltaTime;
anim.SetBool(inRadiusHash, true);
}
else
{
anim.SetBool(inRadiusHash, false);
}
anim.SetBool(attackPlayerHash, sqrDistance <= AttackRange * AttackRange);
}
There are a lot of things you can do to fix it.
GameObject.Find(); is very heavy, you are searching through every game object in the scene and comparing the name by string, which is also very slow, 100 per frame! Calling your player transform Player is also confusing, it should not be capitalized because it’s an instance, it should be player, or playerTransform.
Transform player;
void Start()
{
player = GameObject.Find("Player").transform;
}
Just that will probably increase fps drastically. Now if you want more efficiency rename Update to ZombieUpdate and call it from another object. I have a free asset to help with that here:
I found that this solved it
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ZK_attack : MonoBehaviour
{
public Transform Player;
public float MoveSpeed = 3.5f;
public float InRadius = 4.0f;
public float AttackRange = 1.0f;
private Animator anim;
private void Start()
{
anim = GetComponent<Animator>();
Player = GameObject.Find("Player").transform;
}
void Update()
{
transform.LookAt(Player);
float dstSqr = (Player.position - transform.position).sqrMagnitude;
bool inRadius = (dstSqr <= InRadius * InRadius);
bool inAttackRange = (dstSqr <= AttackRange * AttackRange);
anim.SetBool("inRadius", inRadius);
anim.SetBool("AttackingPlayer", inAttackRange);
if (inRadius)
{
transform.position += transform.forward * MoveSpeed * Time.deltaTime;
}
}
}