How to do: Melee attack movment AI around player

So I made a hack and slash prototype where generic place holder box enemies move to and attack the player when close. The player can slash at them with a sword and kill them. Seems wonderful but there is one really terrible problem…

As you can see, enemies in the back get stuck on the enemy that gets to the player first. So they stop. In AAA games multiple melee AIs will surround the player and attack several at a time. That’s what I want! But I can’t even begin to begin to even think about beginning to think about beginning that. :face_with_spiral_eyes:

I imagine there have been many zombie game projects out there with this same issue. So, can anyone point me in the right direction as to what is generally done to have enemies do something other than stick to the butt of the enemy that’s blocking them? I keep thinking to have them move to the side if it’s clear then try to find a lien of sight again but I am not sure how to begin.

Here’s a chart illustrating what the AI is doing on the left and what I want them to do on the right.

Use a raycast, if the raycast is hitting another enemy move them until they have a clear line of site then let them charge at the player again.

Yes, I understand the theory of what to do already. My problem is is the part where the AI checks to see if it’s hitting another enemy. Here is the default line of sight code for the AI from the FPS tutorial.

function CanSeeTarget () : boolean {
	if (Vector3.Distance(transform.position, target.position) > attackRange)
		return false;
	var hit : RaycastHit;

	if (Physics.Linecast (transform.position, target.position, hit))
		return hit.transform == target;

	return false;
}

Here it uses target.position (target = player) to do a physics linecast, whatever that means, idk. So should I add another line in: if (Physics.Linecast (transform.position, target2.position, hit)) Where target2 is another enemy then return hit.transform == ???; I don’t understand how I could tell the AI to move some arbitrary location.

Ok so your function can see target returns true your enemy just continues straight ahead, that’s fine.If not you need a function to do some maneuvering. There’s lots of different ways of doing this. You can cast two more rays one slightly to the right and one to the left and use these to make a decision on which way to move, then move until you get a true result from your can see target function.

Do a search for “Object Avoidance”.

Alright, I’ll look into it.

See my post is an NPC smart.
My NPC use Object Avoidance raycasting.
http://forum.unity3d.com/threads/100102-Problem-with-Waypoints-HELP!?p=655758#post655758

Well I think I found a good tutorial here http://vimeo.com/9304844 that will solve my problem. If I am successful I will post what I did for others to learn from.

its the same i use for my NPC :wink: is an good tutorial, whith creativity we get more;)

I used the guy’s tutorial and the AI did indeed avoid obstacles and find it’s way to me but it would not avoid other instances of its own kind. I turned off collision between enemies but they have this tendency to ram through solid walls and the player regardless of colliders. Sometimes they will go off in some arbitrary direction. I’ll post my code. I’m about to ditch it and just do something easier.

var target : Transform; //The target of which we seek
var attackRange = 20.0;
var yieldRange = 15.0;
var stopRange = 2.5;
var rotationSpeed = 5.0;
var onTrail = 0;
var moveOrders = 0;

function Update () {
transform.eulerAngles.x = 0;
transform.eulerAngles.z = 0;

//Enemy is within attack range
if (transform){
var sqrDistance = (target.position - transform.position).sqrMagnitude;
// square the distance we compare with
if( sqrDistance < attackRange*attackRange  sqrDistance > stopRange  CanSeeTarget ())
	{
	moveOrders += 1;
	onTrail = 1;
	print("HERE I GO!");
	MoveTowardsPlayer();
	}
}

//Enemy is close, stay facing him
if( sqrDistance < yieldRange)
{FaceFoe();}

if (!CanSeeTarget ()  onTrail == 1  sqrDistance < attackRange*attackRange  sqrDistance > stopRange){
	SearchPlayer();}
		
}

function CanSeeTarget () : boolean {
	if (Vector3.Distance(transform.position, target.position) > (attackRange*attackRange + 5))
		return false;
			
	var hit : RaycastHit;
	if (Physics.Linecast (transform.position, target.position, hit))
		return hit.transform == target;

	return false;
}

function MoveTowardsPlayer () {
//Move to the player!!!
//The directional Vector to our target
var hit : RaycastHit;
var dir = (target.position - transform.position).normalized;

transform.eulerAngles = Vector3(0, transform.eulerAngles.y, 0);

if(Physics.Raycast(transform.position, transform.forward, hit, attackRange)){
	if(hit.transform != transform){
		dir += hit.normal * 3;
	}
}

var leftR = transform.position;
var rightR = transform.position;

leftR.x -= 1;
rightR.x += 1;

if(Physics.Raycast(leftR, transform.forward, hit, attackRange)){
	if(hit.transform != transform){
		dir += hit.normal * 3;
	}
}

if(Physics.Raycast(rightR, transform.forward, hit, attackRange)){
	if(hit.transform != transform){
		dir += hit.normal * 3;
	}
}

var rot = Quaternion.LookRotation(dir);

transform.rotation = Quaternion.Slerp(transform.rotation, rot, rotationSpeed * Time.deltaTime);
transform.position += transform.forward * 3 * Time.deltaTime;

//Finish move to the player code

if (transform){
var sqrDistanceB = (target.position - transform.position).sqrMagnitude;
// square the distance we compare with
if( sqrDistanceB < stopRange)
	{
	print("I got too close so I stopped!");
	yield;
	} 
}

}

function SearchPlayer () {   
print("searching");
	// Run towards the player but after 3 seconds timeout and go back to doing whatever
	var timeout = 3.0;
	while (timeout > 0.0) {
	onTrail = 2;
	transform.eulerAngles = Vector3(0, transform.eulerAngles.y, 0);
	transform.position += transform.forward * 3 * Time.deltaTime;
	
		// We found the player
		if (CanSeeTarget ())
			{
			yield;
			}
			
		timeout -= Time.deltaTime; 
		yield;
	}


var sqrDistanceC = (target.position - transform.position).sqrMagnitude;
// square the distance we compare with
if( sqrDistanceC < stopRange)
	{
	print("Oh, I can't see the player so I will stop ramming him!");
	yield;
	} 


}

function FaceFoe () {
var dir = (target.position - transform.position).normalized;

var rot = Quaternion.LookRotation(dir);
transform.eulerAngles = Vector3(0, transform.eulerAngles.y, 0);
transform.rotation = Quaternion.Slerp(transform.rotation, rot, rotationSpeed * Time.deltaTime);
}

Okay bump and follow up:

I went back to using the FPS tutorial code and I discovered the biggest difference effecting the enemies getting stuck on stuff now was if they had a collision layer on. But it’s weird…

When the enemies have a collision layer on to avoid collisions with their own kind they slide smoothly around environment obstacles, but then they all end up standing in the same space. The thing is in my project I really don’t care about environment obstacles I just want the freaking enemies to be able to move to attack the player without lining up behind each other, jumping on each others’ heads, flying into the sky, running through a wall or standing in all in the same space.

Making melee AI for multiple enemies is really hard compared to ranged AI.

Is there a way to make it so an object won’t have collision with itself but still will with others of it’s own kind?

Whatevers. They get around stuff well enough with the basic FPS tutorial AI. Melee attack AI won’t compose enough of my game for me to worry about it more.