I am recently working on a 2d Game which have massive enemies
(I always like to watch massive enemies coming, it give me chill)
however, when the enemies number get to too big like 100 or 200. The game starting to get very slow.
I am pretty sure this issue is mostly due to all those collides from enemies to enemies pushing eachother, but I don’t want to delete colliders because then enemies just collapse together like one enemy. it doesn’t looks right.
so is there any other ways to fix this issue?
here is the enemy script:
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.UIElements;
using UnityEngine;
public class Enemy : MonoBehaviour
{
GameManager gameManager;
Property thisProperty;
public LayerMask targetLayer;
public GameObject baseStation;
public GameObject nearestTarget;
public Collider2D attackCollider;
public List<GameObject > allTargets = new List<GameObject> ();
public float visionRange;
public float attackRange;
public float moveSpeed=1;
public float attackAnimationWaitTime = 0.5f;
public float nextAttackTime;
public float attackWaitTime=1;
public Vector2 offSetOfThisToTarget;
public bool TargetInVisionRange;
public bool TargetInAttackRange;
void Start()
{
thisProperty = this.GetComponent<Property>();
gameManager = FindObjectOfType<GameManager>();
baseStation = gameManager.BaseStation;
}
// Update is called once per frame
void Update()
{
FindNearestTarget();
MovingToTargetPosition();
AttackingTarget();
}
public void FixedUpdate()
{
}
public void AttackingTarget()
{
if (nearestTarget.GetComponent<Rigidbody2D>().IsTouching(attackCollider))
{
TargetInAttackRange = true;
}
else
{
TargetInAttackRange = false;
}
if(TargetInAttackRange == true&&Time.time>nextAttackTime )
{
this.GetComponent<Animator>().SetTrigger("attack");
nextAttackTime = Time.time + attackWaitTime;
StartCoroutine(attackWaitAnimation());
}
}
IEnumerator attackWaitAnimation()
{
GameObject currentTarget = null;
currentTarget = nearestTarget;
yield return new WaitForSeconds(attackAnimationWaitTime);
if (nearestTarget != null)
{
if (nearestTarget.GetComponent<Rigidbody2D>().IsTouching(attackCollider))
{
currentTarget.GetComponent<Property>().GetDamage(thisProperty.damage);
}
}
}
public void MovingToTargetPosition()
{
Vector2 offSetBetweenThisAndTarget= this.transform .position;
if (nearestTarget != null)
{
offSetBetweenThisAndTarget = new Vector2(nearestTarget.transform.position.x - this.transform.position.x, nearestTarget.transform.position.y - this.transform.position.y);
}
if (TargetInAttackRange == false )
{
this.transform.position = Vector2.MoveTowards(this.transform.position, nearestTarget.transform.position, moveSpeed * Time.deltaTime);
}
if (nearestTarget != null)
{
this.GetComponent<Animator>().SetBool("moving", true);
}
else
{
this.GetComponent<Animator>().SetBool("moving", false);
}
if(Mathf.Atan2(offSetBetweenThisAndTarget.x, offSetBetweenThisAndTarget.y) * Mathf.Rad2Deg < 0)
{
this.GetComponent<SpriteRenderer>().flipX = true;
}
else
{
this.GetComponent<SpriteRenderer>().flipX = false;
}
}
void FindNearestTarget()
{
allTargets.Clear();
allTargets.AddRange(GameObject.FindGameObjectsWithTag("player"));
allTargets.AddRange(GameObject.FindGameObjectsWithTag("ally"));
allTargets.AddRange(GameObject.FindGameObjectsWithTag("building"));
float distanceBtweenTargetAndThis = Mathf.Infinity;
foreach (GameObject enemyObj in allTargets)
{
if (Vector2.Distance(this.gameObject.transform.position, enemyObj.transform.position) <= distanceBtweenTargetAndThis)
{
distanceBtweenTargetAndThis = Vector2.Distance(this.gameObject.transform.position, enemyObj.transform.position);
nearestTarget = enemyObj;
}
}
if (distanceBtweenTargetAndThis < visionRange)
{
TargetInVisionRange = true;
}
else
{
TargetInVisionRange = false;
nearestTarget = baseStation;
}
if(nearestTarget != null)
{
offSetOfThisToTarget = nearestTarget.transform.position - this.gameObject.transform.position;
}
}
public void OnDrawGizmosSelected()
{
Gizmos.DrawWireSphere(this.gameObject.transform.position, visionRange);
}
}
what kurt said - profile first and then fix the problem you see, not the problem you guess.
if collision is an issue though:
Perhaps keeping enemies apart with collisions is the wrong approach.
you might want to investigate flocking behaviours.
if you haven’t played around with the physics parameters in the project settings then that could also help. Reduce iterations, reduce the physics timestep. Although depending on how your player moves this may not be an acceptable solution
i did tried to use time or physics2d or quality panel in the project setting to fix the problem, but it seems not doing much, and now i am trying to do what u said flocking behaviours.
i end up add some physic2d Line cast to make enemies collide less
flock behavior is bit difficult for me, and i need more time digging in it, so for now the code will be like that.
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
public class Enemy : MonoBehaviour
{
GameManager gameManager;
Property thisProperty;
public LayerMask targetLayer;
public LayerMask OtherEnemyLayer;
public SpriteRenderer thisSpriteRender;
public Animator thisAnimator;
public GameObject baseStation;
public GameObject nearestTarget;
public Collider2D attackCollider;
public List<GameObject > allTargets = new List<GameObject> ();
public float visionRange;
public float attackRange;
public float moveSpeed=1;
public float attackAnimationWaitTime = 0.5f;
public float nextAttackTime;
public float attackWaitTime=1;
public Vector2 offSetOfThisToTarget;
public bool TargetInVisionRange;
public bool TargetInAttackRange;
public bool FriendlyObjectInfront;
public bool FriendlyObjectInRandomPos;
void Start()
{
thisProperty = this.GetComponent<Property>();
gameManager = FindObjectOfType<GameManager>();
baseStation = gameManager.BaseStation;
}
void Update()
{
FindNearestTarget();
AvoidOtherFriendlyObject();
if (FriendlyObjectInfront == false)
{
MovingToTargetPosition();
}
AttackingTarget();
}
public void AttackingTarget()
{
if (nearestTarget.GetComponent<Rigidbody2D>().IsTouching(attackCollider))
{
TargetInAttackRange = true;
}
else
{
TargetInAttackRange = false;
}
if(TargetInAttackRange == true&&Time.time>nextAttackTime )
{
thisAnimator.SetTrigger("attack");
nextAttackTime = Time.time + attackWaitTime;
StartCoroutine(attackWaitAnimation());
}
}
IEnumerator attackWaitAnimation()
{
GameObject currentTarget = null;
currentTarget = nearestTarget;
yield return new WaitForSeconds(attackAnimationWaitTime);
if (nearestTarget != null)
{
if (nearestTarget.GetComponent<Rigidbody2D>().IsTouching(attackCollider))
{
currentTarget.GetComponent<Property>().GetDamage(thisProperty.damage);
}
}
}
public void MovingToTargetPosition()
{
Vector2 offSetBetweenThisAndTarget = new Vector2(nearestTarget.transform.position.x - this.transform.position.x, nearestTarget.transform.position.y - this.transform.position.y);
if (TargetInAttackRange == false)
{
this.transform.position = Vector2.MoveTowards(this.transform.position, nearestTarget.transform.position, moveSpeed * Time.deltaTime);
}
if (nearestTarget != null)
{
thisAnimator.SetBool("moving", true);
}
else
{
thisAnimator.SetBool("moving", false);
}
if(Mathf.Atan2(offSetBetweenThisAndTarget.x, offSetBetweenThisAndTarget.y) * Mathf.Rad2Deg < 0)
{
thisSpriteRender.flipX = true;
}
else
{
thisSpriteRender.flipX = false;
}
}
void FindNearestTarget()
{
allTargets.Clear();
allTargets.AddRange(GameObject.FindGameObjectsWithTag("player"));
allTargets.AddRange(GameObject.FindGameObjectsWithTag("ally"));
allTargets.AddRange(GameObject.FindGameObjectsWithTag("building"));
float distanceBtweenTargetAndThis = Mathf.Infinity;
foreach (GameObject enemyObj in allTargets)
{
if (Vector2.Distance(this.gameObject.transform.position, enemyObj.transform.position) <= distanceBtweenTargetAndThis)
{
distanceBtweenTargetAndThis = Vector2.Distance(this.gameObject.transform.position, enemyObj.transform.position);
nearestTarget = enemyObj;
}
}
if (distanceBtweenTargetAndThis < visionRange)
{
TargetInVisionRange = true;
}
else
{
TargetInVisionRange = false;
nearestTarget = baseStation;
}
if(nearestTarget != null)
{
offSetOfThisToTarget = nearestTarget.transform.position - this.gameObject.transform.position;
}
}
public void AvoidOtherFriendlyObject()
{
UnityEngine.Debug.DrawLine((Vector2)this.gameObject.transform.position + ((Vector2)this.GetComponent<Collider2D>().bounds.size * ((Vector2)nearestTarget.transform.position - (Vector2)this.gameObject.transform.position).normalized)
, (Vector2)this.transform.position + ((Vector2)attackCollider.bounds.size * ((Vector2)nearestTarget.transform.position - (Vector2)this.gameObject.transform.position)).normalized);
//here is where i made change
RaycastHit2D[] otherEnemyLine = Physics2D.LinecastAll((Vector2)this.gameObject.transform.position + ((Vector2)this.GetComponent<Collider2D>().bounds.size * ((Vector2)nearestTarget.transform.position - (Vector2)this.gameObject.transform.position).normalized)
, (Vector2)this.transform.position + ((Vector2)attackCollider.bounds.size * ((Vector2)nearestTarget.transform.position - (Vector2)this.gameObject.transform.position)).normalized);
FriendlyObjectInfront = false;
foreach(RaycastHit2D hited in otherEnemyLine)
{
if(hited .collider.gameObject.tag == "enemy"|| hited.collider.gameObject.tag == "building")
{
FriendlyObjectInfront = true;
}
}
if(FriendlyObjectInfront == true)
{
Vector2 randomPositionToGo = new Vector2(Random.Range(this.transform .position.x-1, this.transform.position.x+1), Random.Range(this.transform.position.y- 1, this.transform.position.y+1));
//here is where i made change
RaycastHit2D[] otherEnemyLineInRandomPos = Physics2D.LinecastAll((Vector2)this.transform.position + ((Vector2)this.GetComponent<Collider2D>().bounds.size * (randomPositionToGo - (Vector2)this.transform.position).normalized)
, (Vector2)this.transform.position + ((Vector2)attackCollider.bounds.size * ((Vector2)randomPositionToGo - (Vector2)this.gameObject.transform.position)).normalized);
FriendlyObjectInRandomPos = false;
foreach (RaycastHit2D hited in otherEnemyLineInRandomPos)
{
if(hited.collider.gameObject.tag == "enemy" || hited.collider.gameObject.tag == "building")
{
FriendlyObjectInRandomPos = true;
}
}
if (FriendlyObjectInRandomPos==false)
{
this.transform.position = Vector2.MoveTowards(this.transform.position, randomPositionToGo, moveSpeed * Time.deltaTime);
}
else
{
this.transform.position= Vector2.MoveTowards(this.transform.position, this.transform.position, moveSpeed * Time.deltaTime);
thisAnimator.SetBool("moving", false);
}
}
}
public void OnDrawGizmosSelected()
{
Gizmos.DrawWireSphere(this.gameObject.transform.position, visionRange);
}
}