making enemy ships detect and dodge projectiles in a 2d spaceship game

I have a “2d” (the game is actually 3d, while the camera is top-down orthogonal 2d style) space shooter game based on the “Space Shooter” tutorial project. I’m working on adding enemies. I want the enemies to try to avoid the player bolts. I spent few hours trying several approaches, but no cigar. Need some input

  1. I tried making the bolt prefab (the player’s shots) to have a Nav Mesh Obstacle component. This causes the enemy ship to dodge the bolts ‘no matter what’ at ridiculous speed, even though the enemy ship’s NavAgent speed is set. If I have to describe it it’s more like the NavAgent geometry and the Nav obstacle (the bolts) geometry can’t touch, and thus the fast moving obstacle “pushes” the agent out of the way. This is obviously not desirable, the enemy ships should try to dodge incoming shots, but often fail so they can actually get hit.
  2. I made a Trigger collider component on an empty child object, in front of the enemy ship to detect when a projectile enters it. Kinda like a vision cone (there’s no built in cone collider so it’s just a rectangular box), where I can adjust its length to change how early the enemy can detect incoming projectiles. Then the plan is to then use NavAgent’s SetDestination to move out of the way. Now the problem is both the enemy’s basic collider, and this new “vision” collider Triggers when the projectile enters any of them, even though the script is attached to the child GameObject (which only contains the vission collider), so the enemy ship would explode when either collider detects the bolt. I can’t put these 2 colliders on separate layer because I need the projectile to be able to trigger both the physical collider and the vision collider, just the correct one at a time.

This seems like would be a common problem but I couldn’t find any solutions from googling.

Thanks

The dodger script attached to the child GameObject that has the vision trigger collider

public class Dodger : MonoBehaviour {
	public EnemyController owner;

	private int hazardsSeen = 0;

	void OnTriggerEnter( Collider other ) {
		if ( other.CompareTag("Hazards") ) {
			if ( hazardsSeen == 0 ) {
				owner.StartDodging(other.transform.position.x);
			}
			hazardsSeen++;
		}
	}

	void OnTriggerExit( Collider other ) {
		if ( other.CompareTag("Hazards") ) {
			hazardsSeen--;
			if ( hazardsSeen == 0 ) {
				owner.StopDodging();
			}
		}
	}

}

Relevant Part from enemy controller script

public class EnemyController : MonoBehaviour {
	public Transform playerTransform;

	public float desiredDistance; //how far to move in front of player
 
	NavMeshAgent navAgent;
    
	void Start() {
		navAgent = GetComponent<NavMeshAgent>();
		nextFire = firstFireDelay;
		shotSpawn = transform.Find("Shot Spawn");
		
		SetDestination();
	}


	void SetDestination(bool dodge = false, float hazardsX = 0) {
		if ( !dodge ) {
			// set destination to desiredDistance away in front of the players
			Vector3 targetDestination = new Vector3(
				playerTransform.position.x,
				0,
				playerTransform.position.z + desiredDistance
				);
			navAgent.SetDestination(targetDestination);

		} else { 
			// else dodge, move perpendicular to incoming's x
			Vector3 targetDestination = new Vector3(0, 0, transform.position.z);
			if ( transform.position.x > hazardsX ) { //if ship is to the right of the incoming
				targetDestination.x = 5f;
			} else {
				targetDestination.x = -5f;
			}
			navAgent.SetDestination(targetDestination);
		}
	}

	public void StartDodging(float x) {
		// x is the hazard's vector3 x value
		SetDestination(true);
	}

	public void StopDodging() {
		SetDestination(false);
	}
}

Hello,

  1. It’s not the way to go i think. But you already figured that out.
  2. Its better in my opinion, but i don’t see why you can’t use Layers. You need to make 2 colliders on the bullets, 1 in a child of the bullet, so that only 1 collider from the ship triggers it.

However, i would go on a different path:
Use Physics.RaycastAll (cast a ray towards all colliders of a certain type up to a certain distance, get all that have hit) or use Physics.OverlapSphere (get ALL colliders inside certain sphere, useful if you want your ennemy to also dodge with regards to ships as well as bullets).

Once you have your informations (where are the bullets coming toward me for example), compute a dodge direction or destination, and begin to move towards it at a certain speed the way you want.

In the end I learnt that a game object’s collider includes all its and children’s colliders. I guess it kinda makes sense. So I ended up making another child called ‘Enemy Body’ which contains the physical collider and other stuff. The being hit stuff is handled in its own game object over there, the trigger colliders are being handled in its own different game object.

I’m going to stay with the cone collider because I’m doing this for learning, and I gt learn dealing with this approach. For instances like detecting things spherically outwards. And I ended up adding a sphere collider (could’ve been a cylinder collider, but sphere’s prob cheaper) to detect asteroids coming from any direction and dodge out of the way.