using System.Collections.Generic;
using UnityEngine;
// Sci-Fi Ship Controller. Copyright (c) 2018-2023 SCSM Pty Ltd. All rights reserved.
namespace SciFiShipController
{
///
/// This sample shows how you can:
/// 1. Run radar queries for a player ship to find nearby enemy
/// 2. Use DisplayTargets on a HUD to track enemy on the screen
/// 3. Assign targets to weapons that use Guided Projectiles
/// 4. Get notified when an enemy is hit
/// This is only sample to demonstrate how API calls could be used in
/// your own code. You should write your own version of this in your own
/// namespace.
/// SETUP:
/// 1. Add Prefabs\Visuals\HUD1 prefab to the scene
/// 2. Add player ship from Prefabs\Ships to the scene (ones that don't have NPC in the name)
/// 3. Remove or disable the default Main Camera
/// 4. Add Prefabs\Environment\PlayerCamera prefab to the scene
/// 5. Hook up the player ship to the PlayerCamera
/// 6. Add this script to the player ship
/// 7. Hook up the HUD to the slot on this script
/// 8. Make sure you player ship has at least one weapon with a guided Projectile
/// 9. Ensure the weapon has Auto Targeting enabled.
/// NOTE: Most of the time you can simply add the AutoTargetingModule
/// to your player ship.
///
[AddComponentMenu("Sci-Fi Ship Controller/Samples/HUD Targets")]
[HelpURL("http://scsmmedia.com/ssc-documentation")]
public class SampleHUDTargets : MonoBehaviour
{
#region Public Variables and Properties
public ShipDisplayModule shipDisplayModule;
#endregion
#region Private Variables
private ShipControlModule playerShip = null;
private bool isInitialised = false;
private SSCRadar sscRadar = null;
private SSCRadarQuery radarQuery = null;
private List sscRadarBlipsList = null;
private int numDisplayTargets = 0;
#endregion
#region Initialisation Methods
// Start is called before the first frame update
void Start()
{
#region Initialise Player Ship
playerShip = GetComponent();
if (playerShip != null)
{
// If Initialise On Awake is not ticking on the ship, initialise the ship now.
if (!playerShip.IsInitialised) { playerShip.InitialiseShip(); }
}
#if UNITY_EDITOR
else
{
Debug.LogWarning("[ERROR] SampleHUDTargets.Start() could not find the ship! Did you attach this script to you Player ship (ShipControlModule)?");
}
#endif
#endregion
#region Initialise HUD
if (shipDisplayModule != null)
{
// If Initialise on Start is not ticked on the HUD, do it now.
if (!shipDisplayModule.IsInitialised) { shipDisplayModule.Initialise(); }
if (shipDisplayModule.IsInitialised)
{
// Typically you can let the HUD update DisplayTarget positions on the screen
// automatically. However, in this sample we want to do things ourselves.
shipDisplayModule.autoUpdateTargetPositions = false;
numDisplayTargets = shipDisplayModule.GetNumberDisplayTargets;
// If no Display Targets are pre-configured, let's add some
// Typically, you will configure them in the HUD before runtime.
if (numDisplayTargets < 1)
{
int numAvailableReticles = shipDisplayModule.GetNumberDisplayReticles;
int numAdded = 0;
// Use the last 3 available reticles as Display Target reticles
for (int rIdx = numAvailableReticles - 1; rIdx >= 0; rIdx--)
{
int guidHash = shipDisplayModule.GetDisplayReticleGuidHash(rIdx);
shipDisplayModule.AddTarget(guidHash);
if (++numAdded == 3) { break; }
}
numDisplayTargets = shipDisplayModule.GetNumberDisplayTargets;
}
}
}
#if UNITY_EDITOR
else
{
Debug.LogWarning("[ERROR] SampleHUDTargets.Start() the ShipDisplayModule is not set. Add the Prefabs/Visuals/HUD1 prefab to the scene, then drag it on to this script which should be attached to your player ship.");
}
#endif
#endregion
#region Create some "enemy" and added them to radar
sscRadar = SSCRadar.GetOrCreateRadar();
if (sscRadar != null && playerShip != null && playerShip.IsInitialised)
{
// Create a position 100m in front of the ship
Vector3 locationWS = playerShip.shipInstance.TransformPosition + (playerShip.shipInstance.TransformForward * 100f);
// Create some "enemy"
for (int eIdx = 0; eIdx < 4; eIdx++)
{
GameObject enemyGO = GameObject.CreatePrimitive(PrimitiveType.Sphere);
if (enemyGO != null)
{
enemyGO.name = "Enemy " + (eIdx + 1).ToString();
enemyGO.transform.position = locationWS + (playerShip.shipInstance.TransformRight * 25 * (eIdx+1));
enemyGO.transform.localScale = Vector3.one * 3f;
enemyGO.GetComponent().material.color = new Color(1f, 1f / (eIdx + 2), 1f / (eIdx + 2));
// Let's get notified when our "enemy" is hit
DamageReceiver damageReceiver = enemyGO.AddComponent();
damageReceiver.callbackOnHit = TakeDamage;
// Tell the radar system about this enemy gameobject
sscRadar.EnableRadar(enemyGO, enemyGO.transform.position, playerShip.shipInstance.factionId + 1, -1, 0, 5);
}
}
// Create a radar query with some default settings
radarQuery = new SSCRadarQuery()
{
range = 1000,
querySortOrder = SSCRadarQuery.QuerySortOrder.DistanceAsc3D,
factionsToExclude = new int[] { playerShip.shipInstance.factionId }
};
// Create an empty list to store the radar results
sscRadarBlipsList = new List(10);
isInitialised = shipDisplayModule != null && sscRadarBlipsList != null && radarQuery != null;
}
#endregion
}
#endregion
#region Update Methods
// Update is called once per frame
void Update()
{
if (isInitialised)
{
// Test code to allow you to move it around in the scene view at runtime
//GameObject enGO = GameObject.Find("Enemy 1");
//if (enGO != null)
//{
// sscRadar.SetItemPosition(sscRadar.GetRadarItemIndexByHash(enGO.GetHashCode()), enGO.transform.position);
//}
sscRadar.GetRadarResults(radarQuery, sscRadarBlipsList);
int numEnemy = sscRadarBlipsList.Count;
Vector2 screenResolution = shipDisplayModule.ScreenResolution;
int nextBlipIndex = 0;
int nextWeaponIndex = 0;
for (int dtIdx = 0; dtIdx < numDisplayTargets; dtIdx++)
{
DisplayTarget displayTarget = shipDisplayModule.GetDisplayTargetByIndex(dtIdx);
// Are there any more enemies that could be in view?
if (dtIdx < numEnemy && nextBlipIndex >= 0)
{
// Find the next enemy in view
nextBlipIndex = sscRadar.GetNextBlipInView(sscRadarBlipsList, nextBlipIndex, shipDisplayModule.mainCamera, screenResolution);
// Is the enemy in view?
if (nextBlipIndex >= 0)
{
SSCRadarBlip blip = sscRadarBlipsList[nextBlipIndex];
// Move the DisplayTarget slot to the correct position on the screen
// For this sample we're just using 1 DisplayTarget slot (or 1 copy of each DT in the DT list in the HUD)
// We are ignoring the Max Number of Targets setting for each DisplayTarget.
shipDisplayModule.SetDisplayTargetPosition(displayTarget, 0, blip.wsPosition);
// Find a weapon to assign to the target
if (nextWeaponIndex >= 0)
{
nextWeaponIndex = playerShip.shipInstance.GetNextAutoTargetingWeaponIndex(nextWeaponIndex);
// Found an available weapon with a guided projectile
if (nextWeaponIndex >= 0)
{
// This just one possible way you could do things. You could also investigate using other methods like
// shipDisplayModule.AssignDisplayTargetSlot(..) like is used in the AutoTargetingModule.
if (sscRadar.IsShipBlip(blip))
{
playerShip.shipInstance.SetWeaponTargetShip(nextWeaponIndex, blip.shipControlModule);
}
else if (sscRadar.IsGameObjectBlip(blip))
{
playerShip.shipInstance.SetWeaponTarget(nextWeaponIndex, blip.itemGameObject);
}
else if (nextBlipIndex >= numEnemy - 1)
{
// No more Ship or GameObject enemy so unassign remaining auto targeting weapons
UnassignWeapons(ref nextWeaponIndex);
// Go to the next DisplayTarget which will be turned off
continue;
}
else
{
// find another enemy target
nextBlipIndex++;
continue;
}
// Advance to the next weapon
nextWeaponIndex++;
}
}
// If the DisplayTarget is not already visible, show it
if (!displayTarget.showTarget) { shipDisplayModule.ShowDisplayTarget(displayTarget); }
// Advance to the next enemy target
nextBlipIndex++;
}
else
{
// If there are no more enemy, the remaining weapons (if any) should not have targets.
UnassignWeapons(ref nextWeaponIndex);
// If the DisplayTarget is visible, turn it off
if (displayTarget.showTarget)
{
shipDisplayModule.HideDisplayTarget(displayTarget);
}
}
}
else
{
// If there are no more enemy, the remaining weapons (if any) should not have targets.
UnassignWeapons(ref nextWeaponIndex);
// If the DisplayTarget is visible, turn it off
if (displayTarget.showTarget)
{
shipDisplayModule.HideDisplayTarget(displayTarget);
}
}
}
}
}
#endregion
#region Private Methods
///
/// Unassign any remaining Auto Targeting weapons that could not be allocated a target.
///
///
private void UnassignWeapons(ref int nextWeaponIndex)
{
int numWeapons = playerShip.NumberOfWeapons;
while (nextWeaponIndex >= 0 && nextWeaponIndex < numWeapons)
{
playerShip.shipInstance.ClearWeaponTarget(nextWeaponIndex);
nextWeaponIndex++;
nextWeaponIndex = playerShip.shipInstance.GetNextAutoTargetingWeaponIndex(nextWeaponIndex);
}
}
#endregion
#region Public Member Methods
///
/// This routine is called by Sci-Fi Ship Controller when a projectile or beam hits our enemy
///
///
public void TakeDamage(CallbackOnObjectHitParameters callbackOnObjectHitParameters)
{
// Rather than taking damage, immediately our "enemy" is hit, we will destroy it.
if (callbackOnObjectHitParameters.hitInfo.transform != null)
{
// Get the radar item by using the hashcode of the GameObject
// Then tell the radar system to stop tracking this item.
sscRadar.RemoveItem(sscRadar.GetRadarItemIndexByHash(callbackOnObjectHitParameters.hitInfo.transform.gameObject.GetHashCode()));
// Now we can destroy the actual gameobject
Destroy(callbackOnObjectHitParameters.hitInfo.transform.gameObject);
}
}
#endregion
}
}