update
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
/// ---------------------------------------------
|
||||
/// Ultimate Character Controller
|
||||
/// Copyright (c) Opsive. All Rights Reserved.
|
||||
/// https://www.opsive.com
|
||||
/// ---------------------------------------------
|
||||
|
||||
namespace Opsive.UltimateCharacterController.ThirdPersonController.Character.Abilities
|
||||
{
|
||||
using Opsive.Shared.Events;
|
||||
using Opsive.UltimateCharacterController.Character.Abilities;
|
||||
using Opsive.UltimateCharacterController.Character.MovementTypes;
|
||||
using Opsive.UltimateCharacterController.Utility;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Follows a path specified by the 2.5D movement type. Ensures the character stays at a consistant horizontal offset.
|
||||
/// </summary>
|
||||
[DefaultStartType(AbilityStartType.Manual)]
|
||||
public class FollowPseudo3DPath : Ability
|
||||
{
|
||||
public override bool IsConcurrent { get { return true; } }
|
||||
|
||||
private MovementTypes.Pseudo3D m_Pseudo3DMovementType;
|
||||
private int m_PathIndex;
|
||||
private float m_Offset;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the default values.
|
||||
/// </summary>
|
||||
public override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
EventHandler.RegisterEvent<MovementType, bool>(m_GameObject, "OnCharacterChangeMovementType", OnCharacterChangeMovementType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The specified movement type has been activated or deactivated.
|
||||
/// </summary>
|
||||
/// <param name="movementType">The movement type that has been activated or deactivated.</param>
|
||||
/// <param name="active">Is the movement type active?</param>
|
||||
private void OnCharacterChangeMovementType(MovementType movementType, bool active)
|
||||
{
|
||||
if (!(movementType is MovementTypes.Pseudo3D)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsActive && active) {
|
||||
// If the movement type is active then the ability should start if a path exists.
|
||||
var pseudo3DMovementType = movementType as MovementTypes.Pseudo3D;
|
||||
if (pseudo3DMovementType.Path != null) {
|
||||
StartAbility();
|
||||
return;
|
||||
}
|
||||
} else if (IsActive && !active) {
|
||||
// If the movement type is no longer active then the ability should stop.
|
||||
StopAbility();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the ablity is tried to be started. If false is returned then the ability will not be started.
|
||||
/// </summary>
|
||||
/// <returns>True if the ability can be started.</returns>
|
||||
public override bool CanStartAbility()
|
||||
{
|
||||
if (!base.CanStartAbility()) {
|
||||
return false;
|
||||
}
|
||||
return (m_CharacterLocomotion.ActiveMovementType is MovementTypes.Pseudo3D) && (m_CharacterLocomotion.ActiveMovementType as MovementTypes.Pseudo3D).Path != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The ability has started.
|
||||
/// </summary>
|
||||
protected override void AbilityStarted()
|
||||
{
|
||||
base.AbilityStarted();
|
||||
|
||||
m_Pseudo3DMovementType = m_CharacterLocomotion.ActiveMovementType as MovementTypes.Pseudo3D;
|
||||
|
||||
// Store the horizontal offset between the character and the path. This offset will ensure the character stays at a constant horizontal position.
|
||||
var pathRotation = Quaternion.LookRotation(m_Pseudo3DMovementType.Path.GetTangent(m_Transform.position, ref m_PathIndex));
|
||||
var closestPoint = m_Pseudo3DMovementType.Path.GetClosestPoint(m_Transform.position, ref m_PathIndex);
|
||||
m_Offset = MathUtility.InverseTransformPoint(m_Transform.position, pathRotation, closestPoint).x;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop the ability if the path no longer exists.
|
||||
/// </summary>
|
||||
public override void Update()
|
||||
{
|
||||
if (m_Pseudo3DMovementType.Path == null) {
|
||||
StopAbility();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the controller's position values.
|
||||
/// </summary>
|
||||
public override void UpdatePosition()
|
||||
{
|
||||
// Retireve the closest point and tangent of the path at the target position. This position will be used when determining if the character needs to change
|
||||
// horizontal offsets.
|
||||
var targetPosition = m_Transform.position + m_CharacterLocomotion.MoveDirection;
|
||||
var closestPoint = m_Pseudo3DMovementType.Path.GetClosestPoint(targetPosition, ref m_PathIndex);
|
||||
var pathRotation = Quaternion.LookRotation(m_Pseudo3DMovementType.Path.GetTangent(targetPosition, ref m_PathIndex));
|
||||
|
||||
// Update the offset if the character can move along the relative x axis. The depth term is used in relation to the camera's depth axis.
|
||||
if (m_Pseudo3DMovementType.AllowDepthMovement && m_CharacterLocomotion.RawInputVector.y != 0) {
|
||||
m_Offset -= MathUtility.InverseTransformDirection(m_CharacterLocomotion.MotorThrottle, pathRotation).x;
|
||||
}
|
||||
|
||||
// Update the target position based on the difference between the current offset and the stored offset.
|
||||
var offset = MathUtility.InverseTransformPoint(targetPosition, pathRotation, closestPoint).x;
|
||||
targetPosition = MathUtility.TransformPoint(targetPosition, pathRotation, Vector3.right * (offset - m_Offset));
|
||||
m_CharacterLocomotion.MoveDirection = targetPosition - m_Transform.position;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The character has been destroyed.
|
||||
/// </summary>
|
||||
public override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
EventHandler.UnregisterEvent<MovementType, bool>(m_GameObject, "OnCharacterChangeMovementType", OnCharacterChangeMovementType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f2b3cd302b28cf54ebb3823309cb9bbf
|
||||
timeCreated: 1528491971
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 415769a5d0a2f5b4baea70728223b0d8
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,179 @@
|
||||
/// ---------------------------------------------
|
||||
/// Ultimate Character Controller
|
||||
/// Copyright (c) Opsive. All Rights Reserved.
|
||||
/// https://www.opsive.com
|
||||
/// ---------------------------------------------
|
||||
|
||||
namespace Opsive.UltimateCharacterController.ThirdPersonController.Character.Abilities.Items
|
||||
{
|
||||
using Opsive.Shared.Events;
|
||||
using Opsive.Shared.Game;
|
||||
using Opsive.UltimateCharacterController.Character.Abilities;
|
||||
using Opsive.UltimateCharacterController.Character.Abilities.Items;
|
||||
using Opsive.UltimateCharacterController.Game;
|
||||
using Opsive.UltimateCharacterController.Utility;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Pulls back the item if the character gets too close to a wall. This will prevent the item from clipping with the wall.
|
||||
/// </summary>
|
||||
[DefaultStopType(AbilityStopType.Automatic)]
|
||||
public class ItemPullback : ItemAbility
|
||||
{
|
||||
[Tooltip("The collider used to detect when the character is near an object and should pull back the items.")]
|
||||
[SerializeField] protected Collider m_Collider;
|
||||
[Tooltip("The layers that the collider can collide with.")]
|
||||
[SerializeField] protected LayerMask m_CollisionLayers = ~(1 << LayerManager.IgnoreRaycast | 1 << LayerManager.TransparentFX | 1 << LayerManager.SubCharacter |
|
||||
1 << LayerManager.Overlay | 1 << LayerManager.VisualEffect | 1 << LayerManager.Water);
|
||||
[Tooltip("The maximum number of collisions that should be detected by the collider.")]
|
||||
[SerializeField] protected int m_MaxCollisionCount = 5;
|
||||
|
||||
public Collider Collider { get { return m_Collider; } set { m_Collider = value; } }
|
||||
public LayerMask CollisionLayers { get { return m_CollisionLayers; } set { m_CollisionLayers = value; } }
|
||||
|
||||
private Transform m_ColliderTransform;
|
||||
private Collider[] m_HitColliders;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the default values.
|
||||
/// </summary>
|
||||
public override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
if (m_Collider == null || (!(m_Collider is CapsuleCollider) && !(m_Collider is SphereCollider))) {
|
||||
Debug.LogError("Error: Only Capsule and Sphere Colliders are supported by the Item Pullback ability.");
|
||||
m_Collider = null;
|
||||
Enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
m_HitColliders = new Collider[m_MaxCollisionCount];
|
||||
m_ColliderTransform = m_Collider.transform;
|
||||
m_Collider.gameObject.SetActive(false);
|
||||
|
||||
EventHandler.RegisterEvent<bool>(m_GameObject, "OnCameraWillChangePerspectives", OnChangePerspectives);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the ablity is tried to be started. If false is returned then the ability will not be started.
|
||||
/// </summary>
|
||||
/// <returns>True if the ability can be started.</returns>
|
||||
public override bool CanStartAbility()
|
||||
{
|
||||
return HasCollision();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is there a collision between the item pullback collider and another object?
|
||||
/// </summary>
|
||||
/// <returns>True if there is a collision with the item pullback collider.</returns>
|
||||
private bool HasCollision()
|
||||
{
|
||||
int hitCount;
|
||||
if (m_Collider is CapsuleCollider) {
|
||||
var capsuleCollider = m_Collider as CapsuleCollider;
|
||||
if (capsuleCollider.radius == 0) {
|
||||
return false;
|
||||
}
|
||||
Vector3 startEndCap, endEndCap;
|
||||
MathUtility.CapsuleColliderEndCaps(capsuleCollider, m_ColliderTransform.position, m_ColliderTransform.rotation, out startEndCap, out endEndCap);
|
||||
hitCount = Physics.OverlapCapsuleNonAlloc(startEndCap, endEndCap, capsuleCollider.radius * MathUtility.ColliderRadiusMultiplier(capsuleCollider), m_HitColliders, m_CollisionLayers, QueryTriggerInteraction.Ignore);
|
||||
} else { // SphereCollider.
|
||||
var sphereCollider = m_Collider as SphereCollider;
|
||||
if (sphereCollider.radius == 0) {
|
||||
return false;
|
||||
}
|
||||
hitCount = Physics.OverlapSphereNonAlloc(m_ColliderTransform.position, sphereCollider.radius * MathUtility.ColliderRadiusMultiplier(sphereCollider), m_HitColliders, m_CollisionLayers, QueryTriggerInteraction.Ignore);
|
||||
}
|
||||
|
||||
for (int i = 0; i < hitCount; ++i) {
|
||||
// Objects which are children of the character aren't considered a collision.
|
||||
if (m_HitColliders[i].transform.IsChildOf(m_Transform)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Projectiles shouldn't prevent the pullback ability.
|
||||
if (m_HitColliders[i].gameObject.GetCachedComponent<Objects.Projectile>() != null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// It only takes one object for the ability to be in a collision state.
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when another ability is attempting to start and the current ability is active.
|
||||
/// Returns true or false depending on if the new ability should be blocked from starting.
|
||||
/// </summary>
|
||||
/// <param name="startingAbility">The ability that is starting.</param>
|
||||
/// <returns>True if the ability should be blocked.</returns>
|
||||
public override bool ShouldBlockAbilityStart(Ability startingAbility)
|
||||
{
|
||||
return CanStopAbility(startingAbility);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the current ability is attempting to start and another ability is active.
|
||||
/// Returns true or false depending on if the active ability should be stopped.
|
||||
/// </summary>
|
||||
/// <param name="activeAbility">The ability that is currently active.</param>
|
||||
/// <returns>True if the ability should be stopped.</returns>
|
||||
public override bool ShouldStopActiveAbility(Ability activeAbility)
|
||||
{
|
||||
return CanStopAbility(activeAbility);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Can the Item Pullback ability stop the specified ability?
|
||||
/// </summary>
|
||||
/// <param name="ability">The ability that may be able to be stopped.</param>
|
||||
/// <returns>True if the ability can be stopped.</returns>
|
||||
private bool CanStopAbility(Ability ability)
|
||||
{
|
||||
#if ULTIMATE_CHARACTER_CONTROLLER_SHOOTER
|
||||
if (ability is Use) {
|
||||
var useAbility = ability as Use;
|
||||
return useAbility.UsesItemActionType(typeof(UltimateCharacterController.Items.Actions.ShootableWeapon));
|
||||
}
|
||||
if (ability is Reload) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Can the ability be stopped?
|
||||
/// </summary>
|
||||
/// <returns>True if the ability can be stopped.</returns>
|
||||
public override bool CanStopAbility()
|
||||
{
|
||||
return !HasCollision();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The character perspective between first and third person has changed.
|
||||
/// </summary>
|
||||
/// <param name="firstPersonPerspective">Is the character in a first person perspective?</param>
|
||||
private void OnChangePerspectives(bool firstPersonPerspective)
|
||||
{
|
||||
// Item Pullback does not work in first person mode.
|
||||
Enabled = !firstPersonPerspective;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The character has been destroyed.
|
||||
/// </summary>
|
||||
public override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
EventHandler.UnregisterEvent<bool>(m_GameObject, "OnCameraWillChangePerspectives", OnChangePerspectives);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 453263f004c12c74e871dd76b4dfa8c4
|
||||
timeCreated: 1522777146
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,68 @@
|
||||
/// ---------------------------------------------
|
||||
/// Ultimate Character Controller
|
||||
/// Copyright (c) Opsive. All Rights Reserved.
|
||||
/// https://www.opsive.com
|
||||
/// ---------------------------------------------
|
||||
|
||||
namespace Opsive.UltimateCharacterController.ThirdPersonController.Character.Abilities.Items
|
||||
{
|
||||
using Opsive.UltimateCharacterController.StateSystem;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Allows the ItemPullback collider to be resized/positioned based upon the state.
|
||||
/// </summary>
|
||||
public class ItemPullbackCollider : StateBehavior
|
||||
{
|
||||
[Tooltip("The offset to apply to the transform relative to the starting local position.")]
|
||||
[SerializeField] protected Vector3 m_LocalPositionOffset;
|
||||
[Tooltip("The offset to apply to the radius to the starting radius.")]
|
||||
[SerializeField] protected float m_RadiusOffset;
|
||||
|
||||
public Vector3 LocalPositionOffset { get { return m_LocalPositionOffset; } set { m_LocalPositionOffset = value; } }
|
||||
public float RadiusOffset { get { return m_RadiusOffset; } set { m_RadiusOffset = value; } }
|
||||
|
||||
private Transform m_Transform;
|
||||
private Collider m_Collider;
|
||||
|
||||
private Vector3 m_LocalPosition;
|
||||
private float m_Radius;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the default values.
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
m_Collider = GetComponent<Collider>();
|
||||
if (!(m_Collider is CapsuleCollider) && !(m_Collider is SphereCollider)) {
|
||||
Debug.LogWarning("Warning: The ItemPullbackCollider only supports capsule and sphere colliders.");
|
||||
enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
m_Transform = transform;
|
||||
m_LocalPosition = m_Transform.localPosition;
|
||||
if (m_Collider is CapsuleCollider) {
|
||||
m_Radius = (m_Collider as CapsuleCollider).radius;
|
||||
} else { // SphereCollider
|
||||
m_Radius = (m_Collider as SphereCollider).radius;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Callback when the StateManager has changed the active state on the current object.
|
||||
/// </summary>
|
||||
public override void StateChange()
|
||||
{
|
||||
m_Transform.localPosition = m_LocalPosition + m_LocalPositionOffset;
|
||||
|
||||
if (m_Collider is CapsuleCollider) {
|
||||
(m_Collider as CapsuleCollider).radius = m_Radius + m_RadiusOffset;
|
||||
} else { // SphereCollider
|
||||
(m_Collider as SphereCollider).radius = m_Radius + m_RadiusOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 156bcc35929b82b49bb622963c6def6a
|
||||
timeCreated: 1522787067
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user