341 lines
14 KiB
C#
341 lines
14 KiB
C#
|
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
|
|||
|
{
|
|||
|
public class ThrusterEffects : MonoBehaviour
|
|||
|
{
|
|||
|
#region Public variables
|
|||
|
/// <summary>
|
|||
|
/// The amount of input required before the effect will have any effect.
|
|||
|
/// This can be useful with controllers which can produce small values
|
|||
|
/// when the user is not moving them (simulates a dead-zone).
|
|||
|
/// It can also avoid thruster effects "firing" for very small inputs.
|
|||
|
/// </summary>
|
|||
|
[Range(0f, 0.1f)] public static float inputThreshold = 0.005f;
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Private variables - General
|
|||
|
private bool initialised = false;
|
|||
|
private int index = 0;
|
|||
|
private float currentThrusterInput = 0f;
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Private shuriken particle variables
|
|||
|
// Arrays of shuriken particle systems and their initial rate over time multipliers
|
|||
|
private ParticleSystem[] shurikenParticles;
|
|||
|
private float[] shurikenParticleInitialRateMultipliers;
|
|||
|
private float[] shurikenParticleInitialSpeedMultipliers;
|
|||
|
|
|||
|
private int numShurikenParticles = 0;
|
|||
|
|
|||
|
private ParticleSystem.EmissionModule shurikenEmissionModule;
|
|||
|
private ParticleSystem.MainModule shurikenMainModule;
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Private AudioSource variables
|
|||
|
// Audio sources for clips
|
|||
|
private AudioSource[] audioSources;
|
|||
|
private int numAudioSources = 0;
|
|||
|
private AudioSource audioSource;
|
|||
|
private float maxVolume = 1f;
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Private or Internal Methods
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// [INTERNAL ONLY]
|
|||
|
/// Clear thruster effects data
|
|||
|
/// </summary>
|
|||
|
internal void Clear()
|
|||
|
{
|
|||
|
shurikenParticles = null;
|
|||
|
shurikenParticleInitialRateMultipliers = null;
|
|||
|
shurikenParticleInitialSpeedMultipliers = null;
|
|||
|
audioSources = null;
|
|||
|
numAudioSources = 0;
|
|||
|
numShurikenParticles = 0;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// If initialised restore the ParticleSystems to their original values. This is typically
|
|||
|
/// used when we want to reinitialise the system.
|
|||
|
/// </summary>
|
|||
|
protected virtual void RestoreInitialValues()
|
|||
|
{
|
|||
|
if (initialised)
|
|||
|
{
|
|||
|
// Restore Particle Systems to original values
|
|||
|
numShurikenParticles = shurikenParticles == null ? 0 : shurikenParticles.Length;
|
|||
|
|
|||
|
for (index = 0; index < numShurikenParticles; index++)
|
|||
|
{
|
|||
|
if (shurikenParticles[index] != null)
|
|||
|
{
|
|||
|
shurikenEmissionModule = shurikenParticles[index].emission;
|
|||
|
shurikenMainModule = shurikenParticles[index].main;
|
|||
|
|
|||
|
shurikenEmissionModule.rateOverTimeMultiplier = shurikenParticleInitialRateMultipliers[index];
|
|||
|
shurikenMainModule.startSpeedMultiplier = shurikenParticleInitialSpeedMultipliers[index];
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Restore Audio Sources to original volumes
|
|||
|
numAudioSources = audioSources == null ? 0 : audioSources.Length;
|
|||
|
|
|||
|
for (index = 0; index < numAudioSources; index++)
|
|||
|
{
|
|||
|
audioSources[index].volume = maxVolume;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Public Virtual Member Methods
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Uses the given thruster input value to update all thruster effects systems i.e. particle systems.
|
|||
|
/// minEffectsRate must be between 0.0 and 1.0. This is the minimum particle system emission rate
|
|||
|
/// proportional to the maximum emission rate.
|
|||
|
/// Assumes thruster is not null.
|
|||
|
/// </summary>
|
|||
|
/// <param name="thruster"></param>
|
|||
|
/// <param name="shipLocalVelocity"></param>
|
|||
|
public virtual void UpdateThrusterInput (Thruster thruster, Vector3 shipLocalVelocity)
|
|||
|
{
|
|||
|
if (initialised)
|
|||
|
{
|
|||
|
// We only want to update the thruster effect systems when we receive input from this function
|
|||
|
// This way we can update this less frequently etc. to improve performance
|
|||
|
|
|||
|
// Check for maxThrust > 0. If no thrust, then no FX or audio.
|
|||
|
float thrusterInput = thruster.maxThrust * thruster.throttle > 0f ? thruster.currentInput * thruster.CurrentPerformance : 0f;
|
|||
|
bool isFXLimited = false;
|
|||
|
|
|||
|
// Check for inputThreshold
|
|||
|
currentThrusterInput = thrusterInput < inputThreshold ? 0f : thrusterInput;
|
|||
|
|
|||
|
// Check if ship is travelling too fast or two slow on Z-axis for the thruster to be active
|
|||
|
if (currentThrusterInput > 0f && thruster.limitEffectsOnZ)
|
|||
|
{
|
|||
|
if (shipLocalVelocity.z < thruster.minEffectsOnZ || shipLocalVelocity.z > thruster.maxEffectsOnZ)
|
|||
|
{
|
|||
|
currentThrusterInput = 0f;
|
|||
|
isFXLimited = true;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Check if ship is travelling too fast or two slow on Y-axis for the thruster to be active
|
|||
|
if (currentThrusterInput > 0f && thruster.limitEffectsOnY)
|
|||
|
{
|
|||
|
if (shipLocalVelocity.y < thruster.minEffectsOnY || shipLocalVelocity.y > thruster.maxEffectsOnY)
|
|||
|
{
|
|||
|
currentThrusterInput = 0f;
|
|||
|
isFXLimited = true;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (currentThrusterInput > 0f || (!isFXLimited && thruster.isMinEffectsAlwaysOn))
|
|||
|
{
|
|||
|
// If user wants a minimum particle emission or audio volume, update accordingly.
|
|||
|
currentThrusterInput = currentThrusterInput < thruster.minEffectsRate ? thruster.minEffectsRate * (thruster.isThrottleMinEffects ? thruster.throttle : 1f) : currentThrusterInput;
|
|||
|
// Clamp to 1.0
|
|||
|
if (currentThrusterInput > 1f) { currentThrusterInput = 1f; }
|
|||
|
}
|
|||
|
|
|||
|
// Update shuriken particle systems
|
|||
|
numShurikenParticles = shurikenParticles == null ? 0 : shurikenParticles.Length;
|
|||
|
for (index = 0; index < numShurikenParticles; index++)
|
|||
|
{
|
|||
|
ParticleSystem pSystem = shurikenParticles[index];
|
|||
|
|
|||
|
shurikenEmissionModule = pSystem.emission;
|
|||
|
shurikenMainModule = pSystem.main;
|
|||
|
|
|||
|
if (currentThrusterInput > 0f)
|
|||
|
{
|
|||
|
// +SMS v1.3.6 Beta 1j. Used with isMinEffectsAlwaysOn and ship.isThrusterFXStationary
|
|||
|
if (thruster.isMinEffectsAlwaysOn && pSystem.isPaused) { pSystem.Play(true); }
|
|||
|
|
|||
|
shurikenEmissionModule.rateOverTimeMultiplier = shurikenParticleInitialRateMultipliers[index] * currentThrusterInput;
|
|||
|
shurikenMainModule.startSpeedMultiplier = shurikenParticleInitialSpeedMultipliers[index] * currentThrusterInput;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
shurikenEmissionModule.rateOverTimeMultiplier = 0f;
|
|||
|
shurikenMainModule.startSpeedMultiplier = 0f;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Update Audio Sources
|
|||
|
// numAudioSources is set in Initialise()
|
|||
|
for (index = 0; index < numAudioSources; index++)
|
|||
|
{
|
|||
|
audioSource = audioSources[index];
|
|||
|
|
|||
|
if (currentThrusterInput > 0f)
|
|||
|
{
|
|||
|
// +SMS v1.3.6 Beta 1j. Used with isMinEffectsAlwaysOn and ship.isThrusterFXStationary
|
|||
|
if (thruster.isMinEffectsAlwaysOn && audioSource.mute) { audioSource.mute = false; }
|
|||
|
|
|||
|
audioSource.volume = currentThrusterInput * maxVolume;
|
|||
|
if (!audioSource.isPlaying && audioSource.isActiveAndEnabled) { audioSource.Play(); }
|
|||
|
}
|
|||
|
else { audioSource.volume = 0f; }
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Pauses the thruster effects.
|
|||
|
/// </summary>
|
|||
|
public virtual void Pause()
|
|||
|
{
|
|||
|
// Update shuriken particle systems
|
|||
|
numShurikenParticles = shurikenParticles == null ? 0 : shurikenParticles.Length;
|
|||
|
for (index = 0; index < numShurikenParticles; index++)
|
|||
|
{
|
|||
|
shurikenParticles[index].Pause();
|
|||
|
}
|
|||
|
|
|||
|
Mute(true);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Stop thruster effects and set sound volume to 0.
|
|||
|
/// </summary>
|
|||
|
public virtual void Stop()
|
|||
|
{
|
|||
|
// Update shuriken particle systems
|
|||
|
numShurikenParticles = shurikenParticles == null ? 0 : shurikenParticles.Length;
|
|||
|
for (index = 0; index < numShurikenParticles; index++)
|
|||
|
{
|
|||
|
// Cannot just do Stop() as previous state could have been Pause()
|
|||
|
shurikenParticles[index].Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear);
|
|||
|
}
|
|||
|
|
|||
|
// Turn off volume.
|
|||
|
// numAudioSources is set in Initialise()
|
|||
|
for (index = 0; index < numAudioSources; index++)
|
|||
|
{
|
|||
|
audioSource = audioSources[index];
|
|||
|
audioSource.volume = 0f;
|
|||
|
if (audioSource.isActiveAndEnabled && audioSource.isPlaying) { audioSource.Stop(); }
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Plays the thruster effects (resuming them if they were previously paused).
|
|||
|
/// </summary>
|
|||
|
public virtual void Play()
|
|||
|
{
|
|||
|
// Update shuriken particle systems
|
|||
|
numShurikenParticles = shurikenParticles == null ? 0 : shurikenParticles.Length;
|
|||
|
for (index = 0; index < numShurikenParticles; index++)
|
|||
|
{
|
|||
|
shurikenParticles[index].Play();
|
|||
|
}
|
|||
|
|
|||
|
UnMute(true);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Set the max volume of the thruster effects audio.
|
|||
|
/// </summary>
|
|||
|
/// <param name="newMaxVolume"></param>
|
|||
|
public virtual void SetMaxVolume(float newMaxVolume)
|
|||
|
{
|
|||
|
maxVolume = newMaxVolume < 0f ? 0f : newMaxVolume > 1f ? 1f : newMaxVolume;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Mutes the sound on all active audio sources on the thruster. This is automatically
|
|||
|
/// called in Pause().
|
|||
|
/// </summary>
|
|||
|
public virtual void Mute(bool isPauseSound)
|
|||
|
{
|
|||
|
// numAudioSources is set in Initialise()
|
|||
|
for (index = 0; index < numAudioSources; index++)
|
|||
|
{
|
|||
|
audioSource = audioSources[index];
|
|||
|
audioSource.mute = true;
|
|||
|
if (isPauseSound && audioSource.isPlaying && audioSource.isActiveAndEnabled) { audioSource.Pause(); }
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// UnMutes the sound on all active audio sources on the thruster. This is automatically
|
|||
|
/// called in Play().
|
|||
|
/// </summary>
|
|||
|
/// <param name="isUnPauseSound"></param>
|
|||
|
public virtual void UnMute(bool isUnPauseSound)
|
|||
|
{
|
|||
|
// numAudioSources is set in Initialise()
|
|||
|
for (index = 0; index < numAudioSources; index++)
|
|||
|
{
|
|||
|
audioSource = audioSources[index];
|
|||
|
if (isUnPauseSound && !audioSource.isPlaying && audioSource.isActiveAndEnabled) { audioSource.Play(); }
|
|||
|
|
|||
|
audioSource.mute = false;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Initialise thruster effects.
|
|||
|
/// NOTE: This will increase Garbage Collection, so use sparingly.
|
|||
|
/// Ignores disabled AudioSources
|
|||
|
/// The audio max volume is set to the current volume of the audio source.
|
|||
|
/// </summary>
|
|||
|
public virtual void Initialise ()
|
|||
|
{
|
|||
|
// If this is being re-initialised, like when effects have been added, removed or disabled,
|
|||
|
// we need to reset the Particle Systems and Audio Sources to original values first.
|
|||
|
if (initialised) { RestoreInitialValues(); }
|
|||
|
|
|||
|
// Find all the attached shuriken particle systems
|
|||
|
List<ParticleSystem> shurikenParticlesList = new List<ParticleSystem>();
|
|||
|
shurikenParticlesList.AddRange(GetComponentsInChildren<ParticleSystem>());
|
|||
|
shurikenParticles = shurikenParticlesList.ToArray();
|
|||
|
|
|||
|
// Get the initial rate over time / speed multipliers - this is necessary as we want to always set the multiplier
|
|||
|
// to this initial value multiplied by the current thruster input
|
|||
|
numShurikenParticles = shurikenParticles == null ? 0 : shurikenParticles.Length;
|
|||
|
shurikenParticleInitialRateMultipliers = new float[numShurikenParticles];
|
|||
|
shurikenParticleInitialSpeedMultipliers = new float[numShurikenParticles];
|
|||
|
for (index = 0; index < numShurikenParticles; index++)
|
|||
|
{
|
|||
|
if (shurikenParticles[index] != null)
|
|||
|
{
|
|||
|
shurikenParticleInitialRateMultipliers[index] = shurikenParticles[index].emission.rateOverTimeMultiplier;
|
|||
|
shurikenParticleInitialSpeedMultipliers[index] = shurikenParticles[index].main.startSpeedMultiplier;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Find all the attached enabled audiosources
|
|||
|
List <AudioSource> audioSourceList = new List<AudioSource>(2);
|
|||
|
// For some reason when includeInactive is false, it still returns them.
|
|||
|
GetComponentsInChildren(false, audioSourceList);
|
|||
|
|
|||
|
// As an optimisation remove any invalid or non-configured sources
|
|||
|
// We need to loop through the list and potentially have to count the items twice, but it will be faster when updating.
|
|||
|
numAudioSources = audioSourceList == null ? 0 : audioSourceList.Count;
|
|||
|
for (index = numAudioSources- 1; index >= 0; index--)
|
|||
|
{
|
|||
|
if (audioSourceList[index].clip == null || !audioSourceList[index].gameObject.activeSelf) { audioSourceList.RemoveAt(index); }
|
|||
|
else { maxVolume = audioSourceList[index].volume; }
|
|||
|
}
|
|||
|
|
|||
|
audioSources = audioSourceList.ToArray();
|
|||
|
numAudioSources = audioSources == null ? 0 : audioSources.Length;
|
|||
|
|
|||
|
initialised = true;
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
}
|
|||
|
}
|