This commit is contained in:
2026-06-09 09:18:17 +07:00
parent 3578a2750c
commit 71a096556a
5777 changed files with 6675 additions and 13 deletions

View File

@@ -1,232 +0,0 @@
/// ---------------------------------------------
/// Ultimate Character Controller
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.UltimateCharacterController.Demo.Objects
{
using Opsive.Shared.Game;
using Opsive.UltimateCharacterController.Character;
using Opsive.UltimateCharacterController.Game;
using Opsive.UltimateCharacterController.Utility;
using UnityEngine;
/// <summary>
/// Controls the animation for the door.
/// </summary>
public class Door : MonoBehaviour
{
private const string c_ColorText = "_Color";
private const string c_EmissionColor = "_EmissionColor";
[Tooltip("The LayerMask of the character.")]
[SerializeField] protected LayerMask m_LayerMask = 1 << LayerManager.Character;
[Tooltip("Is the door locked?")]
[SerializeField] protected bool m_Locked = false;
[Tooltip("Should the door be permanently locked? A permanently locked door cannot be opened by the DemoManager.")]
[UnityEngine.Serialization.FormerlySerializedAs("m_PermantlyLocked")]
[SerializeField] protected bool m_PermanentlyLocked;
[Tooltip("Should the door be opened at the start?")]
[SerializeField] protected bool m_OpenAtStart;
[Tooltip("Should the door close when the character leaves the trigger?")]
[SerializeField] protected bool m_CloseOnTriggerExit = true;
[Tooltip("The material that can change colors when the door is locked or unlcoked.")]
[SerializeField] protected Material m_StatusMaterial;
[Tooltip("The color of the closed door.")]
[SerializeField] protected Color m_LockedColor = Color.red;
[Tooltip("The color of the opened door.")]
[SerializeField] protected Color m_UnlockedColor = Color.green;
[Tooltip("The AudioClip that should play when the door is opened.")]
[SerializeField] protected AudioClip m_OpenAudioClip;
[Tooltip("The AudioClip that should play when the door is closed.")]
[SerializeField] protected AudioClip m_CloseAudioClip;
public bool Locked { get { return m_Locked; }
set {
if (m_Locked == value) {
return;
}
m_Locked = value;
UpdateDoorStatus();
if (m_Locked && m_Open) {
OpenClose(false, true, true);
} else if (!m_Locked && m_CharacterCount > 0) {
m_Open = false;
OpenClose(true, true, true);
}
}
}
public bool PermanentlyLocked { get { return m_PermanentlyLocked; } set { m_PermanentlyLocked = value; } }
public bool CloseOnTriggerExit { get { return m_CloseOnTriggerExit; } set { m_CloseOnTriggerExit = value; } }
private static int s_OpenHash = Animator.StringToHash("Open");
private Animator m_Animator;
private AudioSource m_AudioSource;
private bool m_ManagerOpen;
private bool m_Open;
private int m_CharacterCount;
private Material[] m_StatusMaterials;
/// <summary>
/// Initializes the default values.
/// </summary>
private void Awake()
{
m_Animator = GetComponent<Animator>();
m_AudioSource = GetComponent<AudioSource>();
var demoManager = FindObjectOfType<DemoManager>();
if (demoManager != null) {
demoManager.RegisterDoor(this);
}
// Cache the light materials so they can be changed.
var renderers = GetComponentsInChildren<Renderer>();
for (int i = 0; i < renderers.Length; ++i) {
var materials = renderers[i].sharedMaterials;
for (int j = 0; j < materials.Length; ++j) {
// The shared material will allow for a valid comparison. The instance material should not be compared.
if (materials[j] != m_StatusMaterial) {
continue;
}
if (m_StatusMaterials == null) {
m_StatusMaterials = new Material[1];
} else {
System.Array.Resize(ref m_StatusMaterials, m_StatusMaterials.Length + 1);
}
// Cache the instance material so each status light operates independently.
m_StatusMaterials[m_StatusMaterials.Length - 1] = renderers[i].materials[j];
}
}
// Permantly locked doors cannot be opened.
if (m_PermanentlyLocked) {
m_Locked = true;
}
if (m_OpenAtStart) {
OpenClose(true, true, false);
}
UpdateDoorStatus();
}
/// <summary>
/// Updates the door status material.
/// </summary>
private void UpdateDoorStatus()
{
if (m_StatusMaterials != null) {
for (int i = 0; i < m_StatusMaterials.Length; ++i) {
var locked = m_Locked || m_PermanentlyLocked;
m_StatusMaterials[i].SetColor(c_ColorText, locked ? m_LockedColor : m_UnlockedColor);
m_StatusMaterials[i].SetColor(c_EmissionColor, locked ? m_LockedColor : m_UnlockedColor);
}
}
}
/// <summary>
/// Closes the door.
/// </summary>
public void Close()
{
OpenClose(false, false, false);
}
/// <summary>
/// Opens the door.
/// </summary>
public void Open()
{
OpenClose(true, false, false);
}
/// <summary>
/// Opens or closes the door.
/// </summary>
/// <param name="open">Should the door be opened?</param>
/// <param name="fromManager">Is the door being opened or closed from the DemoManager?</param>
/// <param name="playAudio">Should the door open/close audio be played?</param>
public void OpenClose(bool open, bool fromManager, bool playAudio)
{
// Permanently locked doors cannot be opened.
if (m_PermanentlyLocked && open) {
return;
}
// Don't close the door if the manager opened the door.
if (!open && !fromManager && m_ManagerOpen) {
return;
}
// The door can't open if it is already open.
if (m_Open == open) {
return;
}
if (open) {
// The door can't open if it's locked.
if (m_Locked) {
// The manager can unlock the door.
if (fromManager) {
m_Locked = false;
} else {
return;
}
}
}
m_Open = open;
m_Animator.SetBool(s_OpenHash, open);
if (playAudio && m_AudioSource != null) {
if (open && m_OpenAudioClip != null) {
m_AudioSource.clip = m_OpenAudioClip;
m_AudioSource.Play();
} else if (!open && m_CloseAudioClip != null) {
m_AudioSource.clip = m_CloseAudioClip;
m_AudioSource.Play();
}
}
// The manager will override the trigger settings.
if (fromManager) {
m_ManagerOpen = open;
}
}
/// <summary>
/// Open the door if the character enters the trigger.
/// </summary>
/// <param name="other">The collider which entered the trigger.</param>
private void OnTriggerEnter(Collider other)
{
if (!MathUtility.InLayerMask(other.gameObject.layer, m_LayerMask) || other.gameObject.GetCachedParentComponent<UltimateCharacterLocomotion>() == null) {
return;
}
m_CharacterCount++;
OpenClose(true, false, true);
}
/// <summary>
/// Close the door if the character leaves the trigger.
/// </summary>
/// <param name="other">The collider which left the trigger.</param>
private void OnTriggerExit(Collider other)
{
if (!MathUtility.InLayerMask(other.gameObject.layer, m_LayerMask) || other.gameObject.GetCachedParentComponent<UltimateCharacterLocomotion>() == null) {
return;
}
// The door should only close when all characters are no longer within the trigger.
m_CharacterCount--;
if (m_CharacterCount == 0 && m_Animator.GetBool(s_OpenHash) && m_CloseOnTriggerExit) {
OpenClose(false, false, true);
}
}
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: aed02a211391a924f8e90218cdd5641a
timeCreated: 1520644758
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 10100
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,163 +0,0 @@
/// ---------------------------------------------
/// Ultimate Character Controller
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.UltimateCharacterController.Demo.Objects
{
using Opsive.Shared.Events;
using Opsive.Shared.Game;
using Opsive.UltimateCharacterController.Traits;
using Opsive.UltimateCharacterController.SurfaceSystem;
using Opsive.UltimateCharacterController.Utility;
using UnityEngine;
/// <summary>
/// A flammable crate will play a flame particle if it gets hit by a fireball.
/// </summary>
public class FlammableCrate : MonoBehaviour
{
[Tooltip("The SurfaceImpact that causes the flame to start.")]
[SerializeField] protected SurfaceImpact m_FlameImpact;
[Tooltip("A reference to the flame particle that should start when the fireball collides with the crate.")]
[SerializeField] protected GameObject m_FlamePrefab;
[Tooltip("The crate that is spawned with the wood shreds.")]
[SerializeField] protected GameObject m_DestroyedCrate;
[Tooltip("The interval that the object should have its health reduced.")]
[SerializeField] protected MinMaxFloat m_HealthReductionInterval = new MinMaxFloat(0.2f, 0.8f);
[Tooltip("The amount that the object should be damaged on each interval.")]
[SerializeField] protected MinMaxFloat m_DamageAmount = new MinMaxFloat(4, 10);
[Tooltip("The amount of time it takes for the wood shreds to be removed.")]
[SerializeField] protected MinMaxFloat m_WoodShreadRemovalTime = new MinMaxFloat(5, 7);
[Tooltip("The amount to fade out the AudioSource.")]
[SerializeField] protected float m_AudioSourceFadeAmount = 0.05f;
private BoxCollider m_DamageTrigger;
private Health m_Health;
private ParticleSystem m_FlameParticle;
private AudioSource m_FlameParticleAudioSource;
private GameObject m_SpawnedCrate;
private ScheduledEventBase m_StopEvent;
private float m_StartHealth;
/// <summary>
/// Initialize the default values.
/// </summary>
private void Awake()
{
m_Health = GetComponent<Health>();
m_StartHealth = m_Health.HealthValue;
// A box collider will be a trigger which damages the character if they stop within the flames.
var colliders = GetComponents<BoxCollider>();
for (int i = 0; i < colliders.Length; ++i) {
if (!colliders[i].isTrigger) {
continue;
}
m_DamageTrigger = colliders[i];
m_DamageTrigger.enabled = false;
break;
}
EventHandler.RegisterEvent<RaycastHit, SurfaceImpact>(gameObject, "OnMagicCastCollision", MagicCastCollision);
}
/// <summary>
/// The crate has been enabled.
/// </summary>
private void OnEnable()
{
StopParticles();
if (m_Health.HealthValue != m_StartHealth) {
m_Health.Heal(m_StartHealth - m_Health.HealthValue);
}
}
/// <summary>
/// The magic cast has collided with another object.
/// </summary>
/// <param name="hit">The raycast that caused the impact.</param>
/// <param name="surfaceImpact">The type of particle that collided with the object.</param>
private void MagicCastCollision(RaycastHit hit, SurfaceImpact surfaceImpact)
{
if (m_FlameParticle != null || (m_FlameImpact != null && m_FlameImpact != surfaceImpact)) {
return;
}
// A fireball has collided with the crate. Start the flame.
var flamePrefab = ObjectPool.Instantiate(m_FlamePrefab, transform.position, transform.rotation);
m_FlameParticle = flamePrefab.GetComponent<ParticleSystem>();
m_FlameParticleAudioSource = flamePrefab.GetCachedComponent<AudioSource>();
m_FlameParticleAudioSource.volume = 1;
m_DamageTrigger.enabled = true;
// The crate should be destroyed by the flame.
ReduceHealth();
}
/// <summary>
/// Reduces the health by the damage amount.
/// </summary>
private void ReduceHealth()
{
m_Health.Damage(m_DamageAmount.RandomValue);
if (m_Health.IsAlive()) {
// Keep reducing the object's health until is is no longer alive.
Scheduler.Schedule(m_HealthReductionInterval.RandomValue, ReduceHealth);
} else {
// After the object is no longer alive spawn some wood shreds. These shreds should be cleaned up after a random
// amount of time.
m_SpawnedCrate = ObjectPool.Instantiate(m_DestroyedCrate, transform.position, transform.rotation);
var maxDestroyTime = 0f;
for (int i = 0; i < m_SpawnedCrate.transform.childCount; ++i) {
var destroyTime = m_WoodShreadRemovalTime.RandomValue;
if (destroyTime > maxDestroyTime) {
maxDestroyTime = destroyTime;
}
Destroy(m_SpawnedCrate.transform.GetChild(i).gameObject, destroyTime);
}
m_StopEvent = Scheduler.Schedule(maxDestroyTime, StopParticles);
}
}
/// <summary>
/// The crate has been destroyed. Stop the particles.
/// </summary>
private void StopParticles()
{
if (m_StopEvent == null) {
return;
}
Scheduler.Cancel(m_StopEvent);
m_StopEvent = null;
m_DamageTrigger.enabled = false;
m_FlameParticle.Stop(true);
m_FlameParticle = null;
Scheduler.Schedule(0.2f, FadeAudioSource);
}
/// <summary>
/// Fades the flame audio source.
/// </summary>
private void FadeAudioSource()
{
m_FlameParticleAudioSource.volume -= m_AudioSourceFadeAmount;
if (m_FlameParticleAudioSource.volume > 0) {
Scheduler.Schedule(0.2f, FadeAudioSource);
}
}
/// <summary>
/// The object has been destroyed.
/// </summary>
private void OnDestroy()
{
EventHandler.UnregisterEvent<RaycastHit, SurfaceImpact>(gameObject, "OnMagicCastCollision", MagicCastCollision);
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: ac8e2615485f28345a3d5afb697b94eb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,75 +0,0 @@
/// ---------------------------------------------
/// Ultimate Character Controller
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.UltimateCharacterController.Demo.Objects
{
using Opsive.Shared.Events;
using Opsive.UltimateCharacterController.Traits;
using UnityEngine;
/// <summary>
/// A healing crate will update its material if it is healed.
/// </summary>
public class HealingCrate : MonoBehaviour
{
[Tooltip("The material representing a damaged crate.")]
[SerializeField] protected Material m_DamagedMaterial;
[Tooltip("The material representing a healed crate.")]
[SerializeField] protected Material m_HealedMaterial;
[Tooltip("The crate is healed when the Health attribute is greater than the specified value.")]
[SerializeField] protected float m_HealedAttributeValue = 40;
private Renderer m_Renderer;
private Attribute m_HealthAttribute;
private float m_StartingAttributeValue;
/// <summary>
/// Initialize the default values.
/// </summary>
private void Awake()
{
m_Renderer = gameObject.GetComponent<Renderer>();
var attributeManager = gameObject.GetComponent<AttributeManager>();
m_HealthAttribute = attributeManager.GetAttribute("Health");
m_StartingAttributeValue = m_HealthAttribute.Value;
EventHandler.RegisterEvent<Attribute>(gameObject, "OnAttributeUpdateValue", OnUpdateValue);
}
/// <summary>
/// The attribute value has beeen updated.
/// </summary>
/// <param name="attribute">The attribute that has been updated.</param>
private void OnUpdateValue(Attribute attribute)
{
if (attribute != m_HealthAttribute) {
return;
}
if (attribute.Value >= m_HealedAttributeValue) {
m_Renderer.sharedMaterial = m_HealedMaterial;
} else {
m_Renderer.sharedMaterial = m_DamagedMaterial;
}
}
/// <summary>
/// Revert the health attribute value when the object is disabled.
/// </summary>
private void OnDisable()
{
m_HealthAttribute.Value = m_StartingAttributeValue;
}
/// <summary>
/// The object has been destroyed.
/// </summary>
private void OnDestroy()
{
EventHandler.UnregisterEvent<Attribute>(gameObject, "OnAttributeUpdateValue", OnUpdateValue);
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: f9291b59f45e20e47aaee07384c8f32c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,59 +0,0 @@
/// ---------------------------------------------
/// Ultimate Character Controller
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.UltimateCharacterController.Demo.Objects
{
using Opsive.UltimateCharacterController.Traits;
using UnityEngine;
/// <summary>
/// Instantly kills the character if the character moves beneath the moving platform as it is moving down.
/// </summary>
public class MovingPlatformDeathZone : MonoBehaviour
{
private Transform m_Transform;
private Vector3 m_PrevPosition;
private bool m_DownwardMovement;
/// <summary>
/// Initialize the default values.
/// </summary>
private void Awake()
{
m_Transform = transform;
m_PrevPosition = m_Transform.position;
}
/// <summary>
/// Detect if the platform is moving downward.
/// </summary>
private void FixedUpdate()
{
m_DownwardMovement = m_Transform.InverseTransformDirection(m_Transform.position - m_PrevPosition).y < 0;
m_PrevPosition = m_Transform.position;
}
/// <summary>
/// An
/// </summary>
/// <param name="other"></param>
private void OnTriggerEnter(Collider other)
{
// The platform has to be moving downward in order to kill the player.
if (!m_DownwardMovement) {
return;
}
// Kill the character.
var health = other.GetComponentInParent<CharacterHealth>();
if (health == null) {
return;
}
health.ImmediateDeath(m_Transform.position, Vector3.down, (m_Transform.position - m_PrevPosition).magnitude);
}
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 53cde399db556304bb9a0a4f6bb87c49
timeCreated: 1543825614
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,44 +0,0 @@
/// ---------------------------------------------
/// Ultimate Character Controller
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.UltimateCharacterController.Demo.Objects
{
using UnityEngine;
/// <summary>
/// A particle stream crate will reset its position when the crate reactivates.
/// </summary>
public class ParticleStreamCrate : MonoBehaviour
{
private Rigidbody m_Rigibody;
private Vector3 m_Position;
private Quaternion m_Rotation;
/// <summary>
/// Initialize the default values.
/// </summary>
private void Awake()
{
m_Rigibody = GetComponent<Rigidbody>();
m_Position = transform.position;
m_Rotation = transform.rotation;
}
/// <summary>
/// The crate has been enabled.
/// </summary>
private void OnEnable()
{
m_Rigibody.linearVelocity = Vector3.zero;
m_Rigibody.angularVelocity = Vector3.zero;
transform.position = m_Position;
transform.rotation = m_Rotation;
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 3af4c96da50a1da4f8996cf0396d1062
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,43 +0,0 @@
/// ---------------------------------------------
/// Ultimate Character Controller
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.UltimateCharacterController.Demo.Objects
{
using UnityEngine;
/// <summary>
/// The ShieldBubble will play an enlarging animation when the object spawns.
/// </summary>
public class ShieldBubble : MonoBehaviour
{
private Transform m_Transform;
private Animator m_Animator;
private Vector3 m_Scale;
private int m_DefaultStateHash;
/// <summary>
/// Initializes the default values.
/// </summary>
private void Awake()
{
m_Transform = GetComponent<Transform>();
m_Animator = GetComponent<Animator>();
m_Scale = m_Transform.localScale;
m_DefaultStateHash = Animator.StringToHash("EnlargingBubble");
}
/// <summary>
/// Reset the changed values.
/// </summary>
private void OnEnable()
{
m_Animator.Play(m_DefaultStateHash, 0, 0);
m_Transform.localScale = m_Scale;
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 3a1c0907934723149b1704eb399e2163
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,85 +0,0 @@
/// ---------------------------------------------
/// Ultimate Character Controller
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.UltimateCharacterController.Demo.Objects
{
using Opsive.UltimateCharacterController.Character;
using Opsive.UltimateCharacterController.Character.Abilities;
using Opsive.UltimateCharacterController.Game;
using Opsive.UltimateCharacterController.Utility;
using UnityEngine;
/// <summary>
/// Moves the platform between two points. This gives an example of using the Move With Object ability and moving on an object that is
/// updated outside of the Kinematic Object Manager update loop.
/// </summary>
public class SimplePlatform : MonoBehaviour
{
[Tooltip("The position that the platform should move towards when the character is not on top of it.")]
[SerializeField] protected Vector3 m_RestingPosition;
[Tooltip("The position that the platform should move towards when the character is on top of it.")]
[SerializeField] protected Vector3 m_ActivePosition;
[Tooltip("The speed that the platform should move.")]
[SerializeField] protected float m_MoveSpeed = 0.05f;
private Transform m_Transform;
private MoveWithObject m_MoveWithObjectAbility;
/// <summary>
/// Initialize the default values.
/// </summary>
private void Awake()
{
m_Transform = transform;
}
/// <summary>
/// Teleport the character to the specified destination.
/// </summary>
/// <param name="other">The collider that entered the trigger. May or may not be a character.</param>
private void OnTriggerEnter(Collider other)
{
if (!MathUtility.InLayerMask(other.gameObject.layer, 1 << LayerManager.Character)) {
return;
}
UltimateCharacterLocomotion characterLocomotion;
if ((characterLocomotion = other.GetComponentInParent<UltimateCharacterLocomotion>()) == null) {
return;
}
m_MoveWithObjectAbility = characterLocomotion.GetAbility<MoveWithObject>();
if (m_MoveWithObjectAbility == null) {
return;
}
m_MoveWithObjectAbility.Target = m_Transform;
}
/// <summary>
/// Moves the platform.
/// </summary>
private void FixedUpdate()
{
// Move towards the active position when the ability reference is not null. This will be set within OnTriggerEnter/Exit.
m_Transform.position = Vector3.MoveTowards(m_Transform.position, (m_MoveWithObjectAbility != null ? m_ActivePosition : m_RestingPosition), m_MoveSpeed);
}
/// <summary>
/// An object has exited the trigger.
/// </summary>
/// <param name="other">The collider that entered the trigger. May or may not be a character.</param>
private void OnTriggerExit(Collider other)
{
if (!MathUtility.InLayerMask(other.gameObject.layer, 1 << LayerManager.Character) || m_MoveWithObjectAbility == null) {
return;
}
m_MoveWithObjectAbility.Target = null;
m_MoveWithObjectAbility = null;
}
}
}

View File

@@ -1,13 +0,0 @@
fileFormatVersion: 2
guid: e78c4b081a5326a4f9b1b2280b21fcff
timeCreated: 1554810719
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,102 +0,0 @@
/// ---------------------------------------------
/// Ultimate Character Controller
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.UltimateCharacterController.Demo.Objects
{
using Opsive.UltimateCharacterController.Character;
using Opsive.UltimateCharacterController.Game;
using Opsive.UltimateCharacterController.Utility;
using UnityEngine;
/// <summary>
/// Teleports the character to the specified destination.
/// </summary>
[RequireComponent(typeof(BoxCollider))]
public class Teleporter : MonoBehaviour
{
[Tooltip("The location that the character will teleport to.")]
[SerializeField] protected Transform m_Destination;
[Tooltip("Should the character's animator be snapped when teleporting to the destination?")]
[SerializeField] protected bool m_SnapAnimator;
[Tooltip("The LayerMask of the character.")]
[SerializeField] protected LayerMask m_LayerMask = 1 << LayerManager.Character;
[Tooltip("The AudioClip that should play when the character is teleported.")]
[SerializeField] protected AudioClip m_TeleportAudioClip;
[Tooltip("The name of the state that should activate when the character teleports.")]
[SerializeField] protected string m_StateName;
#if UNITY_EDITOR
[Tooltip("The color to draw the editor gizmo in (editor only).")]
[SerializeField] protected Color m_GizmoColor = new Color(0, 0, 1, 0.3f);
#endif
public Transform Destination { get { return m_Destination; } set { m_Destination = value; } }
#if UNITY_EDITOR
public Color GizmoColor { get { return m_GizmoColor; } set { m_GizmoColor = value; } }
#endif
private AudioSource m_AudioSource;
private bool m_IgnoreCharacterEnter;
public bool IgnoreCharacterEnter { set { m_IgnoreCharacterEnter = true; } }
/// <summary>
/// Initialize the default values.
/// </summary>
private void Awake()
{
m_AudioSource = GetComponent<AudioSource>();
}
/// <summary>
/// Teleport the character to the specified destination.
/// </summary>
/// <param name="other">The collider that entered the trigger. May or may not be a character.</param>
private void OnTriggerEnter(Collider other)
{
if (!MathUtility.InLayerMask(other.gameObject.layer, m_LayerMask) || m_IgnoreCharacterEnter) {
return;
}
UltimateCharacterLocomotion characterLocomotion;
if ((characterLocomotion = other.GetComponentInParent<UltimateCharacterLocomotion>()) != null) {
// Do not allow teleportation if the Drive or Ride abilities are active.
if (characterLocomotion.IsAbilityTypeActive<UltimateCharacterController.Character.Abilities.Drive>() ||
characterLocomotion.IsAbilityTypeActive<UltimateCharacterController.Character.Abilities.Ride>()) {
return;
}
var destinationTeleporter = m_Destination.GetComponentInParent<Teleporter>();
if (destinationTeleporter != null) {
destinationTeleporter.IgnoreCharacterEnter = true;
}
characterLocomotion.SetPositionAndRotation(m_Destination.position, m_Destination.rotation, m_SnapAnimator);
if (m_AudioSource != null && m_TeleportAudioClip != null) {
m_AudioSource.clip = m_TeleportAudioClip;
m_AudioSource.Play();
}
if (!string.IsNullOrEmpty(m_StateName)) {
StateSystem.StateManager.SetState(characterLocomotion.gameObject, m_StateName, true);
}
}
}
/// <summary>
/// An object has exited the trigger.
/// </summary>
/// <param name="other">The collider that entered the trigger. May or may not be a character.</param>
private void OnTriggerExit(Collider other)
{
if (!MathUtility.InLayerMask(other.gameObject.layer, m_LayerMask)) {
return;
}
m_IgnoreCharacterEnter = false;
}
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: f6fbe57b61f6ad9459762bb36de41799
timeCreated: 1510845725
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,206 +0,0 @@
/// ---------------------------------------------
/// Ultimate Character Controller
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.UltimateCharacterController.Demo.Objects
{
using Opsive.Shared.Game;
using Opsive.UltimateCharacterController.Character;
using Opsive.UltimateCharacterController.Game;
#if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER
using Opsive.UltimateCharacterController.Networking;
using Opsive.UltimateCharacterController.Networking.Game;
#endif
using Opsive.UltimateCharacterController.Objects;
using Opsive.UltimateCharacterController.SurfaceSystem;
using Opsive.UltimateCharacterController.Traits;
using Opsive.UltimateCharacterController.Utility;
using UnityEngine;
/// <summary>
/// A simple turret which will fire a projectile towards the character. This turret is setup for the demo scene and will likely require modifications if used in other areas.
/// </summary>
public class Turret : MonoBehaviour
{
[Tooltip("The object that rotates on the turret.")]
[SerializeField] protected Transform m_TurretHead;
[Tooltip("The speed at which the head rotates.")]
[SerializeField] protected float m_RotationSpeed = 5;
[Tooltip("The location that the projectile should be fired.")]
[SerializeField] protected Transform m_FireLocation;
[Tooltip("The distance in which the turret can start firing.")]
[SerializeField] protected float m_FireRange = 10;
[Tooltip("The delay until the turret will fire again.")]
[SerializeField] protected float m_FireDelay = 0.5f;
[Tooltip("The projectile that is fired.")]
[SerializeField] protected GameObject m_Projectile;
[Tooltip("The magnitude of the projectile velocity when fired. The direction is determined by the fire direction.")]
[SerializeField] protected float m_VelocityMagnitude = 10;
[Tooltip("A LayerMask of the layers that can be hit when fired at.")]
[SerializeField] protected LayerMask m_ImpactLayers = ~(1 << LayerManager.IgnoreRaycast | 1 << LayerManager.TransparentFX | 1 << LayerManager.UI | 1 << LayerManager.Overlay);
[Tooltip("The amount of damage to apply to the hit object.")]
[SerializeField] protected float m_DamageAmount = 10;
[Tooltip("How much force to apply to the hit object.")]
[SerializeField] protected float m_ImpactForce = 0.05f;
[Tooltip("The number of frames to add the force to.")]
[SerializeField] protected int m_ImpactForceFrames = 1;
[Tooltip("The Surface Impact triggered when the weapon hits an object.")]
[SerializeField] protected SurfaceImpact m_SurfaceImpact;
[Tooltip("Optionally specify a muzzle flash that should appear when the turret is fired.")]
[SerializeField] protected GameObject m_MuzzleFlash;
[Tooltip("The location that the muzzle flash should spawn.")]
[SerializeField] protected Transform m_MuzzleFlashLocation;
[Tooltip("Optionally specify an audio clip that should play when the turret is fired.")]
[SerializeField] protected AudioClip m_FireAudioClip;
private GameObject m_GameObject;
private Transform m_Transform;
private AudioSource m_AudioSource;
#if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER
private INetworkInfo m_NetworkInfo;
#endif
private Transform m_Target;
private Health m_Health;
private float m_LastFireTime;
/// <summary>
/// Initialize the default values.
/// </summary>
private void Awake()
{
m_GameObject = gameObject;
m_Transform = transform;
m_AudioSource = GetComponent<AudioSource>();
#if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER
m_NetworkInfo = GetComponent<INetworkInfo>();
#endif
// A turret head is required.
if (m_TurretHead == null) {
m_TurretHead = m_Transform;
}
}
#if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER
/// <summary>
/// Determine if the object should be enabled on the network.
/// </summary>
private void Start()
{
if (m_NetworkInfo != null && !m_NetworkInfo.IsLocalPlayer()) {
enabled = false;
}
}
#endif
/// <summary>
/// The turret has been enabled.
/// </summary>
private void OnEnable()
{
m_LastFireTime = Time.time;
}
/// <summary>
/// Rotates the turret head and attacks if the character is within range.
/// </summary>
private void Update()
{
if (m_Target == null) {
return;
}
RotateTowardsTarget();
CheckForAttack();
}
/// <summary>
/// Keep facing the target so the turret can fire at any time.
/// </summary>
public void RotateTowardsTarget()
{
var targetRotation = Quaternion.Euler(0, Quaternion.LookRotation(m_TurretHead.position - m_Target.position).eulerAngles.y, 0);
m_TurretHead.rotation = Quaternion.Slerp(m_TurretHead.rotation, targetRotation, m_RotationSpeed * Time.deltaTime);
}
/// <summary>
/// Determine if the turret can attack.
/// </summary>
public void CheckForAttack()
{
// The turret can attack if it hasn't fired recently and the target is in front of the turret.
if (m_LastFireTime + m_FireDelay < Time.time && (m_Transform.position - m_Target.position).magnitude < m_FireRange && (m_Health == null || m_Health.Value > 0)) {
Fire();
}
}
/// <summary>
/// Does the actual fire.
/// </summary>
public void Fire()
{
m_LastFireTime = Time.time;
// Spawn a projectile which will move in the direction that the turret is facing
var projectile = ObjectPool.Instantiate(m_Projectile, m_FireLocation.position, m_Transform.rotation).GetCachedComponent<Projectile>();
projectile.Initialize(m_FireLocation.forward * m_VelocityMagnitude, Vector3.zero, m_DamageAmount, m_ImpactForce, m_ImpactForceFrames,
m_ImpactLayers, string.Empty, 0, m_SurfaceImpact, m_GameObject);
#if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER
if (m_NetworkInfo != null) {
NetworkObjectPool.NetworkSpawn(m_Projectile, projectile.gameObject, true);
}
#endif
// Spawn a muzzle flash.
if (m_MuzzleFlash) {
var muzzleFlash = ObjectPool.Instantiate(m_MuzzleFlash, m_MuzzleFlashLocation.position, m_MuzzleFlashLocation.rotation, m_Transform).GetCachedComponent<MuzzleFlash>();
muzzleFlash.Show(null, 0, true, null);
}
// Play a firing sound.
if (m_FireAudioClip != null) {
m_AudioSource.clip = m_FireAudioClip;
m_AudioSource.Play();
}
}
/// <summary>
/// An object has entered the trigger.
/// </summary>
/// <param name="other">The object that entered the trigger.</param>
private void OnTriggerEnter(Collider other)
{
if (m_Target != null || !MathUtility.InLayerMask(other.gameObject.layer, 1 << LayerManager.Character)) {
return;
}
var characterLocomotion = other.GetComponentInParent<UltimateCharacterLocomotion>();
if (characterLocomotion == null) {
return;
}
m_Target = characterLocomotion.transform;
m_Health = characterLocomotion.GetComponent<Health>();
}
/// <summary>
/// An object has exited the trigger.
/// </summary>
/// <param name="other">The collider that exited the trigger.</param>
private void OnTriggerExit(Collider other)
{
if (other.gameObject != m_Target) {
return;
}
m_Target = null;
}
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 14f6d55780b9f914c8d4c40bb2fbf030
timeCreated: 1525258500
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: