431 lines
16 KiB
C#
431 lines
16 KiB
C#
|
using UnityEngine;
|
|||
|
|
|||
|
// Sci-Fi Ship Controller. Copyright (c) 2018-2023 SCSM Pty Ltd. All rights reserved.
|
|||
|
namespace SciFiShipController
|
|||
|
{
|
|||
|
[AddComponentMenu("Sci-Fi Ship Controller/Weapon Components/Beam Module")]
|
|||
|
[HelpURL("http://scsmmedia.com/ssc-documentation")]
|
|||
|
public class BeamModule : MonoBehaviour
|
|||
|
{
|
|||
|
#region Public Enumerations
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Public Variables
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The speed the beam travels in metres per second
|
|||
|
/// </summary>
|
|||
|
public float speed = 200f;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The type of damage the beam does. The amount of damage dealt to a ship upon collision is dependent
|
|||
|
/// on the ship's multiplier for this damage type (i.e. if a Type A beam with a damage amount of 10 hits a ship
|
|||
|
/// with a Type A damage multiplier of 2, a total damage of 20 will be done to the ship). If the damage type is set to Default,
|
|||
|
/// the damage multipliers are ignored i.e. the damage amount is unchanged.
|
|||
|
/// </summary>
|
|||
|
public ProjectileModule.DamageType damageType = ProjectileModule.DamageType.Default;
|
|||
|
/// <summary>
|
|||
|
/// The amount of damage this beam does, per second, to the ship or object it hits. NOTE: Non-ship objects need a DamageReceiver component.
|
|||
|
/// </summary>
|
|||
|
public float damageRate = 10f;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Whether pooling is used when spawning beams of this type.
|
|||
|
/// Currently we don't support changing this at runtime.
|
|||
|
/// </summary>
|
|||
|
public bool usePooling = true;
|
|||
|
/// <summary>
|
|||
|
/// The starting size of the pool. Only relevant when usePooling is enabled.
|
|||
|
/// </summary>
|
|||
|
public int minPoolSize = 10;
|
|||
|
/// <summary>
|
|||
|
/// The maximum allowed size of the pool. Only relevant when usePooling is enabled.
|
|||
|
/// </summary>
|
|||
|
public int maxPoolSize = 100;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The start width (in metres) of the beam on the local x-axis
|
|||
|
/// In this version the width will be the same for the entire length of the beam.
|
|||
|
/// </summary>
|
|||
|
[Range(0.001f, 5f)] public float beamStartWidth = 0.1f;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The minimum amount of time, in seconds, the beam must be active
|
|||
|
/// </summary>
|
|||
|
[Range(0.1f, 5f)] public float minBurstDuration = 0.5f;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The maximum amount of time, in seconds, the beam can be active in a single burst
|
|||
|
/// </summary>
|
|||
|
[Range(0.1f, 30f)] public float maxBurstDuration = 5f;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The time (in seconds) it takes a single beam to discharge the beam weapon from full charge
|
|||
|
/// </summary>
|
|||
|
[Range(0.1f, 60f)] public float dischargeDuration = 10f;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The ID number for this beam prefab (as assigned by the Ship Controller Manager in the scene).
|
|||
|
/// This is the index in the SSCManager beamTemplatesList.
|
|||
|
/// [INTERNAL USE ONLY]
|
|||
|
/// </summary>
|
|||
|
public int beamPrefabID;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The sound or particle FX used when a collision occurs.
|
|||
|
/// If you modify this, call shipControlModule.ReinitialiseShipBeams() for each ship
|
|||
|
/// this beam is used on.
|
|||
|
/// </summary>
|
|||
|
public EffectsModule effectsObject = null;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The ID number for this beam's effects object prefab (as assigned by the Ship Controller Manager in the scene).
|
|||
|
/// This is the index in the SSCManager effectsObjectTemplatesList. Not defined = -1.
|
|||
|
/// [INTERNAL USE ONLY]
|
|||
|
/// </summary>
|
|||
|
public int effectsObjectPrefabID = -1;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The sound and/or particle FX used when a beam is fired from a weapon.
|
|||
|
/// If you modify this, call shipControlModule.ReinitialiseShipBeams() for each ship
|
|||
|
/// this beam is used on.
|
|||
|
/// </summary>
|
|||
|
public EffectsModule muzzleEffectsObject = null;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The distance in local space that the muzzle Effects Object should be instantiated
|
|||
|
/// from the weapon firing point. Typically only the z-axis would be used when the beam
|
|||
|
/// is instantiated in front or forwards from the actual weapon.
|
|||
|
/// </summary>
|
|||
|
public Vector3 muzzleEffectsOffset = Vector3.zero;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The ID number for this beam's muzzle object prefab (as assigned by the Ship Controller Manager in the scene).
|
|||
|
/// This is the index in the SSCManager effectsObjectTemplatesList. Not defined = -1.
|
|||
|
/// [INTERNAL USE ONLY]
|
|||
|
/// </summary>
|
|||
|
[System.NonSerialized] public int muzzleEffectsObjectPrefabID = -1;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The Id of the ship that fired the beam
|
|||
|
/// </summary>
|
|||
|
[System.NonSerialized] public int sourceShipId = -1;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The Squadron which the ship belonged to when it fired the beam
|
|||
|
/// </summary>
|
|||
|
[System.NonSerialized] public int sourceSquadronId = -1;
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Private and Internal Variables
|
|||
|
[System.NonSerialized] internal bool isInitialised = false;
|
|||
|
|
|||
|
[System.NonSerialized] internal bool isBeamEnabled = false;
|
|||
|
|
|||
|
[System.NonSerialized] internal float burstDuration = 0f;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// [INTERNAL ONLY]
|
|||
|
/// Reference to the LineRenderer component which should be on a child
|
|||
|
/// gameobject of this module.
|
|||
|
/// </summary>
|
|||
|
[System.NonSerialized] internal LineRenderer lineRenderer = null;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// [INTERNAL ONLY]
|
|||
|
/// Used to determine uniqueness
|
|||
|
/// </summary>
|
|||
|
[System.NonSerialized] internal uint itemSequenceNumber;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The zero-based index of the weapon on the ship that fired the beam
|
|||
|
/// </summary>
|
|||
|
[System.NonSerialized] internal int weaponIndex;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The zero-based index of the fire position on the weapon that fired the beam
|
|||
|
/// </summary>
|
|||
|
[System.NonSerialized] internal int firePositionIndex;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The item key of the muzzle effects object (if any) firing from this beam
|
|||
|
/// </summary>
|
|||
|
[System.NonSerialized] internal SSCEffectItemKey muzzleEffectsItemKey;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The item key of the effects object that is spawned when the beam hits something
|
|||
|
/// </summary>
|
|||
|
[System.NonSerialized] internal SSCEffectItemKey effectsItemKey;
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Internal Delegates
|
|||
|
internal delegate void CallbackOnMove(BeamModule beamModule);
|
|||
|
|
|||
|
[System.NonSerialized] internal CallbackOnMove callbackOnMove = null;
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Internal Static Variables
|
|||
|
internal static uint nextSequenceNumber = 1;
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Enable/Disable Event Methods
|
|||
|
|
|||
|
void OnDisable()
|
|||
|
{
|
|||
|
isInitialised = false;
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Update Methods
|
|||
|
|
|||
|
// FixedUpdate is called once per physics update (typically about 50 times per second)
|
|||
|
private void FixedUpdate()
|
|||
|
{
|
|||
|
if (isInitialised && isBeamEnabled)
|
|||
|
{
|
|||
|
burstDuration += Time.deltaTime;
|
|||
|
|
|||
|
if (callbackOnMove != null) { callbackOnMove(this); }
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Private and Internal Methods
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Intialise the beam and return it's unique sequence number
|
|||
|
/// </summary>
|
|||
|
/// <param name="ibParms"></param>
|
|||
|
internal uint InitialiseBeam(InstantiateBeamParameters ibParms)
|
|||
|
{
|
|||
|
// Store the index to the BeamTemplate in the SSCManager beamTemplatesList
|
|||
|
// This is used with Beam FX when we know the BeamModule but not the parent BeamTemplate.
|
|||
|
this.beamPrefabID = ibParms.beamPrefabID;
|
|||
|
|
|||
|
// Store the index to the EffectsObjectTemplate in sscManager.effectsObjectTemplatesList
|
|||
|
this.effectsObjectPrefabID = ibParms.effectsObjectPrefabID;
|
|||
|
|
|||
|
// Store the details on what fired the beam
|
|||
|
this.sourceShipId = ibParms.shipId;
|
|||
|
this.sourceSquadronId = ibParms.squadronId;
|
|||
|
this.weaponIndex = ibParms.weaponIndex;
|
|||
|
this.firePositionIndex = ibParms.firePositionIndex;
|
|||
|
|
|||
|
IncrementSequenceNumber();
|
|||
|
|
|||
|
muzzleEffectsItemKey = new SSCEffectItemKey(-1, -1, 0);
|
|||
|
effectsItemKey = new SSCEffectItemKey(-1, -1, 0);
|
|||
|
|
|||
|
if (lineRenderer == null) { lineRenderer = GetComponentInChildren<LineRenderer>(); }
|
|||
|
|
|||
|
if (lineRenderer != null)
|
|||
|
{
|
|||
|
lineRenderer.startWidth = beamStartWidth;
|
|||
|
lineRenderer.endWidth = beamStartWidth;
|
|||
|
lineRenderer.alignment = LineAlignment.View;
|
|||
|
lineRenderer.startColor = Color.white;
|
|||
|
lineRenderer.endColor = Color.white;
|
|||
|
|
|||
|
// Our calculations in ship.MoveBeam(..) assume world-space positions
|
|||
|
lineRenderer.useWorldSpace = true;
|
|||
|
|
|||
|
// After a given amount of time, automatically destroy this beam
|
|||
|
burstDuration = 0f;
|
|||
|
|
|||
|
isBeamEnabled = gameObject.activeSelf;
|
|||
|
|
|||
|
isInitialised = true;
|
|||
|
}
|
|||
|
|
|||
|
return itemSequenceNumber;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Makes this BeamModule unique from all others that have gone before them.
|
|||
|
/// This is called every time beam is Initialised.
|
|||
|
/// </summary>
|
|||
|
internal void IncrementSequenceNumber()
|
|||
|
{
|
|||
|
itemSequenceNumber = nextSequenceNumber++;
|
|||
|
// if sequence number needs to be wrapped, do so to a high-ish number that is unlikely to be in use
|
|||
|
if (nextSequenceNumber > uint.MaxValue - 100) { nextSequenceNumber = 100000; }
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// [INTERNAL ONLY]
|
|||
|
/// Removes the beam. How this is done depends on what system is being used (i.e. pooling etc.).
|
|||
|
/// </summary>
|
|||
|
internal void DestroyBeam()
|
|||
|
{
|
|||
|
// Reset
|
|||
|
weaponIndex = -1;
|
|||
|
firePositionIndex = -1;
|
|||
|
isBeamEnabled = false;
|
|||
|
// Remove the reference
|
|||
|
callbackOnMove = null;
|
|||
|
|
|||
|
SSCManager sscManager = null;
|
|||
|
|
|||
|
// Stop muzzle fx
|
|||
|
if (muzzleEffectsItemKey.effectsObjectSequenceNumber > 0)
|
|||
|
{
|
|||
|
sscManager = SSCManager.GetOrCreateManager();
|
|||
|
|
|||
|
if (sscManager != null)
|
|||
|
{
|
|||
|
sscManager.DestroyEffectsObject(muzzleEffectsItemKey);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Stop hit fx
|
|||
|
if (effectsItemKey.effectsObjectSequenceNumber > 0)
|
|||
|
{
|
|||
|
if (sscManager == null) { sscManager = SSCManager.GetOrCreateManager(); }
|
|||
|
|
|||
|
if (sscManager != null)
|
|||
|
{
|
|||
|
sscManager.DestroyEffectsObject(effectsItemKey);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (usePooling)
|
|||
|
{
|
|||
|
// Deactivate the beam
|
|||
|
gameObject.SetActive(false);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// Destroy the beam
|
|||
|
Destroy(gameObject);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Public API Static Methods
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Determine if a ship has been hit. Apply damage as required based on the damage rate, damage type
|
|||
|
/// and hit duration.
|
|||
|
/// Hit duration is the time in seconds that the beam has been in contact with the target object since
|
|||
|
/// it was last checked (typically the duration of the last frame or time step).
|
|||
|
/// </summary>
|
|||
|
/// <param name="raycastHitInfo"></param>
|
|||
|
/// <param name="beamModule">Must not be null</param>
|
|||
|
/// <param name="hitDuration"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public static bool CheckShipHit (RaycastHit raycastHitInfo, BeamModule beamModule, float hitDuration)
|
|||
|
{
|
|||
|
bool isHit = false;
|
|||
|
Rigidbody hitRigidbody = raycastHitInfo.rigidbody;
|
|||
|
|
|||
|
if (hitRigidbody != null && beamModule != null)
|
|||
|
{
|
|||
|
// Check if there is a ship control module attached to the hit object
|
|||
|
ShipControlModule shipControlModule = hitRigidbody.GetComponent<ShipControlModule>();
|
|||
|
|
|||
|
if (shipControlModule != null)
|
|||
|
{
|
|||
|
float damageAmount = beamModule.damageRate * hitDuration;
|
|||
|
|
|||
|
// Apply damage to the ship
|
|||
|
shipControlModule.shipInstance.ApplyNormalDamage(damageAmount, beamModule.damageType, raycastHitInfo.point);
|
|||
|
|
|||
|
// If required, call the custom method
|
|||
|
if (shipControlModule.callbackOnHit != null)
|
|||
|
{
|
|||
|
// Get a reference to the SSCManager
|
|||
|
SSCManager sscManager = SSCManager.GetOrCreateManager();
|
|||
|
|
|||
|
// Create a struct with the necessary parameters
|
|||
|
CallbackOnShipHitParameters callbackOnShipHitParameters = new CallbackOnShipHitParameters
|
|||
|
{
|
|||
|
hitInfo = raycastHitInfo,
|
|||
|
projectilePrefab = null,
|
|||
|
beamPrefab = sscManager.GetBeamPrefab(beamModule.beamPrefabID),
|
|||
|
damageAmount = damageAmount,
|
|||
|
sourceSquadronId = beamModule.sourceSquadronId,
|
|||
|
sourceShipId = beamModule.sourceShipId
|
|||
|
};
|
|||
|
// Call the custom callback
|
|||
|
shipControlModule.callbackOnHit(callbackOnShipHitParameters);
|
|||
|
}
|
|||
|
|
|||
|
isHit = true;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return isHit;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Determine if an object with a DamageReceiver component attached has been hit by a Beam.
|
|||
|
/// </summary>
|
|||
|
/// <param name="raycastHitInfo"></param>
|
|||
|
/// <param name="beamModule">Must not be null</param>
|
|||
|
/// <returns></returns>
|
|||
|
public static bool CheckObjectHit (RaycastHit raycastHitInfo, BeamModule beamModule, float hitDuration)
|
|||
|
{
|
|||
|
bool isHit = false;
|
|||
|
Rigidbody hitRigidbody = raycastHitInfo.rigidbody;
|
|||
|
|
|||
|
if (raycastHitInfo.collider != null)
|
|||
|
{
|
|||
|
DamageReceiver damageReceiver = raycastHitInfo.collider.GetComponent<DamageReceiver>();
|
|||
|
if (damageReceiver != null && damageReceiver.callbackOnHit != null)
|
|||
|
{
|
|||
|
// Get a reference to the SSCManager
|
|||
|
SSCManager sscManager = SSCManager.GetOrCreateManager();
|
|||
|
|
|||
|
// Create a struct with the necessary parameters
|
|||
|
CallbackOnObjectHitParameters callbackOnObjectHitParameters = new CallbackOnObjectHitParameters
|
|||
|
{
|
|||
|
hitInfo = raycastHitInfo,
|
|||
|
projectilePrefab = null,
|
|||
|
beamPrefab = sscManager.GetBeamPrefab(beamModule.beamPrefabID),
|
|||
|
damageAmount = beamModule.damageRate * hitDuration,
|
|||
|
sourceSquadronId = beamModule.sourceSquadronId
|
|||
|
};
|
|||
|
// Call the custom callback
|
|||
|
damageReceiver.callbackOnHit(callbackOnObjectHitParameters);
|
|||
|
|
|||
|
isHit = true;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return isHit;
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Public API Methods
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Re-enable a beam after it has been disabled with
|
|||
|
/// DisableBeam(). See also SSCManager.ResumeBeams()
|
|||
|
/// </summary>
|
|||
|
public void EnableBeam()
|
|||
|
{
|
|||
|
isBeamEnabled = true;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Useful when you want to pause the action in a game.
|
|||
|
/// Should always be called BEFORE setting Time.timeScale to 0.
|
|||
|
/// See also SSCManager.PauseBeams().
|
|||
|
/// </summary>
|
|||
|
public void DisableBeam()
|
|||
|
{
|
|||
|
isBeamEnabled = false;
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
}
|
|||
|
}
|