using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// Sci-Fi Ship Controller. Copyright (c) 2018-2023 SCSM Pty Ltd. All rights reserved.
namespace SciFiShipController
{
///
/// Executes a simple state machine for determining which state a "patrol" AI should be in.
/// This is only sample to demonstrate how API calls could be used in your own code.
///
[AddComponentMenu("Sci-Fi Ship Controller/Samples/Patrol AI")]
[HelpURL("http://scsmmedia.com/ssc-documentation")]
[RequireComponent(typeof(ShipAIInputModule))]
public class SamplePatrol : MonoBehaviour
{
#region Public Variables
///
/// The enemy ship this AI ship should patrol for.
///
public ShipControlModule enemyShip;
///
/// The name of the path this AI should patrol.
///
public string pathName = "Path Name Here";
///
/// The maximum distance at which this AI ship can "see" the enemy ship.
///
public float FOVDistance = 250f;
///
/// The maximum angle (in the field of view) at which this AI ship can "see" the enemy ship.
///
public float FOVAngle = 120f;
///
/// The maximum time of losing visual contact with the enemy ship before this AI ship returns to patrolling.
///
public float contactLostTime = 10f;
#endregion
#region Private Variables
private float contactLostTimer = 0f;
private ShipAIInputModule shipAIInputModule;
private SSCManager sscManager;
private PathData patrolPath;
private RaycastHit hitInfo;
private int lastPatrolPathIndex = 0;
#endregion
#region Initialise Methods
void Awake()
{
// Get a reference to the ship AI input module attached to this gameobject
shipAIInputModule = GetComponent();
// Initialise the ship AI (if it hasn't been initialised already)
shipAIInputModule.Initialise();
// Get a reference to the Ship Controller Manager instance
sscManager = SSCManager.GetOrCreateManager();
// Find the Path
patrolPath = sscManager.GetPath(pathName);
#if UNITY_EDITOR
if (patrolPath == null) { Debug.Log("SamplePatrol: Path not found: " + pathName); }
#endif
// Initialise the AI in the "move to" state
shipAIInputModule.SetState(AIState.moveToStateID);
shipAIInputModule.AssignTargetPath(patrolPath);
}
#endregion
#region Update Methods
// Update is called once per frame
void Update()
{
// First check which state the AI is currently in
int currentStateID = shipAIInputModule.GetState();
if (currentStateID == AIState.moveToStateID)
{
// We are currently in the "move to" state - patrolling the given path
// If we see the enemy ship, switch to the dogfight state, and set the enemy ship as the target
if (EnemyShipAlive() && EnemyShipVisible())
{
shipAIInputModule.SetState(AIState.dogfightStateID);
shipAIInputModule.AssignTargetShip(enemyShip);
contactLostTimer = 0f;
// Remember the last patrol path index
lastPatrolPathIndex = shipAIInputModule.GetCurrentTargetPathLocationIndex();
}
}
else if (currentStateID == AIState.dogfightStateID)
{
// We are currently in the "dogfight" state - attacking the enemy ship
// If we lose sight of the enemy, increment the lost contact timer
if (!EnemyShipVisible()) { contactLostTimer += Time.deltaTime; }
else { contactLostTimer = 0f; }
// If the enemy ship is destroyed or we lose sight of it for a given amount of time, switch back to
// the move to state to continue patrolling, and set the patrol path as the target
//if (!EnemyShipAlive() || contactLostTimer > contactLostTime)
if (shipAIInputModule.HasCompletedStateAction() || contactLostTimer > contactLostTime)
{
shipAIInputModule.SetState(AIState.moveToStateID);
shipAIInputModule.AssignTargetPath(patrolPath);
// Set the current target path location index to the index we were at
// when we last stopped following the patrol path
shipAIInputModule.SetCurrentTargetPathLocationIndex(lastPatrolPathIndex, 0f);
}
}
else
{
// We are not in one of the above states, hence we are not in the state we are supposed to be in
// Change to the move to state and continue patrolling
shipAIInputModule.SetState(AIState.moveToStateID);
shipAIInputModule.AssignTargetPath(patrolPath);
}
}
#endregion
#region Public Member Methods
///
/// Returns true if the enemy ship is alive.
///
///
public bool EnemyShipAlive ()
{
return enemyShip == null ? false : !enemyShip.shipInstance.Destroyed();
}
///
/// Returns true if the enemy ship is visible to this ship.
///
///
public bool EnemyShipVisible ()
{
// Check that the enemy ship is within the FOV distance
Vector3 toEnemyShipVector = enemyShip.transform.position - transform.position;
float sqrDistToEnemyShip = toEnemyShipVector.sqrMagnitude;
if (sqrDistToEnemyShip < FOVDistance * FOVDistance)
{
// Check that the enemy ship is within the FOV angle
float distToEnemyShip = Mathf.Sqrt(sqrDistToEnemyShip);
if (Mathf.Acos(Vector3.Dot(transform.forward, toEnemyShipVector) / distToEnemyShip) < FOVAngle * Mathf.Deg2Rad / 2f)
{
// Check that there aren't any obstacles obstructing the ship from view
if (Physics.Linecast(transform.position, enemyShip.transform.position, out hitInfo))
{
return (hitInfo.rigidbody != null && hitInfo.rigidbody.gameObject == enemyShip.gameObject);
}
else { return true; }
}
else { return false; }
}
else { return false; }
}
#endregion
}
}