312 lines
14 KiB
C#
312 lines
14 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
// Sci-Fi Ship Controller. Copyright (c) 2018-2023 SCSM Pty Ltd. All rights reserved.
|
|
namespace SciFiShipController
|
|
{
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
[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<SSCRadarBlip> 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<ShipControlModule>();
|
|
|
|
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<Renderer>().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>();
|
|
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<SSCRadarBlip>(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
|
|
|
|
/// <summary>
|
|
/// Unassign any remaining Auto Targeting weapons that could not be allocated a target.
|
|
/// </summary>
|
|
/// <param name="nextWeaponIndex"></param>
|
|
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
|
|
|
|
/// <summary>
|
|
/// This routine is called by Sci-Fi Ship Controller when a projectile or beam hits our enemy
|
|
/// </summary>
|
|
/// <param name="callbackOnObjectHitParameters"></param>
|
|
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
|
|
}
|
|
}
|