Update
This commit is contained in:
@@ -1,36 +0,0 @@
|
||||
/// ---------------------------------------------
|
||||
/// Ultimate Character Controller
|
||||
/// Copyright (c) Opsive. All Rights Reserved.
|
||||
/// https://www.opsive.com
|
||||
/// ---------------------------------------------
|
||||
|
||||
namespace Opsive.UltimateCharacterController.Game
|
||||
{
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Interface for any kinematic object that can be moved with no parameters.
|
||||
/// </summary>
|
||||
public interface IKinematicObject
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the location that the object should be updated.
|
||||
/// </summary>
|
||||
KinematicObjectManager.UpdateLocation UpdateLocation { get; }
|
||||
|
||||
/// <summary>
|
||||
/// A reference to the object's transform component.
|
||||
/// </summary>
|
||||
Transform transform { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets the index of the kinematic object.
|
||||
/// </summary>
|
||||
int KinematicObjectIndex { set; }
|
||||
|
||||
/// <summary>
|
||||
/// Moves the object.
|
||||
/// </summary>
|
||||
void Move();
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 82e30ca7ab725e24fa5b2362030da093
|
||||
timeCreated: 1519599876
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,70 +0,0 @@
|
||||
/// ---------------------------------------------
|
||||
/// Ultimate Character Controller
|
||||
/// Copyright (c) Opsive. All Rights Reserved.
|
||||
/// https://www.opsive.com
|
||||
/// ---------------------------------------------
|
||||
|
||||
namespace Opsive.UltimateCharacterController.Game
|
||||
{
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// The Kinematic Object component allows an object to be moved outside of the Kinematic Object Manager loop while still being tracked by the Kinematic Object Manager.
|
||||
/// This component should be used with the Move With Object ability:
|
||||
/// https://opsive.com/support/documentation/ultimate-character-controller/character/abilities/included-abilities/move-with-object/
|
||||
/// </summary>
|
||||
public class KinematicObject : MonoBehaviour, IKinematicObject
|
||||
{
|
||||
private Transform m_Transform;
|
||||
private Vector3 m_LastPosition;
|
||||
private Quaternion m_LastRotation;
|
||||
|
||||
private int m_KinematicObjectIndex;
|
||||
public int KinematicObjectIndex { set { m_KinematicObjectIndex = value; } }
|
||||
public KinematicObjectManager.UpdateLocation UpdateLocation { get { return KinematicObjectManager.UpdateLocation.FixedUpdate; } }
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the default values.
|
||||
/// </summary>
|
||||
private void Awake()
|
||||
{
|
||||
m_Transform = transform;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers the object with the Kinematic Object Manager.
|
||||
/// </summary>
|
||||
public void OnEnable()
|
||||
{
|
||||
m_KinematicObjectIndex = KinematicObjectManager.RegisterKinematicObject(this);
|
||||
m_LastPosition = m_Transform.position;
|
||||
m_LastRotation = m_Transform.rotation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the position/rotation of the object. The Kinematic Object component should execute after the object has been moved.
|
||||
/// </summary>
|
||||
public void FixedUpdate()
|
||||
{
|
||||
m_LastPosition = m_Transform.position;
|
||||
m_LastRotation = m_Transform.rotation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets up the object to be moved by the Kinematic Object Manager.
|
||||
/// </summary>
|
||||
public void Move()
|
||||
{
|
||||
m_Transform.position = m_LastPosition;
|
||||
m_Transform.rotation = m_LastRotation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters the object with the Kinematic Object Manager.
|
||||
/// </summary>
|
||||
public void OnDisable()
|
||||
{
|
||||
KinematicObjectManager.UnregisterKinematicObject(m_KinematicObjectIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d9ccb9cb210fb444f8daaac8d2c2f133
|
||||
timeCreated: 1554267520
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 9000
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,13 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ec179f9473f7f9849bab9aac78619ca9
|
||||
timeCreated: 1554812395
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: -5
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,184 +0,0 @@
|
||||
/// ---------------------------------------------
|
||||
/// Ultimate Character Controller
|
||||
/// Copyright (c) Opsive. All Rights Reserved.
|
||||
/// https://www.opsive.com
|
||||
/// ---------------------------------------------
|
||||
|
||||
namespace Opsive.UltimateCharacterController.Game
|
||||
{
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/// <summary>
|
||||
/// Singleton object which manages the index values of the Unity layers.
|
||||
/// </summary>
|
||||
public class LayerManager : MonoBehaviour
|
||||
{
|
||||
private static LayerManager s_Instance;
|
||||
private static bool s_Initialized;
|
||||
|
||||
// Built-in Unity layers.
|
||||
private const int DefaultLayer = 0;
|
||||
private const int TransparentFXLayer = 1;
|
||||
private const int IgnoreRaycastLayer = 2;
|
||||
private const int WaterLayer = 4;
|
||||
private const int UILayer = 5;
|
||||
|
||||
public static int Default { get { return DefaultLayer; } }
|
||||
public static int TransparentFX { get { return TransparentFXLayer; } }
|
||||
public static int IgnoreRaycast { get { return IgnoreRaycastLayer; } }
|
||||
public static int Water { get { return WaterLayer; } }
|
||||
public static int UI { get { return UILayer; } }
|
||||
|
||||
// Custom layers.
|
||||
private const int EnemyLayer = 26;
|
||||
private const int MovingPlatformLayer = 27;
|
||||
private const int VisualEffectLayer = 28;
|
||||
private const int OverlayLayer = 29;
|
||||
private const int SubCharacterLayer = 30;
|
||||
private const int CharacterLayer = 31;
|
||||
|
||||
public static int Enemy { get { return EnemyLayer; } }
|
||||
public static int MovingPlatform { get { return MovingPlatformLayer; } }
|
||||
public static int VisualEffect { get { return VisualEffectLayer; } }
|
||||
public static int Overlay { get { return OverlayLayer; } }
|
||||
public static int SubCharacter { get { return SubCharacterLayer; } }
|
||||
public static int Character{ get { return CharacterLayer; } }
|
||||
|
||||
private static Dictionary<Collider, List<Collider>> s_IgnoreCollisionMap;
|
||||
|
||||
/// <summary>
|
||||
/// The LayerManager may not have been added to the Game GameObject.
|
||||
/// </summary>
|
||||
public static void Initialize()
|
||||
{
|
||||
if (!s_Initialized) {
|
||||
s_Instance = new GameObject("Layer Manager").AddComponent<LayerManager>();
|
||||
s_Initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// Setup the layer collisions.
|
||||
/// </summary>
|
||||
private void Awake()
|
||||
{
|
||||
Physics.IgnoreLayerCollision(IgnoreRaycast, VisualEffect);
|
||||
Physics.IgnoreLayerCollision(SubCharacter, Default);
|
||||
Physics.IgnoreLayerCollision(SubCharacter, VisualEffect);
|
||||
Physics.IgnoreLayerCollision(VisualEffect, VisualEffect);
|
||||
Physics.IgnoreLayerCollision(Overlay, Default);
|
||||
Physics.IgnoreLayerCollision(Overlay, VisualEffect);
|
||||
Physics.IgnoreLayerCollision(Overlay, Enemy);
|
||||
Physics.IgnoreLayerCollision(Overlay, SubCharacter);
|
||||
Physics.IgnoreLayerCollision(Overlay, Character);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ignore the collision between the main collider and the other collider.
|
||||
/// </summary>
|
||||
/// <param name="mainCollider">The main collider collision to ignore.</param>
|
||||
/// <param name="otherCollider">The collider to ignore.</param>
|
||||
public static void IgnoreCollision(Collider mainCollider, Collider otherCollider)
|
||||
{
|
||||
// Keep a mapping of the colliders that mainCollider is ignorning so the collision can easily be reverted.
|
||||
if (s_IgnoreCollisionMap == null) {
|
||||
s_IgnoreCollisionMap = new Dictionary<Collider, List<Collider>>();
|
||||
}
|
||||
|
||||
// Add the collider to the list so it can be reverted.
|
||||
List<Collider> colliderList;
|
||||
if (!s_IgnoreCollisionMap.TryGetValue(mainCollider, out colliderList)) {
|
||||
colliderList = new List<Collider>();
|
||||
s_IgnoreCollisionMap.Add(mainCollider, colliderList);
|
||||
}
|
||||
colliderList.Add(otherCollider);
|
||||
|
||||
// The otherCollider must also keep track of the mainCollder. This allows otherCollider to be removed before mainCollider.
|
||||
if (!s_IgnoreCollisionMap.TryGetValue(otherCollider, out colliderList)) {
|
||||
colliderList = new List<Collider>();
|
||||
s_IgnoreCollisionMap.Add(otherCollider, colliderList);
|
||||
}
|
||||
colliderList.Add(mainCollider);
|
||||
|
||||
// Do the actual ignore.
|
||||
Physics.IgnoreCollision(mainCollider, otherCollider);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The main collider should no longer ignore any collisions.
|
||||
/// </summary>
|
||||
/// <param name="mainCollider">The collider to revert the collisions on.</param>
|
||||
public static void RevertCollision(Collider mainCollider)
|
||||
{
|
||||
List<Collider> colliderList;
|
||||
List<Collider> otherColliderList;
|
||||
// Revert the IgnoreCollision setting on all of the colliders that the object is currently ignoring.
|
||||
if (s_IgnoreCollisionMap != null && s_IgnoreCollisionMap.TryGetValue(mainCollider, out colliderList)) {
|
||||
for (int i = 0; i < colliderList.Count; ++i) {
|
||||
if (!mainCollider.enabled || !mainCollider.gameObject.activeInHierarchy || !colliderList[i].enabled || !colliderList[i].gameObject.activeInHierarchy) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Physics.IgnoreCollision(mainCollider, colliderList[i], false);
|
||||
|
||||
// A two way map was added when the initial IgnoreCollision was added. Remove that second map because the IgnoreCollision has been removed.
|
||||
if (s_IgnoreCollisionMap.TryGetValue(colliderList[i], out otherColliderList)) {
|
||||
for (int j = 0; j < otherColliderList.Count; ++j) {
|
||||
if (otherColliderList[j].Equals(mainCollider)) {
|
||||
otherColliderList.RemoveAt(j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
colliderList.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// <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: 0f5d94b1cdb36f44e9edf5f60afb90de
|
||||
timeCreated: 1543541086
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: -600
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,213 +0,0 @@
|
||||
/// ---------------------------------------------
|
||||
/// Ultimate Character Controller
|
||||
/// Copyright (c) Opsive. All Rights Reserved.
|
||||
/// https://www.opsive.com
|
||||
/// ---------------------------------------------
|
||||
|
||||
namespace Opsive.UltimateCharacterController.Game
|
||||
{
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies a location that the object can spawn.
|
||||
/// </summary>
|
||||
public class SpawnPoint : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the shape in which the spawn point should randomly be determined.
|
||||
/// </summary>
|
||||
public enum SpawnShape
|
||||
{
|
||||
Point, // The spawn point will be determined at the transform position.
|
||||
Sphere, // The spawn point will be determined within a random sphere.
|
||||
Box // The spawn point will be determined within a box.
|
||||
}
|
||||
|
||||
[Tooltip("An index value used to group multiple sets of spawn points. A value of -1 will ignore the grouping.")]
|
||||
[SerializeField] protected int m_Grouping = -1;
|
||||
[Tooltip("Specifies the shape in which the spawn point should randomly be determined.")]
|
||||
[SerializeField] protected SpawnShape m_Shape;
|
||||
[Tooltip("The size of the spawn shape.")]
|
||||
[SerializeField] protected float m_Size;
|
||||
[Tooltip("Should the object be spawned randomly within the shape?")]
|
||||
[SerializeField] protected bool m_RandomShapeSpawn = true;
|
||||
[Tooltip("Specifies the height of the ground check.")]
|
||||
[SerializeField] protected float m_GroundSnapHeight;
|
||||
[Tooltip("Should the character spawn with a random y direction?")]
|
||||
[SerializeField] protected bool m_RandomDirection;
|
||||
[Tooltip("Should a check be performed to determine if there are any objects obstructing the spawn point?")]
|
||||
[SerializeField] protected bool m_CheckForObstruction;
|
||||
[Tooltip("The maximum number of collision points which the spawn points should check against.")]
|
||||
[SerializeField] protected int m_MaxCollisionCount = 20;
|
||||
[Tooltip("The layers which can obstruct the spawn point.")]
|
||||
[SerializeField] protected LayerMask m_ObstructionLayers = ~(1 << LayerManager.Default | 1 << LayerManager.IgnoreRaycast | 1 << LayerManager.TransparentFX |
|
||||
1 << LayerManager.SubCharacter | 1 << LayerManager.Overlay | 1 << LayerManager.VisualEffect);
|
||||
[Tooltip("If checking for obstruction, specifies how many times the location should be determined before it is decided that there are no valid spawn locations.")]
|
||||
[SerializeField] protected int m_PlacementAttempts = 10;
|
||||
#if UNITY_EDITOR
|
||||
[Tooltip("The color to draw the editor gizmo in (editor only).")]
|
||||
[SerializeField] protected Color m_GizmoColor = new Color(1, 0, 0, 0.3f);
|
||||
#endif
|
||||
|
||||
public int Grouping
|
||||
{
|
||||
get { return m_Grouping; }
|
||||
set
|
||||
{
|
||||
if (m_Grouping != value) {
|
||||
// The SpawnPointManager needs to be aware of the change so it can update its internal mapping.
|
||||
if (Application.isPlaying) {
|
||||
SpawnPointManager.UpdateSpawnPointGrouping(this, value);
|
||||
}
|
||||
m_Grouping = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
public SpawnShape Shape { get { return m_Shape; } set { m_Shape = value; } }
|
||||
public float Size { get { return m_Size; } set { m_Size = value; } }
|
||||
public float GroundSnapHeight { get { return m_GroundSnapHeight; } set { m_GroundSnapHeight = value; } }
|
||||
public bool RandomDirection { get { return m_RandomDirection; } set { m_RandomDirection = value; } }
|
||||
public bool CheckForObstruction { get { return m_CheckForObstruction; } set { m_CheckForObstruction = value; } }
|
||||
public int PlacementAttempts { get { return m_PlacementAttempts; } set { m_PlacementAttempts = value; } }
|
||||
#if UNITY_EDITOR
|
||||
public Color GizmoColor { get { return m_GizmoColor; } set { m_GizmoColor = value; } }
|
||||
#endif
|
||||
|
||||
private Transform m_Transform;
|
||||
private Collider[] m_ObstructionColliders;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the default values.
|
||||
/// </summary>
|
||||
private void Awake()
|
||||
{
|
||||
m_Transform = transform;
|
||||
|
||||
if (m_CheckForObstruction) {
|
||||
m_ObstructionColliders = new Collider[m_MaxCollisionCount];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the spawn point to the manager.
|
||||
/// </summary>
|
||||
private void OnEnable()
|
||||
{
|
||||
SpawnPointManager.AddSpawnPoint(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the position and rotation of the spawn point. If false is returned then the point wasn't successfully retrieved.
|
||||
/// </summary>
|
||||
/// <param name="spawningObject">The object that is spawning.</param>
|
||||
/// <param name="position">The position of the spawn point.</param>
|
||||
/// <param name="rotation">The rotation of the spawn point.</param>
|
||||
/// <returns>True if the spawn point was successfully retrieved.</returns>
|
||||
public virtual bool GetPlacement(GameObject spawningObject, ref Vector3 position, ref Quaternion rotation)
|
||||
{
|
||||
position = RandomPosition(0);
|
||||
|
||||
// Ensure the spawn point is clear of any obstructing objects.
|
||||
if (m_CheckForObstruction) {
|
||||
var attempt = 0;
|
||||
var success = false;
|
||||
while (attempt < m_PlacementAttempts) {
|
||||
if (m_Shape == SpawnShape.Point) {
|
||||
// A point will always succeed.
|
||||
success = true;
|
||||
} else if (m_Shape == SpawnShape.Sphere) {
|
||||
// Ignore any collisions with itself.
|
||||
var overlapCount = Physics.OverlapSphereNonAlloc(position, m_Size / 2, m_ObstructionColliders, m_ObstructionLayers, QueryTriggerInteraction.Ignore);
|
||||
if (spawningObject != null) {
|
||||
for (int i = overlapCount - 1; i > -1; --i) {
|
||||
if (!m_ObstructionColliders[i].transform.IsChildOf(spawningObject.transform)) {
|
||||
break;
|
||||
}
|
||||
overlapCount--;
|
||||
}
|
||||
}
|
||||
success = overlapCount == 0;
|
||||
if (success) {
|
||||
break;
|
||||
}
|
||||
} else { // Box.
|
||||
var extents = Vector3.zero;
|
||||
extents.x = extents.z = m_Size / 2;
|
||||
extents.y = m_GroundSnapHeight / 2;
|
||||
var boxPosition = m_Transform.TransformPoint(extents);
|
||||
|
||||
// Ignore any collisions with itself.
|
||||
var overlapCount = Physics.OverlapBoxNonAlloc(boxPosition, extents, m_ObstructionColliders, m_Transform.rotation, m_ObstructionLayers, QueryTriggerInteraction.Ignore);
|
||||
for (int i = overlapCount - 1; i > -1; --i) {
|
||||
if (!m_ObstructionColliders[i].transform.IsChildOf(spawningObject.transform)) {
|
||||
break;
|
||||
}
|
||||
overlapCount--;
|
||||
}
|
||||
success = overlapCount == 0;
|
||||
if (success) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
++attempt;
|
||||
position = RandomPosition(attempt);
|
||||
}
|
||||
|
||||
// No valid position was found - return false.
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If the ground snap height is positive then the position should be located on the ground.
|
||||
if (m_GroundSnapHeight > 0) {
|
||||
RaycastHit raycastHit;
|
||||
if (Physics.Raycast(position + m_Transform.up * m_GroundSnapHeight, -m_Transform.up, out raycastHit, m_GroundSnapHeight + 0.2f, m_ObstructionLayers, QueryTriggerInteraction.Ignore)) {
|
||||
position = raycastHit.point + m_Transform.up * 0.01f;
|
||||
}
|
||||
}
|
||||
|
||||
// Optionally rotate a random spawn direction.
|
||||
if (m_RandomDirection) {
|
||||
rotation = Quaternion.Euler(m_Transform.up * Random.Range(0, 360));
|
||||
} else {
|
||||
rotation = m_Transform.rotation;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retruns a position based on the spawn shape.
|
||||
/// </summary>
|
||||
/// <param name="attempt">The attempt to position the object.</param>
|
||||
/// <returns>A position within the spawn spape.</returns>
|
||||
private Vector3 RandomPosition(int attempt)
|
||||
{
|
||||
// Always first try to position in the center.
|
||||
if (attempt == 0 || !m_RandomShapeSpawn) {
|
||||
return m_Transform.position;
|
||||
}
|
||||
var localPosition = Vector3.zero;
|
||||
if (m_Shape == SpawnShape.Sphere) {
|
||||
localPosition = Random.insideUnitSphere * m_Size;
|
||||
localPosition.y = 0;
|
||||
} else if (m_Shape == SpawnShape.Box) {
|
||||
var halfSize = m_Size / 2;
|
||||
localPosition.x = Random.Range(-halfSize, halfSize);
|
||||
localPosition.z = Random.Range(-halfSize, halfSize);
|
||||
}
|
||||
|
||||
return m_Transform.TransformPoint(localPosition);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the spawn point from the manager.
|
||||
/// </summary>
|
||||
private void OnDisable()
|
||||
{
|
||||
SpawnPointManager.RemoveSpawnPoint(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 37c3920022c7fba4bb45557e85435be4
|
||||
timeCreated: 1509974222
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,270 +0,0 @@
|
||||
/// ---------------------------------------------
|
||||
/// Ultimate Character Controller
|
||||
/// Copyright (c) Opsive. All Rights Reserved.
|
||||
/// https://www.opsive.com
|
||||
/// ---------------------------------------------
|
||||
|
||||
namespace Opsive.UltimateCharacterController.Game
|
||||
{
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/// <summary>
|
||||
/// Determines which SpawnPoint to use in order to determine where the object should spawn.
|
||||
/// </summary>
|
||||
public class SpawnPointManager : MonoBehaviour
|
||||
{
|
||||
private static SpawnPointManager s_Instance;
|
||||
private static SpawnPointManager Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!s_Initialized) {
|
||||
s_Instance = new GameObject("Spawn Point Manager").AddComponent<SpawnPointManager>();
|
||||
s_Initialized = true;
|
||||
}
|
||||
return s_Instance;
|
||||
}
|
||||
}
|
||||
private static bool s_Initialized;
|
||||
|
||||
[Tooltip("Checks the first spawn point before checking the other spawn points.")]
|
||||
[SerializeField] protected bool m_FirstSpawnPreferred;
|
||||
|
||||
private Dictionary<int, List<SpawnPoint>> m_SpawnPointGroupings = new Dictionary<int, List<SpawnPoint>>();
|
||||
private Dictionary<int, SpawnPoint> m_FirstSpawnPoint = new Dictionary<int, SpawnPoint>();
|
||||
|
||||
/// <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>
|
||||
/// Adds the specified spawn point to the manager.
|
||||
/// </summary>
|
||||
/// <param name="spawnPoint">The spawn point to add.</param>
|
||||
public static void AddSpawnPoint(SpawnPoint spawnPoint)
|
||||
{
|
||||
Instance.AddSpawnPointInternal(spawnPoint);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal method which adds the specified spawn point to the manager.
|
||||
/// </summary>
|
||||
/// <param name="spawnPoint">The spawn point to add.</param>
|
||||
private void AddSpawnPointInternal(SpawnPoint spawnPoint)
|
||||
{
|
||||
AddSpawnPointGrouping(spawnPoint, spawnPoint.Grouping);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the SpawnPoint to the specified grouping.
|
||||
/// </summary>
|
||||
/// <param name="spawnPoint">The SpawnPoint that should be added.</param>
|
||||
/// <param name="groupingIndex">The value of the grouping index.</param>
|
||||
private void AddSpawnPointGrouping(SpawnPoint spawnPoint, int groupingIndex)
|
||||
{
|
||||
List<SpawnPoint> spawnPoints;
|
||||
if (!m_SpawnPointGroupings.TryGetValue(groupingIndex, out spawnPoints)) {
|
||||
spawnPoints = new List<SpawnPoint>();
|
||||
m_SpawnPointGroupings.Add(groupingIndex, spawnPoints);
|
||||
}
|
||||
spawnPoints.Add(spawnPoint);
|
||||
|
||||
if (!m_FirstSpawnPoint.ContainsKey(groupingIndex)) {
|
||||
m_FirstSpawnPoint.Add(groupingIndex, spawnPoint);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The SpawnPoint's grouping value has changed. Update the internal group mapping.
|
||||
/// </summary>
|
||||
/// <param name="spawnPoint">The SpawnPoint whose grouping value changed.</param>
|
||||
/// <param name="newGroupingIndex">The new grouping index of the SpawnPoint.</param>
|
||||
public static void UpdateSpawnPointGrouping(SpawnPoint spawnPoint, int newGroupingIndex)
|
||||
{
|
||||
// If the manager isn't initialized yet then the grouping will be updated when the spawn point is added to the manager.
|
||||
if (!s_Initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
Instance.UpdateSpawnPointGroupingInternal(spawnPoint, newGroupingIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The SpawnPoint's grouping value has changed. Internal method which updates the internal group mapping.
|
||||
/// </summary>
|
||||
/// <param name="spawnPoint">The SpawnPoint whose grouping value changed.</param>
|
||||
/// <param name="newGroupingIndex">The new grouping value of the SpawnPoint.</param>
|
||||
private void UpdateSpawnPointGroupingInternal(SpawnPoint spawnPoint, int newGroupingIndex)
|
||||
{
|
||||
// Remove from the old grouping map.
|
||||
RemoveSpawnPoint(spawnPoint);
|
||||
|
||||
// Add to the updated grouping map.
|
||||
AddSpawnPointGrouping(spawnPoint, newGroupingIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the position and rotation of the spawn point with the specified grouping.
|
||||
/// If false is returned then the point wasn't successfully retrieved.
|
||||
/// </summary>
|
||||
/// <param name="spawningObject">The object that is spawning.</param>
|
||||
/// <param name="grouping">The grouping index of spawn points to select from.</param>
|
||||
/// <param name="position">The position of the spawn point.</param>
|
||||
/// <param name="rotation">The rotation of the spawn point.</param>
|
||||
/// <returns>True if the spawn point was successfully retrieved.</returns>
|
||||
public static bool GetPlacement(GameObject spawningObject, int grouping, ref Vector3 position, ref Quaternion rotation)
|
||||
{
|
||||
return Instance.GetPlacementInternal(spawningObject, grouping, ref position, ref rotation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal method which gets the position and rotation of the spawn point with the specified grouping.
|
||||
/// If false is returned then the point wasn't successfully retrieved.
|
||||
/// </summary>
|
||||
/// <param name="spawningObject">The object that is spawning.</param>
|
||||
/// <param name="grouping">The grouping index of spawn points to select from.</param>
|
||||
/// <param name="position">The position of the spawn point.</param>
|
||||
/// <param name="rotation">The rotation of the spawn point.</param>
|
||||
/// <returns>True if the spawn point was successfully retrieved.</returns>
|
||||
protected virtual bool GetPlacementInternal(GameObject spawningObject, int grouping, ref Vector3 position, ref Quaternion rotation)
|
||||
{
|
||||
List<SpawnPoint> spawnPoints;
|
||||
if (!m_SpawnPointGroupings.TryGetValue(grouping, out spawnPoints)) {
|
||||
Debug.LogError("Error: Unable to find a spawn point with the grouping index " + grouping);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Optionally try to spawn in the first spawn point.
|
||||
SpawnPoint firstSpawnPoint;
|
||||
if (m_FirstSpawnPreferred && m_FirstSpawnPoint.TryGetValue(grouping, out firstSpawnPoint)) {
|
||||
if (firstSpawnPoint.GetPlacement(spawningObject, ref position, ref rotation)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Choose a random spawn point and get the spawn placement.
|
||||
ShuffleSpawnPoints(spawnPoints);
|
||||
var attempt = 0;
|
||||
while (attempt < spawnPoints.Count) {
|
||||
if (spawnPoints[attempt].GetPlacement(spawningObject, ref position, ref rotation)) {
|
||||
return true;
|
||||
}
|
||||
attempt++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shuffles the spawn points list.
|
||||
/// </summary>
|
||||
/// <param name="spawnPoints">The list that should be shuffled.</param>
|
||||
private void ShuffleSpawnPoints(List<SpawnPoint> spawnPoints)
|
||||
{
|
||||
var n = spawnPoints.Count - 1;
|
||||
while (n > 0) {
|
||||
var k = Random.Range(0, n);
|
||||
var temp = spawnPoints[n];
|
||||
spawnPoints[n] = spawnPoints[k];
|
||||
spawnPoints[k] = temp;
|
||||
n--;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the list of spawn points that belong to the specified grouping.
|
||||
/// </summary>
|
||||
/// <param name="grouping">The grouping of spawn points that should be retrieved.</param>
|
||||
/// <returns>The list of spawn points that belong to the specifeid grouping.</returns>
|
||||
public static List<SpawnPoint> GetSpawnPoints(int grouping)
|
||||
{
|
||||
return Instance.GetSpawnPointsInternal(grouping);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal method which returns the list of spawn points that belong to the specified grouping.
|
||||
/// </summary>
|
||||
/// <param name="grouping">The grouping of spawn points that should be retrieved.</param>
|
||||
/// <returns>The list of spawn points that belong to the specifeid grouping.</returns>
|
||||
private List<SpawnPoint> GetSpawnPointsInternal(int grouping)
|
||||
{
|
||||
List<SpawnPoint> spawnPoints;
|
||||
if (!m_SpawnPointGroupings.TryGetValue(grouping, out spawnPoints)) {
|
||||
Debug.LogError("Error: Unable to find a spawn point with the grouping index " + grouping);
|
||||
}
|
||||
return spawnPoints;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the specified spawn point from the manager.
|
||||
/// </summary>
|
||||
/// <param name="spawnPoint">The spawn point to remove.</param>
|
||||
public static void RemoveSpawnPoint(SpawnPoint spawnPoint)
|
||||
{
|
||||
Instance.RemoveSpawnPointInternal(spawnPoint);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal method which removes the specified spawn point from the manager.
|
||||
/// </summary>
|
||||
/// <param name="spawnPoint">The spawn point to remove.</param>
|
||||
private void RemoveSpawnPointInternal(SpawnPoint spawnPoint)
|
||||
{
|
||||
List<SpawnPoint> spawnPoints;
|
||||
if (m_SpawnPointGroupings.TryGetValue(spawnPoint.Grouping, out spawnPoints)) {
|
||||
spawnPoints.Remove(spawnPoint);
|
||||
|
||||
SpawnPoint firstSpawnPoint;
|
||||
if (m_FirstSpawnPoint.TryGetValue(spawnPoint.Grouping, out firstSpawnPoint)) {
|
||||
// Update the first spawn point with the new first spawn point element.
|
||||
if (spawnPoints.Count > 0) {
|
||||
m_FirstSpawnPoint[spawnPoint.Grouping] = spawnPoints[0];
|
||||
} else {
|
||||
m_FirstSpawnPoint.Remove(spawnPoint.Grouping);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The object has been disabled.
|
||||
/// </summary>
|
||||
private void OnDisable()
|
||||
{
|
||||
SceneManager.sceneUnloaded += SceneUnloaded;
|
||||
}
|
||||
|
||||
/// <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;
|
||||
}
|
||||
|
||||
#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: a757f3b80c1feef4a85331d2482be4a1
|
||||
timeCreated: 1526491565
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: -450
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user