Help with an Enemy AI

v 4.1.1

Question

I am making an enemy with to main states: patrolling and seeking.
Currently, patrolling state is activated whenever is player out of chasing range. But I want to be activated at start and then if player gets closer it will be permanently switched to seeking state.

In short, i want to have patrolling state as like some kind of spawn state and if player gets closer he would be permanently chased down by an enemy.

The problem is that now, if player runs away from enemy, the enemy will switch back to patrolling state. Here is my physics process with all functions:

	public override void _PhysicsProcess(double delta)
	{
		wallCheck.LookAt(new Vector3(player.Position.X, Position.Y, player.Position.Z), Vector3.Up); //raycast that detects walls

#region EYE LOOK
		eyes.LookAt(new Vector3(player.Position.X, player.Position.Y, player.Position.Z), Vector3.Up);
		Vector3 eyeRot = eyes.Rotation;
		eyeRot.X = Mathf.Clamp(eyeRot.X, Mathf.DegToRad(-45), Mathf.DegToRad(45));
		eyeRot.Y = Mathf.Clamp(eyeRot.Y, Mathf.DegToRad(-45), Mathf.DegToRad(45));
		eyes.Rotation = eyeRot;
#endregion


		if (stayTime > 0)
		{
			stayTime -= (float)delta;
			Animations.Play(animIdle);
			StateChange = false;
  			return;
		}


		var points = patrolPoints[_nextPointIndex].GlobalPosition;
		var victim = player.GlobalTransform.Origin;
		var gotoTarget = points;


		if(!navAgent.IsTargetReachable())
		{
			if(TargetMid())
			{
				LookAt(new Vector3(player.GlobalPosition.X, GlobalPosition.Y, player.GlobalPosition.Z), Vector3.Up);
			}
		}

		if (wallCheck.IsColliding())
		{
			var Interaction = wallCheck.GetCollider() as StaticBody3D;

			if(Interaction != null)
			{
				Speed = 0f;
			}
		}

		if (eyes.IsColliding())
		{
			var Interaction = eyes.GetCollider() as CharacterBody3D;

			if(Interaction != null)
			{
				if (Interaction.IsInGroup("player"))
				{
					ChasingMode();
				}
			}
		}


		if(Patrolling())
		{
			PatrolMode();
		}


#region PATH SETUP
		var NextNavPoint = navAgent.GetNextPathPosition();
		Velocity = (NextNavPoint - GlobalTransform.Origin).Normalized() * Speed;
#endregion
		
#region ROTATION
		var rotation = new Quaternion(Basis).Normalized();

		if(Velocity != Vector3.Zero)
		{
			LookAt(new Vector3(GlobalPosition.X + Velocity.X, GlobalPosition.Y, GlobalPosition.Z + Velocity.Z), Vector3.Up);
		}
		else if(Velocity == Vector3.Zero)
		{
		}


		var targetRot = new Quaternion(Basis).Normalized();
		Rotation = rotation.Slerp(targetRot, 0.15f).Normalized().GetEuler();
#endregion




		MoveAndSlide();
	}


	public bool Patrolling()
	{
		return GlobalPosition.DistanceTo(player.GlobalPosition) > chaseRange;
	}
    public bool Seeking()
	{
		return GlobalPosition.DistanceTo(player.GlobalPosition) < chaseRange;
	}

	public bool TargetClose()
	{
		return GlobalPosition.DistanceTo(player.GlobalPosition) < atkRange;
	}

	public bool TargetMid()
	{
		return GlobalPosition.DistanceTo(player.GlobalPosition) < 5f;
	}

    public bool TargetFar()
	{
		return GlobalPosition.DistanceTo(player.GlobalPosition) > atkRange;
	}





		public void ChasingMode()
		{
			var points = patrolPoints[_nextPointIndex].GlobalPosition;
			var victim = player.GlobalTransform.Origin;
			var gotoTarget = points;


			if(StateChange == true)
			{
				if(TargetFar())
     		  	{
					//CHASING
    		     	Animations.Play(animRun);
					Speed = defSpeed;

					StateChange = false;

					navAgent.TargetPosition = victim;
				}
   			}
			if(StateChange == true)
			{
				if(TargetClose())
  				{
					//ATTACKING
 		          	Animations.Play(animAttack);
			  		Speed = atkSpeed;

					StateChange = false;

			  	 	navAgent.TargetPosition = victim;

				   	LookAt(new Vector3(player.GlobalPosition.X, GlobalPosition.Y, player.GlobalPosition.Z), Vector3.Up);

    	  		}
			}
		}

		public void PatrolMode()
		{
			var points = patrolPoints[_nextPointIndex].GlobalPosition;
			var victim = player.GlobalTransform.Origin;
			var gotoTarget = points;


			Patrol();

            if(TargetFar())
        	{
				//wALKING
                Animations.Play(animWalk);
		    	Speed = wondSpeed;

				StateChange = false;

				navAgent.TargetPosition = points;
        	}
			if(StateChange == true)
			{
            	if(TargetClose())
    	        {
					//STAYING

					Animations.Play(animIdle);
				   	//Speed = 0f;

					StateChange = false;

					navAgent.TargetPosition = points;
         	   }
			}
		}





        public void Patrol()
    {
        if (patrolPoints[_nextPointIndex].GlobalPosition.DistanceTo(GlobalPosition) < atkRange)
			{
				Speed = 0f;
				stayTime = patience;
				//walking = true;

				_nextPointIndex++;
				_nextPointIndex = _nextPointIndex % patrolPoints.Count; // Wrap from length (out of bounds) back to 0.
			}
    }
}

If some variable is unclear let me know.

C# is a little difficult for me to read, but I think your problem is here:

public bool Patrolling()
	{
		return GlobalPosition.DistanceTo(player.GlobalPosition) > chaseRange;
	}

This seems to say that if the enemy is further away than the chaseRange (which I don’t see declared here) they will go back to patrolling with:

if(Patrolling())
		{
			PatrolMode();
		}

It seems to me that you’d want to remove that check and just set Patrolling to true when the enemy spawns.

Then in your:

public bool Seeking()
	{
		return GlobalPosition.DistanceTo(player.GlobalPosition) < chaseRange;
	}

You don’t want this to return false again so you’d need to add something like:

public bool Seeking()
	{
		if Seeking() == false 
		{
			return GlobalPosition.DistanceTo(player.GlobalPosition) < chaseRange;
		}
	}

So that when Seeking is true it will never check the condition again.

Sorry if the syntax is wrong, I don’t use C#.

1 Like

bro thanks a lot. Your code was a part of big reconstruction to make it work.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.