The question is answered, right answer was accepted
How to stop a.i. just circling eachother?
Hi all,
I'm a hobbyist with very little experience, but have been trying to avoid buying pre-built assets and am having a stab at creating my own A.I. logic. I've been scouring the forums to get ideas on how to implement it, and have settled on a simple finite state machine (fsm). Though i'm thinking my problem may need a stacked fsm...
I've got Defend, Seek, Attack and Evade modes working, but the ships end up just circling each other switching rapidly between all states.
Any advice greatly appreciated.
using UnityEngine;
public class SpaceBattle : MonoBehaviour
{
public Transform moon;
public GameObject[] enemyShips;
private Transform target;
public GameObject Bullet_Emitter;
public GameObject Bullet;
public float Bullet_Forward_Force;
public bool chasing = false;
public float distance;
public float nearest = 10000;
public string targeted;
public float rotationSpeed;
public float maxDist;
public float minDist;
public float fireDist;
public string mode="Seek";
//debug
public string TargetName;
public float moveSpeed;
private void Start()
{
enemyShips = GameObject.FindGameObjectsWithTag("EnemyShip");
}
void Update()
{
enemyShips = null;
enemyShips = GameObject.FindGameObjectsWithTag("EnemyShip");
Debug.Log("Enemy Ships Found: " + enemyShips.Length);
if (target != null && target!=moon)
{
//If target is further than maxDist we lost the target
if ((target.transform.position - transform.position).magnitude >= maxDist)
{
target.GetComponentInParent<EnemyShip>().targeted =null;
target = null;
nearest = 10000;
}
// else nearest = (target.transform.position - transform.position).magnitude;
}
target = null;
// Look for a nearby target
nearest = 1000;
for (int i = 0; i < enemyShips.Length; i++)
{
if (enemyShips[i].GetComponent<EnemyShip>().targeted != null)
{
distance = (enemyShips[i].transform.position - transform.position).magnitude;
Debug.Log("Checking " + enemyShips[i].name + "... distance is " + distance.ToString()+" and Max:"+maxDist+" Nearest:"+nearest+" i="+i.ToString());
if (distance < nearest)
{
Debug.Log("Found a nearer target: " + enemyShips[i].name + " " + distance);
nearest = distance;
target = enemyShips[i].transform;
}
}
}
if (target != null) target.GetComponentInParent<EnemyShip>().targeted = this.name;
if (target == null || (moon.transform.position - transform.position).magnitude>1000) target = moon;
TargetName = target.name;
if (target == moon)
{
mode = "Defend";
}
else if (nearest <= minDist)
{
mode = "Evade";
}
else if (nearest>=fireDist && mode=="Evade")
{
mode = "Attack";
}
else if (mode=="Seek" && nearest<=fireDist)
{
mode = "Attack";
}
else if (nearest<=maxDist)
{
mode = "Seek";
}
switch (mode)
{
case "Seek":
{
SeekTarget(transform, target);
MoveToTarget(transform, target);
break;
}
case "Defend":
{
SeekTarget(transform, moon);
MoveToTarget(transform, moon);
break;
}
case "Evade":
{
EvadeTarget(transform, target);
break;
}
case "Attack":
{
SpeedToTarget(transform, target);
FireAtTarget(transform, target);
break;
}
}
}
void EvadeTarget(Transform rTransform, Transform rtarget)
{
rTransform.rotation = Quaternion.Slerp(rTransform.rotation, Quaternion.LookRotation(rTransform.position - rtarget.position), rotationSpeed * Time.deltaTime);
rTransform.position += rTransform.forward * moveSpeed * Time.deltaTime;
}
void SeekTarget(Transform rTransform, Transform rtarget)
{
rTransform.rotation = Quaternion.Slerp(rTransform.rotation, Quaternion.LookRotation(rtarget.position - rTransform.position), rotationSpeed * Time.deltaTime);
Debug.Log("Seeking " + rtarget.name);
}
void MoveToTarget(Transform rTransform, Transform rtarget)
{
rTransform.position += rTransform.forward * moveSpeed * Time.deltaTime;
Debug.Log("Moving towards " + rtarget.name);
}
void SpeedToTarget(Transform rTransform, Transform rtarget)
{
rTransform.rotation = Quaternion.Slerp(rTransform.rotation, Quaternion.LookRotation(predictedPosition(rtarget.position,rTransform.position,rtarget.GetComponentInParent<Rigidbody>().velocity, 30f) - rTransform.position), rotationSpeed * Time.deltaTime);
rTransform.position += rTransform.forward * (moveSpeed*1.5f) * Time.deltaTime;
Debug.Log("Attacking " + rtarget.name);
}
void FireAtTarget(Transform rTransform, Transform rtarget)
{
if (Physics.Raycast(rTransform.position, rTransform.forward, 100) && GameObject.FindGameObjectsWithTag("FriendlyFire").Length < 3)
{
GameObject Temporary_Bullet_Handler;
Temporary_Bullet_Handler = Instantiate(Bullet, Bullet_Emitter.transform.position, Bullet_Emitter.transform.rotation) as GameObject;
Temporary_Bullet_Handler.transform.Rotate(Vector3.left * 90);
Rigidbody Temporary_RigidBody;
Temporary_RigidBody = Temporary_Bullet_Handler.GetComponent<Rigidbody>();
Temporary_RigidBody.AddForce(transform.forward * Bullet_Forward_Force);
Destroy(Temporary_Bullet_Handler, 0.5f);
}
}
private Vector3 predictedPosition(Vector3 targetPosition, Vector3 shooterPosition, Vector3 targetVelocity, float projectileSpeed)
{
Vector3 displacement = targetPosition - shooterPosition;
float targetMoveAngle = Vector3.Angle(-displacement, targetVelocity) * Mathf.Deg2Rad;
//if the target is stopping or if it is impossible for the projectile to catch up with the target (Sine Formula)
if (targetVelocity.magnitude == 0 || targetVelocity.magnitude > projectileSpeed && Mathf.Sin(targetMoveAngle) / projectileSpeed > Mathf.Cos(targetMoveAngle) / targetVelocity.magnitude)
{
Debug.Log("Position prediction is not feasible.");
return targetPosition;
}
//also Sine Formula
float shootAngle = Mathf.Asin(Mathf.Sin(targetMoveAngle) * targetVelocity.magnitude / projectileSpeed);
return targetPosition + targetVelocity * displacement.magnitude / Mathf.Sin(Mathf.PI - targetMoveAngle - shootAngle) * Mathf.Sin(shootAngle) / targetVelocity.magnitude;
}
}
Answer by KevRev · Feb 13, 2019 at 11:13 PM
Figured it out myself. Pretty cool little space a.i. :)
I tried updating this question with the code for everybody to use if they were interested, but the webpage wouldnt let me submit the code. :(
That's not a bad idea. I was thinking of toying with the velocity. Similar to the "arrive" behaviour. I also read an article by a guy that developed the star wars trench run game. He used randomly placed invisible targets around the player and randomly switched to them. This gave the enemies what looked like a fairly natural movement in random directions, bringing them back around for attack. I might try implementing something similar.
Answer by UltraGearGames · Feb 08, 2019 at 12:07 PM
You could set their speed to 0 when at a specific distance from each other. not angular speed though, thats rotation.
Follow this Question
Related Questions
Ally AI shoot arrow 0 Answers
how to use RunCoroutine in Unity 2D Gamekit Behavior Tree 0 Answers
Is there a way to identify the state of the scene obstacles? 0 Answers
Won't fire when switching weapons? 0 Answers