projectEli/Assets/NeoFPS/Core/Surfaces/SurfaceManager.cs
2022-11-06 20:28:33 -05:00

254 lines
9.1 KiB
C#

using NeoFPS.Constants;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace NeoFPS
{
[CreateAssetMenu(fileName = "FpsManager_Surfaces", menuName = "NeoFPS/Managers/Surface Manager", order = NeoFpsMenuPriorities.manager_surfaces)]
[HelpURL("https://docs.neofps.com/manual/surfacesref-so-poolmanager.html")]
public class SurfaceManager : NeoFpsManager<SurfaceManager>
{
private static RuntimeBehaviour s_ProxyBehaviour = null;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
static void LoadSurfaceManager()
{
GetInstance("FpsManager_Surfaces");
}
protected override void Initialise()
{
s_ProxyBehaviour = GetBehaviourProxy<RuntimeBehaviour>();
}
[Header("VFX")]
[SerializeField, RequiredObjectProperty, Tooltip("The impact special effects for things like bullet hits.")]
private SurfaceHitFxData m_ImpactEffects = null;
[SerializeField, Tooltip("Which physics layers will show decals.")]
private LayerMask m_DecalLayers = PhysicsFilter.LayerFilter.EnvironmentDetail;
[Header("Audio")]
[SerializeField, RequiredObjectProperty, Tooltip("The audio library for impact audio, eg. bullet hits.")]
private SurfaceAudioData m_ImpactAudio = null;
[SerializeField, Tooltip("Should impact audio be louder for heavier hits.")]
private bool m_ScaleVolumeToHitSize = false;
[Tooltip("The maximum distance from the player character to play impact audio.")]
[SerializeField] private float m_MaxImpactAudioDistance = 20f;
private static Camera s_CurrentMainCamera = null;
private float m_MaxAudioDistanceSqrd = 0f;
public static SurfaceFxOverrides currentOverrides
{
get;
private set;
}
public static LayerMask decalLayers
{
get
{
if (instance != null)
return instance.m_DecalLayers;
else
return PhysicsFilter.LayerFilter.EnvironmentDetail;
}
}
void OnValidate()
{
if (m_MaxImpactAudioDistance < 10f)
m_MaxImpactAudioDistance = 10f;
}
public override bool IsValid()
{
return m_ImpactEffects != null && m_ImpactAudio != null;
}
private void Awake()
{
m_MaxAudioDistanceSqrd = m_MaxImpactAudioDistance * m_MaxImpactAudioDistance;
}
protected override void OnDestroy()
{
base.OnDestroy();
if (s_ProxyBehaviour != null)
{
Destroy(s_ProxyBehaviour);
s_ProxyBehaviour = null;
}
}
class RuntimeBehaviour : MonoBehaviour
{
public BaseHitFxBehaviour[] impactEffectInstances
{
get;
private set;
}
private Transform m_EffectsParent = null;
void Awake()
{
SetSurfaceHitFx(instance.m_ImpactEffects);
SceneManager.activeSceneChanged += OnActiveSceneChanged;
}
void OnDestroy()
{
SceneManager.activeSceneChanged -= OnActiveSceneChanged;
}
void OnActiveSceneChanged(Scene s1, Scene s2)
{
for (int i = 0; i < impactEffectInstances.Length; ++i)
{
if (impactEffectInstances[i] != null)
impactEffectInstances[i].OnActiveSceneChange();
}
}
IEnumerator DelayedInitEffects()
{
yield return null;
for (int i = 0; i < impactEffectInstances.Length; ++i)
{
if (impactEffectInstances[i] != null && impactEffectInstances[i].forceInitialise)
impactEffectInstances[i].Hit(null, new Vector3(0f, -10000f, 0f), Vector3.up);
}
}
public void SetSurfaceHitFx(SurfaceHitFxData data)
{
if (data == null)
return;
if (m_EffectsParent == null)
{
// Create effects parent object
var go = new GameObject("SurfaceManagerEffects");
m_EffectsParent = go.transform;
m_EffectsParent.SetParent(transform);
}
// Allocate array
if (impactEffectInstances == null)
impactEffectInstances = new BaseHitFxBehaviour[FpsSurfaceMaterial.count];
// Instantiate surface effects
for (int i = 0; i < impactEffectInstances.Length; ++i)
{
var effect = data.GetImpactEffect((FpsSurfaceMaterial)i);
if (effect != null)
{
// Destroy existing effect
if (impactEffectInstances[i] != null)
Destroy(impactEffectInstances[i].gameObject);
// Instantiate new effect
impactEffectInstances[i] = Instantiate(effect, m_EffectsParent);
}
}
// Initialise effects on the next frame (not compulsory but prevents frame hitch when first used)
StartCoroutine(DelayedInitEffects());
}
}
public static void ApplyOverrides(SurfaceFxOverrides overrides)
{
if (instance == null)
return;
currentOverrides = overrides;
if (s_ProxyBehaviour != null)
s_ProxyBehaviour.SetSurfaceHitFx(overrides.impactEffects);
}
public static void RemoveOverrides(SurfaceFxOverrides overrides)
{
if (instance == null)
return;
if (currentOverrides == overrides)
currentOverrides = null;
if (s_ProxyBehaviour != null)
s_ProxyBehaviour.SetSurfaceHitFx(instance.m_ImpactEffects);
}
public static void ShowBulletHit(RaycastHit hit, Vector3 rayDirection, float size, bool rigidBody = false)
{
if (instance == null)
return;
if (hit.transform == null)
{
Debug.LogError("SurfaceManager.ShowBulletHit() called with invalid RaycastHit data. Are you checking the raycast actually hit before sending?");
return;
}
FpsSurfaceMaterial surfaceMaterial = FpsSurfaceMaterial.Default;
// Get the surface material using BaseSurface behaviour (if present - default if not / invalid)
BaseSurface surface = hit.transform.GetComponentInParent<BaseSurface>();
if (surface != null)
surfaceMaterial = surface.GetSurface(hit);
// Show bullet hit
bool decal = !rigidBody && (instance.m_DecalLayers & (1 << hit.collider.gameObject.layer)) != 0;
s_ProxyBehaviour.impactEffectInstances[surfaceMaterial].Hit(hit.transform.gameObject, hit.point, hit.normal, rayDirection, size, decal);
// Get the main camera (Camera.main sucks, but I can't assume all users will add a camera tracking component to any relevant camera)
if (s_CurrentMainCamera == null || !s_CurrentMainCamera.enabled)
s_CurrentMainCamera = Camera.main;
// Play bullet hit sound effect if close enough to camera
if (instance.m_ImpactAudio != null && (s_CurrentMainCamera.transform.position - hit.point).sqrMagnitude <= instance.m_MaxAudioDistanceSqrd)
{
float volume = 1f;
AudioClip clip = null;
// Get audio clip & volume
if (currentOverrides != null)
{
clip = currentOverrides.impactAudio.GetAudioClip(surfaceMaterial, out volume);
if (clip == null)
clip = instance.m_ImpactAudio.GetAudioClip(surfaceMaterial, out volume);
}
else
clip = instance.m_ImpactAudio.GetAudioClip(surfaceMaterial, out volume);
if (instance.m_ScaleVolumeToHitSize)
volume *= size;
// Play the audio
NeoFpsAudioManager.PlayEffectAudioAtPosition(clip, hit.point, volume);
}
}
public static void PlayImpactNoiseAtPosition(FpsSurfaceMaterial surfaceMaterial, Vector3 position, float volume)
{
if (instance == null || instance.m_ImpactAudio == null)
return;
// Get audio clip
float clipVolume = 1f;
AudioClip clip = instance.m_ImpactAudio.GetAudioClip(surfaceMaterial, out clipVolume);
volume *= clipVolume;
// Play the audio
NeoFpsAudioManager.PlayEffectAudioAtPosition(clip, position, volume);
}
}
}