Update
This commit is contained in:
@@ -1,327 +0,0 @@
|
||||
/// ---------------------------------------------
|
||||
/// Ultimate Character Controller
|
||||
/// Copyright (c) Opsive. All Rights Reserved.
|
||||
/// https://www.opsive.com
|
||||
/// ---------------------------------------------
|
||||
|
||||
namespace Opsive.UltimateCharacterController.SurfaceSystem
|
||||
{
|
||||
using Opsive.Shared.Game;
|
||||
using Opsive.UltimateCharacterController.Game;
|
||||
using Opsive.UltimateCharacterController.Utility;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
/// <summary>
|
||||
/// The DecalManager is responsible for managing the spawned decals. The decals can be capped at a limit to prevent too many from being
|
||||
/// spawned. These decals can then slowly be faded (weathered) for a smooth transition rather than the decal just popping out of existance.
|
||||
/// </summary>
|
||||
public class DecalManager : MonoBehaviour
|
||||
{
|
||||
private static DecalManager s_Instance;
|
||||
private static DecalManager Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!s_Initialized) {
|
||||
s_Instance = new GameObject("Decal Manager").AddComponent<DecalManager>();
|
||||
s_Initialized = true;
|
||||
}
|
||||
return s_Instance;
|
||||
}
|
||||
}
|
||||
private static bool s_Initialized;
|
||||
|
||||
[Tooltip("The maximum number of decals.")]
|
||||
[SerializeField] protected int m_DecalLimit = 100;
|
||||
[Tooltip("The number of decals which should slowly fade after the decal limit has been reached.")]
|
||||
[SerializeField] protected int m_WeatheredDecalLimit = 20;
|
||||
[Tooltip("The speed that the decals should fadeout after they have been removed from the weathered array.")]
|
||||
[SerializeField] protected int m_RemoveFadeoutSpeed = 10;
|
||||
|
||||
public int DecalLimit { get { return m_DecalLimit; } set { m_DecalLimit = value; } }
|
||||
public int WeatheredDecalLimit { get { return m_WeatheredDecalLimit; } set { m_WeatheredDecalLimit = value; } }
|
||||
public int RemoveFadeoutSpeed { get { return m_RemoveFadeoutSpeed; } set { m_RemoveFadeoutSpeed = value; } }
|
||||
|
||||
private List<GameObject> m_Decals = new List<GameObject>();
|
||||
private List<Renderer> m_WeatheredDecals = new List<Renderer>();
|
||||
private List<Renderer> m_DecalsToFade = new List<Renderer>();
|
||||
private Dictionary<GameObject, Renderer> m_DecalRendererMap = new Dictionary<GameObject, Renderer>();
|
||||
private Dictionary<GameObject, Mesh> m_DecalMeshMap = new Dictionary<GameObject, Mesh>();
|
||||
|
||||
/// <summary>
|
||||
/// The object has been enabled.
|
||||
/// </summary>
|
||||
private void OnEnable()
|
||||
{
|
||||
// The object may have been enabled outside of the scene unloading.
|
||||
if (s_Instance == null) {
|
||||
s_Instance = this;
|
||||
s_Initialized = true;
|
||||
SceneManager.sceneUnloaded -= SceneUnloaded;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a new decal.
|
||||
/// </summary>
|
||||
/// <param name="original">The original prefab to spawn an instance of.</param>
|
||||
/// <param name="hit">The RaycastHit which caused the decal to spawn.</param>
|
||||
/// <param name="scale">The scale of the decal to spawn.</param>
|
||||
/// <param name="allowedEdgeOverlap">How close to the edge the decal is allowed to spawn.</param>
|
||||
public static void Spawn(GameObject original, RaycastHit hit, float scale, float allowedEdgeOverlap)
|
||||
{
|
||||
Instance.SpawnInternal(original, hit, scale, allowedEdgeOverlap);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal method which instantiates a new decal.
|
||||
/// </summary>
|
||||
/// <param name="original">The original prefab to spawn an instance of.</param>
|
||||
/// <param name="hit">The RaycastHit which caused the decal to spawn.</param>
|
||||
/// <param name="scale">The scale of the decal to spawn.</param>
|
||||
/// <param name="allowedEdgeOverlap">How close to the edge the decal is allowed to spawn.</param>
|
||||
private void SpawnInternal(GameObject original, RaycastHit hit, float scale, float allowedEdgeOverlap)
|
||||
{
|
||||
SpawnDecal(original, hit, Quaternion.LookRotation(hit.normal) * Quaternion.AngleAxis(Random.Range(0, 360), Vector3.forward), scale, allowedEdgeOverlap);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a new footprint.
|
||||
/// </summary>
|
||||
/// <param name="original">The original prefab to spawn an instance of.</param>
|
||||
/// <param name="hit">The RaycastHit which caused the footprint to spawn.</param>
|
||||
/// <param name="scale">The scale of the decal to spawn.</param>
|
||||
/// <param name="allowedEdgeOverlap">How close to the edge the footprint is allowed to spawn.</param>
|
||||
/// <param name="footprintDirection">The direction that the footprint decal should face.</param>
|
||||
/// <param name="flipFootprint">Should the footprint decal be flipped?</param>
|
||||
public static void SpawnFootprint(GameObject original, RaycastHit hit, float scale, float allowedEdgeOverlap, Vector3 footprintDirection, bool flipFootprint)
|
||||
{
|
||||
Instance.SpawnFootprintInternal(original, hit, scale, allowedEdgeOverlap, footprintDirection, flipFootprint);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal method which instantiates a new footprint.
|
||||
/// </summary>
|
||||
/// <param name="original">The original prefab to spawn an instance of.</param>
|
||||
/// <param name="hit">The RaycastHit which caused the footprint to spawn.</param>
|
||||
/// <param name="scale">The scale of the decal to spawn.</param>
|
||||
/// <param name="allowedEdgeOverlap">How close to the edge the footprint is allowed to spawn.</param>
|
||||
/// <param name="footprintDirection">The direction that the footprint decal should face.</param>
|
||||
/// <param name="flipFootprint">Should the footprint decal be flipped?</param>
|
||||
private void SpawnFootprintInternal(GameObject original, RaycastHit hit, float scale, float allowedEdgeOverlap, Vector3 footprintDirection, bool flipFootprint)
|
||||
{
|
||||
var decal = SpawnDecal(original, hit, Quaternion.LookRotation(hit.normal, footprintDirection), scale, allowedEdgeOverlap);
|
||||
// Changing the local x axis will flip the footprint.
|
||||
if (decal != null && flipFootprint) {
|
||||
var localScale = decal.transform.localScale;
|
||||
localScale.x *= -1;
|
||||
decal.transform.localScale = localScale;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a new decal.
|
||||
/// </summary>
|
||||
/// <param name="original">The original prefab to spawn an instance of.</param>
|
||||
/// <param name="hit">The RaycastHit which caused the footprint to spawn.</param>
|
||||
/// <param name="rotation">The rotation of the decal which should be spawned.</param>
|
||||
/// <param name="scale">The scale of the decal to spawn.</param>
|
||||
/// <param name="allowedEdgeOverlap">How close to the edge the footprint is allowed to spawn.</param>
|
||||
/// <returns>The spawned decal. Can be null.</returns>
|
||||
private GameObject SpawnDecal(GameObject original, RaycastHit hit, Quaternion rotation, float scale, float allowedEdgeOverlap)
|
||||
{
|
||||
// Prevent z fighting by slightly raising the decal off of the surface.
|
||||
var decal = ObjectPool.Instantiate(original, hit.point + (hit.normal * 0.001f), rotation);
|
||||
// Only set the decal parent to the hit transform on uniform objects to prevent stretching.
|
||||
if (MathUtility.IsUniform(hit.transform.localScale)) {
|
||||
decal.transform.parent = hit.transform;
|
||||
}
|
||||
if (scale != 1) {
|
||||
var vectorScale = Vector3.one;
|
||||
vectorScale.x = vectorScale.y = scale;
|
||||
decal.transform.localScale = Vector3.Scale(decal.transform.localScale, vectorScale);
|
||||
}
|
||||
|
||||
// Destroy the object if it cannot be cached. The object won't be able to be cached if it doesn't have all of the required components.
|
||||
if (!CacheMeshAndRenderer(decal)) {
|
||||
ObjectPool.Destroy(decal);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Do a test on the decal's quad to ensure all four corners are flush against a surface. This will prevent the decal from sticking out on an edge.
|
||||
if (allowedEdgeOverlap < 0.5f) {
|
||||
if (!DoQuadTest(decal, allowedEdgeOverlap)) {
|
||||
ObjectPool.Destroy(decal);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// The decal can be added.
|
||||
Add(decal);
|
||||
|
||||
return decal;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores the decal's mesh and renderer.
|
||||
/// </summary>
|
||||
/// <param name="decal">The decal to store the mesh and renderer of.</param>
|
||||
/// <returns>True if the mesh and renderer were able to be cached.</returns>
|
||||
private bool CacheMeshAndRenderer(GameObject decal)
|
||||
{
|
||||
Renderer renderer;
|
||||
if (!m_DecalRendererMap.TryGetValue(decal, out renderer)) {
|
||||
var meshFilter = decal.GetComponent<MeshFilter>();
|
||||
if (meshFilter == null) {
|
||||
return false;
|
||||
}
|
||||
if (meshFilter.mesh == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
renderer = decal.GetComponent<Renderer>();
|
||||
if (renderer == null) {
|
||||
return false;
|
||||
}
|
||||
if (renderer.material == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cache the decal renderer and mesh.
|
||||
m_DecalRendererMap.Add(decal, renderer);
|
||||
m_DecalMeshMap.Add(decal, meshFilter.mesh);
|
||||
}
|
||||
|
||||
// The decal should start opaque.
|
||||
var color = renderer.material.color;
|
||||
color.a = 1;
|
||||
renderer.material.color = color;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check all four corners of the decal for surface contact.
|
||||
/// </summary>
|
||||
/// <param name="decal">The decal to check the corners of.</param>
|
||||
/// <param name="allowedEdgeOverlap">How close to the edge the decal is allowed to spawn.</param>
|
||||
/// <returns>True if all four corners are flush against a surface.</returns>
|
||||
private bool DoQuadTest(GameObject decal, float allowedEdgeOverlap)
|
||||
{
|
||||
Mesh mesh;
|
||||
if (!m_DecalMeshMap.TryGetValue(decal, out mesh)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RaycastHit hit;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
// The decal isn't hitting anything if the raycast returns false.
|
||||
if (!Physics.Raycast(decal.transform.TransformPoint(mesh.vertices[i] * (1 - (allowedEdgeOverlap * 2))) + (decal.transform.forward * 0.1f),
|
||||
-decal.transform.forward, out hit, 0.2f, ~((1 << LayerManager.TransparentFX) | (1 << LayerManager.IgnoreRaycast) |
|
||||
(1 << LayerManager.VisualEffect) | (1 << LayerManager.Water)), QueryTriggerInteraction.Ignore)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the decal to the active decal stack.
|
||||
/// </summary>
|
||||
/// <param name="decal">The decal to add.</param>
|
||||
private void Add(GameObject decal)
|
||||
{
|
||||
m_Decals.Add(decal);
|
||||
// If the total decal count is greater than the specified limit then the oldest decal should start to be weathered.
|
||||
if (m_Decals.Count >= m_DecalLimit) {
|
||||
var oldestDecalRenderer = m_DecalRendererMap[m_Decals[0]];
|
||||
m_WeatheredDecals.Add(oldestDecalRenderer);
|
||||
m_Decals.RemoveAt(0);
|
||||
WeatherDecals();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Slowly fade out the oldest decal in the weathered list.
|
||||
/// </summary>
|
||||
private void WeatherDecals()
|
||||
{
|
||||
// As each decal is added to the weathered list it should slowly fade out.
|
||||
for (int i = 0; i < m_WeatheredDecals.Count; ++i) {
|
||||
if (m_WeatheredDecals[i] == null) {
|
||||
m_WeatheredDecals.RemoveAt(i);
|
||||
continue;
|
||||
}
|
||||
var color = m_WeatheredDecals[i].material.color;
|
||||
color.a = Mathf.Clamp01(color.a - (1 / (float)m_WeatheredDecalLimit));
|
||||
m_WeatheredDecals[i].material.color = color;
|
||||
}
|
||||
|
||||
// Remove the oldest weathered decal if the limit is reached. This decal will be added to the fade list.
|
||||
if (m_WeatheredDecals.Count >= m_WeatheredDecalLimit) {
|
||||
m_DecalsToFade.Add(m_WeatheredDecals[0]);
|
||||
m_WeatheredDecals.RemoveAt(0);
|
||||
enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fade out the decals in the decals to fade list.
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
for (int i = m_DecalsToFade.Count - 1; i >= 0; --i) {
|
||||
if (m_DecalsToFade[i] == null) {
|
||||
m_DecalsToFade.RemoveAt(i);
|
||||
continue;
|
||||
}
|
||||
var color = m_DecalsToFade[i].material.color;
|
||||
color.a = Mathf.Lerp(color.a, 0, Time.deltaTime * m_RemoveFadeoutSpeed);
|
||||
// The decal can be removed from the list when it is completely faded out.
|
||||
if (color.a == 0) {
|
||||
ObjectPool.Destroy(m_DecalsToFade[i].gameObject);
|
||||
m_DecalsToFade.RemoveAt(i);
|
||||
} else {
|
||||
m_DecalsToFade[i].material.color = color;
|
||||
}
|
||||
}
|
||||
// The component can be disabled when there are no decals within the list.
|
||||
if (m_DecalsToFade.Count == 0) {
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset the initialized variable when the scene is no longer loaded.
|
||||
/// </summary>
|
||||
/// <param name="scene">The scene that was unloaded.</param>
|
||||
private void SceneUnloaded(Scene scene)
|
||||
{
|
||||
s_Initialized = false;
|
||||
s_Instance = null;
|
||||
SceneManager.sceneUnloaded -= SceneUnloaded;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The object has been disabled.
|
||||
/// </summary>
|
||||
private void OnDisable()
|
||||
{
|
||||
SceneManager.sceneUnloaded += SceneUnloaded;
|
||||
}
|
||||
|
||||
#if UNITY_2019_3_OR_NEWER
|
||||
/// <summary>
|
||||
/// Reset the static variables for domain reloading.
|
||||
/// </summary>
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||
private static void DomainReset()
|
||||
{
|
||||
s_Initialized = false;
|
||||
s_Instance = null;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8b17e11b4e85729449b773c74f06daef
|
||||
timeCreated: 1508769295
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,243 +0,0 @@
|
||||
/// ---------------------------------------------
|
||||
/// Ultimate Character Controller
|
||||
/// Copyright (c) Opsive. All Rights Reserved.
|
||||
/// https://www.opsive.com
|
||||
/// ---------------------------------------------
|
||||
|
||||
namespace Opsive.UltimateCharacterController.SurfaceSystem
|
||||
{
|
||||
using Opsive.UltimateCharacterController.Audio;
|
||||
using Opsive.UltimateCharacterController.StateSystem;
|
||||
using Opsive.UltimateCharacterController.Utility;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies a recipe for effects that can be spawned in response to a certain type of collision. This collision might occur when
|
||||
/// bullets hit a wall, a character places a footprint, or the character falls to the ground.
|
||||
/// </summary>
|
||||
public class SurfaceEffect : ScriptableObject
|
||||
{
|
||||
[Tooltip("Objects that should be spawned during a collision.")]
|
||||
[SerializeField] protected ObjectSpawnInfo[] m_SpawnedObjects;
|
||||
[Tooltip("An array of decals that can be spawned. A single decal will be randomlly chosen when the SurfaceEffect should spawn.")]
|
||||
[SerializeField] protected GameObject[] m_Decals;
|
||||
[Tooltip("The minimum scale of the decal.")]
|
||||
[SerializeField] protected float m_MinDecalScale = 1;
|
||||
[Tooltip("The maximum scale of the decal.")]
|
||||
[SerializeField] protected float m_MaxDecalScale = 1;
|
||||
[Tooltip("How close to the edge the decal is allowed to spawn. A value of 0 requires the full quad is required to sit on the background surface.")]
|
||||
[Range(0, 0.5f)] [SerializeField] protected float m_AllowedDecalEdgeOverlap = 0.25f;
|
||||
[Tooltip("The AudioClips that can be triggered from a collision.")]
|
||||
[SerializeField] protected AudioClip[] m_AudioClips;
|
||||
[Tooltip("The minimum volume of the AudioClip.")]
|
||||
[SerializeField] protected float m_MinAudioVolume = 1;
|
||||
[Tooltip("The maximum volume of the AudioClip.")]
|
||||
[SerializeField] protected float m_MaxAudioVolume = 1;
|
||||
[Tooltip("The minimum pitch of the AudioClip.")]
|
||||
[SerializeField] protected float m_MinAudioPitch = 1;
|
||||
[Tooltip("The maximum pitch of the AudioClip.")]
|
||||
[SerializeField] protected float m_MaxAudioPitch = 1;
|
||||
[Tooltip("Should a maximum of one clip be played per frame? This prevents too many sounds from playing at once.")]
|
||||
[SerializeField] protected bool m_OneClipPerFrame;
|
||||
[Tooltip("Should the AudioClip be randomly selected? If false the clips will be played sequentially.")]
|
||||
[SerializeField] protected bool m_RandomClipSelection = true;
|
||||
[Tooltip("The name of the state that should be activated upon impact.")]
|
||||
[SerializeField] protected string m_StateName;
|
||||
[Tooltip("The number of seconds until the specified state is disabled. A value of -1 will require the state to be disabled manually.")]
|
||||
[SerializeField] protected float m_StateDisableTimer = 10;
|
||||
|
||||
public ObjectSpawnInfo[] SpawnedObjects { get { return m_SpawnedObjects; } set { m_SpawnedObjects = value; } }
|
||||
public GameObject[] Decals { get { return m_Decals; } set { m_Decals = value; } }
|
||||
public float MinDecalScale { get { return m_MinDecalScale; } set { m_MinDecalScale = value; } }
|
||||
public float MaxDecalScale { get { return m_MaxDecalScale; } set { m_MaxDecalScale = value; } }
|
||||
public float AllowedDecalEdgeOverlap { get { return m_AllowedDecalEdgeOverlap; } set { m_AllowedDecalEdgeOverlap = value; } }
|
||||
public AudioClip[] AudioClips { get { return m_AudioClips; } set { m_AudioClips = value; } }
|
||||
public float MinAudioVolume { get { return m_MinAudioVolume; } set { m_MinAudioVolume = value; } }
|
||||
public float MaxAudioVolume { get { return m_MaxAudioVolume; } set { m_MaxAudioVolume = value; } }
|
||||
public float MinAudioPitch { get { return m_MinAudioPitch; } set { m_MinAudioPitch = value; } }
|
||||
public float MaxAudioPitch { get { return m_MaxAudioPitch; } set { m_MaxAudioPitch = value; } }
|
||||
public bool OneClipPerFrame { get { return m_OneClipPerFrame; } set { m_OneClipPerFrame = value; } }
|
||||
public bool RandomClipSelection { get { return m_RandomClipSelection; } set { m_RandomClipSelection = value; } }
|
||||
public string StateName { get { return m_StateName; } set { m_StateName = value; } }
|
||||
public float StateDisableTimer { get { return m_StateDisableTimer; } set { m_StateDisableTimer = value; } }
|
||||
|
||||
private GameObject m_LastSpawnedDecal;
|
||||
private AudioClip m_LastPlayedAudioClip;
|
||||
private int m_LastPlayedAudioClipFrame;
|
||||
private int m_AudioClipIndex;
|
||||
|
||||
/// <summary>
|
||||
/// Spawns the surface objects and decals.
|
||||
/// </summary>
|
||||
/// <param name="hit">The RaycastHit which caused the collision.</param>
|
||||
/// <param name="gravityDirection">The normalized direction of the character's gravity.</param>
|
||||
/// <param name="timeScale">The timescale of the originator.</param>
|
||||
/// <param name="originator">The object which spawned the effect.</param>
|
||||
/// <param name="spawnDecals">Should the decals be spawned? Not all surfaces allow for decals.</param>
|
||||
public void Spawn(RaycastHit hit, Vector3 gravityDirection, float timeScale, GameObject originator, bool spawnDecals)
|
||||
{
|
||||
SpawnObjects(hit, gravityDirection);
|
||||
|
||||
PlayAudioClip(hit, timeScale, null);
|
||||
|
||||
SetState(hit);
|
||||
|
||||
// Return early if the SurfaceType doesn't allow decals.
|
||||
if (!spawnDecals) {
|
||||
return;
|
||||
}
|
||||
|
||||
SpawnDecal(hit);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates the Spawned Objects.
|
||||
/// </summary>
|
||||
/// <param name="hit">The RaycastHit which caused the collision.</param>
|
||||
/// <param name="gravityDirection">The normalized direction of the character's gravity.</param>
|
||||
private void SpawnObjects(RaycastHit hit, Vector3 gravityDirection)
|
||||
{
|
||||
for (int i = 0; i < m_SpawnedObjects.Length; ++i) {
|
||||
if (m_SpawnedObjects[i] == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
m_SpawnedObjects[i].Instantiate(hit.point, hit.normal, gravityDirection);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a decal.
|
||||
/// </summary>
|
||||
/// <param name="hit">The RaycastHit which caused the collision.</param>
|
||||
private void SpawnDecal(RaycastHit hit)
|
||||
{
|
||||
var decal = GetDecal();
|
||||
// The last spawned decal should be remembered so no two decals are spawned immediately after one another if multiple decals can be spawned.
|
||||
m_LastSpawnedDecal = decal;
|
||||
DecalManager.Spawn(decal, hit, Random.Range(m_MinDecalScale, m_MaxDecalScale), m_AllowedDecalEdgeOverlap);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a random decal from the decals array.
|
||||
/// </summary>
|
||||
/// <returns>A random decal from the decals array.</returns>
|
||||
private GameObject GetDecal()
|
||||
{
|
||||
if (m_Decals.Length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// A random decal should be chosen from the array.
|
||||
var decal = m_Decals[Random.Range(0, m_Decals.Length)];
|
||||
if (decal == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If there are multiple decals available then the same decal shouldn't spawn twice in a row.
|
||||
while (m_Decals.Length > 1 && decal == m_LastSpawnedDecal) {
|
||||
decal = m_Decals[Random.Range(0, m_Decals.Length)];
|
||||
if (decal == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return decal;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A footprint and any related effects should be spawned at the hit point.
|
||||
/// </summary>
|
||||
/// <param name="hit">The RaycastHit which caused the collision.</param>
|
||||
/// <param name="gravityDirection">The normalized direction of the character's gravity.</param>
|
||||
/// <param name="timeScale">The timescale of the originator.</param>
|
||||
/// <param name="originator">The object which spawned the effect.</param>
|
||||
/// <param name="spawnDecals">Should the decals be spawned? Not all surfaces allow for decals.</param>
|
||||
/// <param name="footprintDirection">The direction that the footprint decal should face.</param>
|
||||
/// <param name="flipFootprint">Should the footprint decal be flipped?</param>
|
||||
public void SpawnFootprint(RaycastHit hit, Vector3 gravityDirection, float timeScale, GameObject originator, bool spawnDecals, Vector3 footprintDirection, bool flipFootprint)
|
||||
{
|
||||
SpawnObjects(hit, gravityDirection);
|
||||
|
||||
PlayAudioClip(hit, timeScale, originator);
|
||||
|
||||
SetState(hit);
|
||||
|
||||
// Return early if the SurfaceType doesn't allow decals.
|
||||
if (!spawnDecals) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The DecalManager will do the footprint spawn.
|
||||
DecalManager.SpawnFootprint(GetDecal(), hit, Random.Range(m_MinDecalScale, m_MaxDecalScale), m_AllowedDecalEdgeOverlap, footprintDirection, flipFootprint);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays an AudioClip on the specified GameObject.
|
||||
/// </summary>
|
||||
/// <param name="hit">The RaycastHit which caused the collision.</param>
|
||||
/// <param name="timeScale">The timescale of the originator.</param>
|
||||
/// <param name="originator">The object which should play the audio clip.</param>
|
||||
private void PlayAudioClip(RaycastHit hit, float timeScale, GameObject originator)
|
||||
{
|
||||
// No clips can be played if there are no clips in the array.
|
||||
if (m_AudioClips == null || m_AudioClips.Length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't play too many clips at once.
|
||||
if (m_OneClipPerFrame && m_LastPlayedAudioClipFrame == Time.frameCount) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the AudioClip.
|
||||
AudioClip audioClip;
|
||||
if (m_RandomClipSelection) {
|
||||
audioClip = m_AudioClips[Random.Range(0, m_AudioClips.Length)];
|
||||
} else {
|
||||
audioClip = m_AudioClips[m_AudioClipIndex];
|
||||
m_AudioClipIndex = (m_AudioClipIndex + 1) % m_AudioClips.Length;
|
||||
}
|
||||
|
||||
// If there are multiple clips available then the same clip shouldn't be played twice in a row.
|
||||
while (m_AudioClips.Length > 1 && audioClip == m_LastPlayedAudioClip) {
|
||||
audioClip = m_AudioClips[Random.Range(0, m_AudioClips.Length)];
|
||||
if (audioClip == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Play the clip.
|
||||
var pitch = Random.Range(m_MinAudioPitch, m_MaxAudioPitch) * Time.timeScale * timeScale;
|
||||
if (originator != null) {
|
||||
AudioManager.Play(originator, audioClip, pitch);
|
||||
} else {
|
||||
var volume = Random.Range(m_MinAudioVolume, m_MaxAudioVolume);
|
||||
AudioManager.PlayAtPosition(audioClip, hit.point, volume, pitch);
|
||||
}
|
||||
// Update the state.
|
||||
m_LastPlayedAudioClip = audioClip;
|
||||
m_LastPlayedAudioClipFrame = Time.frameCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the specified state on the hit object.
|
||||
/// </summary>
|
||||
/// <param name="hit">The RaycastHit which caused the collision.</param>
|
||||
private void SetState(RaycastHit hit)
|
||||
{
|
||||
if (string.IsNullOrEmpty(m_StateName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var hitGameObject = hit.transform.gameObject;
|
||||
StateManager.SetState(hitGameObject, m_StateName, true);
|
||||
|
||||
// If the timer isn't -1 then the state should be disabled after a specified amount of time. If it is -1 then the state
|
||||
// will have to be disabled manually.
|
||||
if (m_StateDisableTimer != -1) {
|
||||
StateManager.DeactivateStateTimer(hitGameObject, m_StateName, m_StateDisableTimer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1c9437dfa59d0164c89d59c14938f889
|
||||
timeCreated: 1508164996
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: c596edb1305969e4c9bf7d24926cd658, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,24 +0,0 @@
|
||||
/// ---------------------------------------------
|
||||
/// Ultimate Character Controller
|
||||
/// Copyright (c) Opsive. All Rights Reserved.
|
||||
/// https://www.opsive.com
|
||||
/// ---------------------------------------------
|
||||
|
||||
namespace Opsive.UltimateCharacterController.SurfaceSystem
|
||||
{
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// The SurfaceIdentifier can be added to GameObjects and identifies the type of surface the object belongs to.
|
||||
/// </summary>
|
||||
public class SurfaceIdentifier : MonoBehaviour
|
||||
{
|
||||
[Tooltip("The SurfaceType of the object.")]
|
||||
[SerializeField] protected SurfaceType m_SurfaceType;
|
||||
[Tooltip("Are decals allowed on this object?")]
|
||||
[SerializeField] protected bool m_AllowDecals = true;
|
||||
|
||||
public SurfaceType SurfaceType { get { return m_SurfaceType; } }
|
||||
public bool AllowDecals { get { return m_AllowDecals; } }
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c6e458a25b435dd4086faf6dce3b39e6
|
||||
timeCreated: 1508165009
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,18 +0,0 @@
|
||||
/// ---------------------------------------------
|
||||
/// Ultimate Character Controller
|
||||
/// Copyright (c) Opsive. All Rights Reserved.
|
||||
/// https://www.opsive.com
|
||||
/// ---------------------------------------------
|
||||
|
||||
namespace Opsive.UltimateCharacterController.SurfaceSystem
|
||||
{
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// The SurfaceImpact is used to distinguish between different types of collisions for surface effects.
|
||||
/// </summary>
|
||||
public class SurfaceImpact : ScriptableObject
|
||||
{
|
||||
// Intentionally left blank.
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8292fa0419937544a9bf8cfbdfc34954
|
||||
timeCreated: 1508168240
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 3061bd671d9c87947bb5b99a48ab7e1b, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,16 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3230f1117d4c11549b64332c8db164af
|
||||
timeCreated: 1545819676
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences:
|
||||
- m_FallbackSurfaceImpact: {instanceID: 0}
|
||||
- m_FallbackSurfaceType: {fileID: 11400000, guid: e616b14f5d2d0cf42bb5b4360f2b2089,
|
||||
type: 2}
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,57 +0,0 @@
|
||||
/// ---------------------------------------------
|
||||
/// Ultimate Character Controller
|
||||
/// Copyright (c) Opsive. All Rights Reserved.
|
||||
/// https://www.opsive.com
|
||||
/// ---------------------------------------------
|
||||
|
||||
namespace Opsive.UltimateCharacterController.SurfaceSystem
|
||||
{
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// A ImpactEffect pairs the SurfaceImpact with a SurfaceEffect.
|
||||
/// </summary>
|
||||
[System.Serializable]
|
||||
public struct ImpactEffect
|
||||
{
|
||||
#pragma warning disable 0649
|
||||
[Tooltip("The SurfaceImpact which triggers the SurfaceEffect.")]
|
||||
[SerializeField] private SurfaceImpact m_SurfaceImpact;
|
||||
[Tooltip("The SurfaceEffect to spawn when triggered by the SurfaceImpact.")]
|
||||
[SerializeField] private SurfaceEffect m_SurfaceEffect;
|
||||
#pragma warning restore 0649
|
||||
|
||||
public SurfaceImpact SurfaceImpact { get { return m_SurfaceImpact; } }
|
||||
public SurfaceEffect SurfaceEffect { get { return m_SurfaceEffect; } }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps a texture to a set of UV coordinates.
|
||||
/// </summary>
|
||||
[System.Serializable]
|
||||
public struct UVTexture
|
||||
{
|
||||
[Tooltip("The texture to map the UV coordinates to.")]
|
||||
[SerializeField] private Texture m_Texture;
|
||||
[Tooltip("The UV coordinates of the texture.")]
|
||||
[SerializeField] private Rect m_UV;
|
||||
|
||||
public Texture Texture { get { return m_Texture; } set { m_Texture = value; } }
|
||||
public Rect UV { get { return m_UV; } set { m_UV = value; } }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represets a default surface listed within the SurfaceManager.
|
||||
/// </summary>
|
||||
[System.Serializable]
|
||||
public struct ObjectSurface
|
||||
{
|
||||
[Tooltip("The type of surface represented.")]
|
||||
[SerializeField] private SurfaceType m_SurfaceType;
|
||||
[Tooltip("The textures which go along with the specified SurfaceType.")]
|
||||
[SerializeField] private UVTexture[] m_UVTextures;
|
||||
|
||||
public SurfaceType SurfaceType { get { return m_SurfaceType; } set { m_SurfaceType = value; } }
|
||||
public UVTexture[] UVTextures { get { return m_UVTextures; } set { m_UVTextures = value; } }
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 84f5cfae165e1744f99ffdb69769baac
|
||||
timeCreated: 1508192337
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,24 +0,0 @@
|
||||
/// ---------------------------------------------
|
||||
/// Ultimate Character Controller
|
||||
/// Copyright (c) Opsive. All Rights Reserved.
|
||||
/// https://www.opsive.com
|
||||
/// ---------------------------------------------
|
||||
|
||||
namespace Opsive.UltimateCharacterController.SurfaceSystem
|
||||
{
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// The SurfaceType is the main surface concept used for spawning objects based on impact.
|
||||
/// </summary>
|
||||
public class SurfaceType : ScriptableObject
|
||||
{
|
||||
[Tooltip("The SurfaceImpactEffects array maps the SurfaceImpact object with the SurfaceEffect object.")]
|
||||
[SerializeField] protected ImpactEffect[] m_ImpactEffects;
|
||||
[Tooltip("Does the SurfaceType allow footprints?")]
|
||||
[SerializeField] protected bool m_AllowFootprints = true;
|
||||
|
||||
public ImpactEffect[] ImpactEffects { get { return m_ImpactEffects; } }
|
||||
public bool AllowFootprints { get { return m_AllowFootprints; } }
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 696b92c305704c84fb3f7a11d0c474fd
|
||||
timeCreated: 1508165060
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: b34170890b9b9d1469d9b451fddc01dd, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user