update
This commit is contained in:
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ab5ca9f2ed95ae948ac0b4d11754812d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,254 +0,0 @@
|
||||
/// ---------------------------------------------
|
||||
/// Ultimate Character Controller
|
||||
/// Copyright (c) Opsive. All Rights Reserved.
|
||||
/// https://www.opsive.com
|
||||
/// ---------------------------------------------
|
||||
|
||||
namespace Opsive.UltimateCharacterController.FirstPersonController.Character.Abilities
|
||||
{
|
||||
using Opsive.Shared.Events;
|
||||
using Opsive.Shared.Game;
|
||||
using Opsive.Shared.Utility;
|
||||
using Opsive.UltimateCharacterController.Character;
|
||||
using Opsive.UltimateCharacterController.Character.Abilities;
|
||||
using Opsive.UltimateCharacterController.Input;
|
||||
using Opsive.UltimateCharacterController.Utility;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// The Lean ability allows the character to lean the camera to the left or the right of the character. This allows the character to peak
|
||||
/// without exposing their body. An optional collider can be used as a hitpoint and to detect any collisions.
|
||||
/// </summary>
|
||||
[DefaultStartType(AbilityStartType.Axis)]
|
||||
[DefaultStopType(AbilityStopType.Axis)]
|
||||
[DefaultInputName("Lean")]
|
||||
public class Lean : Ability
|
||||
{
|
||||
private const string c_LeanEventName = "OnCharacterLean";
|
||||
|
||||
[Tooltip("The distance that the camera should lean.")]
|
||||
[SerializeField] protected float m_Distance = 0.7f;
|
||||
[Tooltip("The amount of tilt to apply with the lean (in degress).")]
|
||||
[SerializeField] protected float m_Tilt = 7;
|
||||
[Tooltip("A tilt multiplier applied to the items.")]
|
||||
[SerializeField] protected float m_ItemTiltMultiplier = 2;
|
||||
[Tooltip("An optional collider that can be used for collision detection and hit points.")]
|
||||
[SerializeField] protected Collider m_Collider;
|
||||
[Tooltip("Optionally modify the distance that the collider leans.")]
|
||||
[Range(0, 1)] [SerializeField] protected float m_ColliderOffsetMultiplier = 0.75f;
|
||||
[Tooltip("The maximum number of collisions that can be detected by the collider.")]
|
||||
[SerializeField] protected int m_MaxCollisionCount = 5;
|
||||
|
||||
public float Distance { get { return m_Distance; } set { m_Distance = value; } }
|
||||
public float Tilt { get { return m_Tilt; } set { m_Tilt = value; } }
|
||||
public float ItemTiltMultiplier { get { return m_ItemTiltMultiplier; } set { m_ItemTiltMultiplier = value; } }
|
||||
[NonSerialized] public Collider Collider { get { return m_Collider; } set { m_Collider = value; } }
|
||||
public float ColliderOffsetMultiplier { get { return m_ColliderOffsetMultiplier; } set { m_ColliderOffsetMultiplier = value; } }
|
||||
|
||||
private UltimateCharacterLocomotionHandler m_Handler;
|
||||
private ActiveInputEvent m_LeanInput;
|
||||
|
||||
private GameObject m_ColliderGameObject;
|
||||
private Transform m_ColliderTransform;
|
||||
private Collider[] m_OverlapColliders;
|
||||
private float m_HitDistance;
|
||||
|
||||
private float m_AxisValue;
|
||||
|
||||
public override bool IsConcurrent { get { return true; } }
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the default values.
|
||||
/// </summary>
|
||||
public override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
m_Handler = m_GameObject.GetCachedComponent<UltimateCharacterLocomotionHandler>();
|
||||
if (m_Collider != null) {
|
||||
if (!(m_Collider is CapsuleCollider) && !(m_Collider is SphereCollider)) {
|
||||
Debug.LogError("Error: Only Capsule and Sphere Colliders are supported by the Lean ability.");
|
||||
m_Collider = null;
|
||||
return;
|
||||
}
|
||||
|
||||
m_OverlapColliders = new Collider[m_MaxCollisionCount];
|
||||
m_ColliderGameObject = m_Collider.gameObject;
|
||||
m_ColliderTransform = m_Collider.transform;
|
||||
m_ColliderGameObject.SetActive(false);
|
||||
}
|
||||
EventHandler.RegisterEvent<bool>(m_GameObject, "OnCharacterChangePerspectives", OnChangePerspectives);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The ability has started.
|
||||
/// </summary>
|
||||
protected override void AbilityStarted()
|
||||
{
|
||||
// If a handler exists then the ability is interested in updates when the axis value changes. This for example allows the lean to switch
|
||||
// between the left and right lean without having to stop and start again.
|
||||
if (m_Handler != null) {
|
||||
m_LeanInput = GenericObjectPool.Get<ActiveInputEvent>();
|
||||
m_LeanInput.Initialize(ActiveInputEvent.Type.Axis, InputNames[InputIndex], "OnLeanInputUpdate");
|
||||
m_Handler.RegisterInputEvent(m_LeanInput);
|
||||
}
|
||||
EventHandler.RegisterEvent<float>(m_GameObject, "OnLeanInputUpdate", OnInputUpdate);
|
||||
|
||||
base.AbilityStarted();
|
||||
|
||||
// The collider should be activated when the ability starts. The collider detects when the character would be clipping with a wall
|
||||
// and also allows the character to be shot at while leaning.
|
||||
if (m_ColliderGameObject != null) {
|
||||
m_ColliderGameObject.SetActive(true);
|
||||
}
|
||||
|
||||
// Start leaning.
|
||||
m_AxisValue = InputAxisValue;
|
||||
UpdateLean(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// As the character is moving the lean should update to ensure the collider doesn't clip with any objects.
|
||||
/// </summary>
|
||||
public override void Update()
|
||||
{
|
||||
UpdateLean(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the lean value. Will first ensure the collider doesn't clip with any other objects.
|
||||
/// </summary>
|
||||
/// <param name="forceUpdate">Should the lean values be forced to update?</param>
|
||||
private void UpdateLean(bool forceUpdate)
|
||||
{
|
||||
var update = forceUpdate;
|
||||
// If a collider exists then the lean should not clip any walls. Note that the collider doesn't actually move - it stays at the maximum lean
|
||||
// distance so ComputePenetration can detect how much to retract the lean in order to prevent any clipping.
|
||||
if (m_Collider != null) {
|
||||
var collisionLayerEnabled = m_CharacterLocomotion.CollisionLayerEnabled;
|
||||
m_CharacterLocomotion.EnableColliderCollisionLayer(false);
|
||||
int hitCount;
|
||||
if (m_Collider is CapsuleCollider) {
|
||||
Vector3 startEndCap, endEndCap;
|
||||
var capsuleCollider = m_Collider as CapsuleCollider;
|
||||
MathUtility.CapsuleColliderEndCaps(capsuleCollider, m_ColliderTransform.TransformPoint(capsuleCollider.center), m_ColliderTransform.rotation, out startEndCap, out endEndCap);
|
||||
hitCount = Physics.OverlapCapsuleNonAlloc(startEndCap, endEndCap, capsuleCollider.radius * MathUtility.ColliderRadiusMultiplier(capsuleCollider),
|
||||
m_OverlapColliders, m_CharacterLayerManager.SolidObjectLayers, QueryTriggerInteraction.Ignore);
|
||||
} else { // SphereCollider.
|
||||
var sphereCollider = m_Collider as SphereCollider;
|
||||
hitCount = Physics.OverlapSphereNonAlloc(m_ColliderTransform.TransformPoint(sphereCollider.center), sphereCollider.radius *
|
||||
MathUtility.ColliderRadiusMultiplier(sphereCollider),
|
||||
m_OverlapColliders, m_CharacterLayerManager.SolidObjectLayers, QueryTriggerInteraction.Ignore);
|
||||
}
|
||||
|
||||
if (hitCount > 0) {
|
||||
Vector3 direction;
|
||||
float distance;
|
||||
var offset = Vector3.zero;
|
||||
// Determine the offset required to resolve the collision. Note that for multiple hit colliders this will not always be resolved on the first iteration
|
||||
// but it doesn't need to be perfect for a lean.
|
||||
for (int i = 0; i < hitCount; ++i) {
|
||||
if (Physics.ComputePenetration(m_Collider, m_ColliderTransform.position, m_ColliderTransform.rotation,
|
||||
m_OverlapColliders[i], m_OverlapColliders[i].transform.position, m_OverlapColliders[i].transform.rotation, out direction, out distance)) {
|
||||
offset += direction.normalized * (distance + m_CharacterLocomotion.ColliderSpacing);
|
||||
}
|
||||
}
|
||||
// Determing if there is any horizontal collision. If a collision exists then the lean should be updated to prevent any clipping.
|
||||
var hitDistance = m_Transform.InverseTransformDirection(offset).x;
|
||||
if (m_HitDistance != hitDistance) {
|
||||
m_HitDistance = hitDistance;
|
||||
update = true;
|
||||
}
|
||||
} else if (m_HitDistance > 0) {
|
||||
// The collider was previously overlapping an object but it is not anymore. Update lean.
|
||||
m_HitDistance = 0;
|
||||
update = true;
|
||||
}
|
||||
m_CharacterLocomotion.EnableColliderCollisionLayer(collisionLayerEnabled);
|
||||
}
|
||||
|
||||
// Update the lean if the ability is just starting or stopping, there is an axis value change, or there is a collision.
|
||||
if (update) {
|
||||
float distance, tilt;
|
||||
if (m_AxisValue == 0) {
|
||||
distance = tilt = 0;
|
||||
} else {
|
||||
distance = m_Distance * -Mathf.Sign(m_AxisValue);
|
||||
tilt = m_Tilt * Mathf.Sign(m_AxisValue);
|
||||
}
|
||||
|
||||
// The collider should always be at the maximum value to allow for a stable ComputePenetration value.
|
||||
if (m_ColliderTransform != null) {
|
||||
var localPosition = m_ColliderTransform.localPosition;
|
||||
localPosition.x = distance * m_ColliderOffsetMultiplier;
|
||||
m_ColliderTransform.localPosition = localPosition;
|
||||
}
|
||||
|
||||
// Prevent any clipping.
|
||||
if (Mathf.Abs(m_HitDistance) > 0) {
|
||||
var percent = 1 - Mathf.Abs(m_HitDistance) / m_Distance;
|
||||
distance *= percent;
|
||||
tilt *= percent;
|
||||
}
|
||||
// Notify those interested of the distance and tilt value.
|
||||
EventHandler.ExecuteEvent(m_GameObject, c_LeanEventName, distance, tilt, m_ItemTiltMultiplier);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The AbilityInputEvent has updated the axis value.
|
||||
/// </summary>
|
||||
/// <param name="value">The updated axis value.</param>
|
||||
private void OnInputUpdate(float value)
|
||||
{
|
||||
if (m_AxisValue != value) {
|
||||
m_AxisValue = value;
|
||||
UpdateLean(true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The ability has stopped running.
|
||||
/// </summary>
|
||||
/// <param name="force">Was the ability force stopped?</param>
|
||||
protected override void AbilityStopped(bool force)
|
||||
{
|
||||
base.AbilityStopped(force);
|
||||
|
||||
// Update one last time with an axis value of 0 to return to the starting position.
|
||||
m_AxisValue = 0;
|
||||
UpdateLean(true);
|
||||
|
||||
// The collider is no longer needed.
|
||||
if (m_ColliderGameObject != null) {
|
||||
m_ColliderGameObject.SetActive(false);
|
||||
}
|
||||
|
||||
if (m_Handler != null) {
|
||||
m_Handler.UnregisterInputEvent(m_LeanInput);
|
||||
GenericObjectPool.Return(m_LeanInput);
|
||||
}
|
||||
EventHandler.UnregisterEvent<float>(m_GameObject, "OnLeanInputUpdate", OnInputUpdate);
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
// Lean does not work in third person mode.
|
||||
Enabled = firstPersonPerspective;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The character has been destroyed.
|
||||
/// </summary>
|
||||
public override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
EventHandler.UnregisterEvent<bool>(m_GameObject, "OnLeanInputUpdate", OnChangePerspectives);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b42e0dab32911994e86de0b9c490e075
|
||||
timeCreated: 1511358501
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,407 +0,0 @@
|
||||
/// ---------------------------------------------
|
||||
/// Ultimate Character Controller
|
||||
/// Copyright (c) Opsive. All Rights Reserved.
|
||||
/// https://www.opsive.com
|
||||
/// ---------------------------------------------
|
||||
|
||||
namespace Opsive.UltimateCharacterController.FirstPersonController.Character
|
||||
{
|
||||
using Opsive.Shared.Events;
|
||||
using Opsive.Shared.Game;
|
||||
using Opsive.UltimateCharacterController.Camera;
|
||||
using Opsive.UltimateCharacterController.Character;
|
||||
using Opsive.UltimateCharacterController.Items;
|
||||
using Opsive.UltimateCharacterController.StateSystem;
|
||||
using Opsive.UltimateCharacterController.Utility;
|
||||
using Opsive.UltimateCharacterController.FirstPersonController.Character.Identifiers;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Manages the location of the first person objects while in first or third person view.
|
||||
/// </summary>
|
||||
public class FirstPersonObjects : StateBehavior
|
||||
{
|
||||
[Tooltip("The minimum pitch angle (in degrees).")]
|
||||
[SerializeField] protected float m_MinPitchLimit = -90;
|
||||
[Tooltip("The maximum pitch angle (in degrees).")]
|
||||
[SerializeField] protected float m_MaxPitchLimit = 90;
|
||||
[Tooltip("Should the object's pitch be locked to the character's rotation?")]
|
||||
[SerializeField] protected bool m_LockPitch;
|
||||
[Tooltip("The minimum yaw angle (in degrees).")]
|
||||
[SerializeField] protected float m_MinYawLimit = -180;
|
||||
[Tooltip("The maximum yaw angle (in degrees).")]
|
||||
[SerializeField] protected float m_MaxYawLimit = 180;
|
||||
[Tooltip("Should the object's yaw be locked to the character's rotation?")]
|
||||
[SerializeField] protected bool m_LockYaw;
|
||||
[Tooltip("Should the object rotate with a change in crosshairs rotation?")]
|
||||
[SerializeField] protected bool m_RotateWithCrosshairs = true;
|
||||
[Tooltip("The speed at which the object rotates towards the target position.")]
|
||||
[SerializeField] protected float m_RotationSpeed = 15;
|
||||
[Tooltip("Should the objects be positioned according to the target position of the camera ignorning the look offset?")]
|
||||
[SerializeField] protected bool m_IgnorePositionalLookOffset;
|
||||
[Tooltip("If ignoring the look offset, specifies the offset from the target position that the first person objects should move towards.")]
|
||||
[SerializeField] protected Vector3 m_PositionOffset;
|
||||
[Tooltip("If ignoring the look offset, specifies the speed that the first person objects should move towards the target position.")]
|
||||
[SerializeField] protected float m_MoveSpeed;
|
||||
|
||||
public float MinPitchLimit
|
||||
{
|
||||
get { return m_MinPitchLimit; }
|
||||
set
|
||||
{
|
||||
m_MinPitchLimit = value;
|
||||
if (Application.isPlaying) {
|
||||
if (m_LockPitch) { UpdateLockedPitchAngle(); }
|
||||
enabled = IsActive();
|
||||
}
|
||||
}
|
||||
}
|
||||
public float MaxPitchLimit
|
||||
{
|
||||
get { return m_MaxPitchLimit; }
|
||||
set
|
||||
{
|
||||
m_MaxPitchLimit = value;
|
||||
if (Application.isPlaying) {
|
||||
if (m_LockPitch) { UpdateLockedPitchAngle(); }
|
||||
enabled = IsActive();
|
||||
}
|
||||
}
|
||||
}
|
||||
public bool LockPitch { get { return m_LockPitch; }
|
||||
set
|
||||
{
|
||||
m_LockPitch = value;
|
||||
if (Application.isPlaying) {
|
||||
if (m_LockPitch) { UpdateLockedPitchAngle(); }
|
||||
enabled = IsActive();
|
||||
}
|
||||
}
|
||||
}
|
||||
public float MinYawLimit
|
||||
{
|
||||
get { return m_MinYawLimit; }
|
||||
set
|
||||
{
|
||||
m_MinYawLimit = value;
|
||||
if (Application.isPlaying) {
|
||||
if (m_LockYaw) { UpdateLockedYawAngle(); }
|
||||
enabled = IsActive();
|
||||
}
|
||||
}
|
||||
}
|
||||
public float MaxYawLimit
|
||||
{
|
||||
get { return m_MaxYawLimit; }
|
||||
set
|
||||
{
|
||||
m_MaxYawLimit = value;
|
||||
if (Application.isPlaying) {
|
||||
if (m_LockYaw) { UpdateLockedYawAngle(); }
|
||||
enabled = IsActive();
|
||||
}
|
||||
}
|
||||
}
|
||||
public bool LockYaw
|
||||
{
|
||||
get { return m_LockYaw; }
|
||||
set
|
||||
{
|
||||
m_LockYaw = value;
|
||||
if (Application.isPlaying) {
|
||||
if (m_LockYaw) { UpdateLockedYawAngle(); }
|
||||
enabled = IsActive();
|
||||
}
|
||||
}
|
||||
}
|
||||
public bool RotateWithCrosshairs
|
||||
{
|
||||
get { return m_RotateWithCrosshairs; }
|
||||
set
|
||||
{
|
||||
m_RotateWithCrosshairs = value;
|
||||
if (Application.isPlaying) {
|
||||
enabled = IsActive();
|
||||
}
|
||||
}
|
||||
}
|
||||
public float RotationSpeed { get { return m_RotationSpeed; } set { m_RotationSpeed = value; } }
|
||||
public bool IgnorePositionalLookOffset { get { return m_IgnorePositionalLookOffset; } set { m_IgnorePositionalLookOffset = value; if (Application.isPlaying) { enabled = IsActive(); } } }
|
||||
public Vector3 PositionOffset { get { return m_PositionOffset; } set { m_PositionOffset = value; } }
|
||||
public float MoveSpeed { get { return m_MoveSpeed; } set { m_MoveSpeed = value; } }
|
||||
|
||||
private Transform m_Transform;
|
||||
private GameObject m_GameObject;
|
||||
private Transform m_CharacterTransform;
|
||||
private UltimateCharacterLocomotion m_CharacterLocomotion;
|
||||
private CameraController m_CameraController;
|
||||
private GameObject m_Character;
|
||||
private Transform m_CameraTransform;
|
||||
private GameObject[] m_FirstPersonBaseObjects;
|
||||
private HashSet<GameObject> m_ShouldActivateObject = new HashSet<GameObject>();
|
||||
private Dictionary<Item, GameObject[]> m_ItemBaseObjectMap = new Dictionary<Item, GameObject[]>();
|
||||
private Item[] m_EquippedItems;
|
||||
|
||||
private float m_Pitch;
|
||||
private float m_Yaw;
|
||||
public GameObject Character { get { return m_Character; } }
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the default values.
|
||||
/// </summary>
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
m_GameObject = gameObject;
|
||||
m_Transform = transform;
|
||||
m_CharacterLocomotion = gameObject.GetCachedParentComponent<UltimateCharacterLocomotion>();
|
||||
m_CharacterTransform = m_CharacterLocomotion.transform;
|
||||
m_Character = m_CharacterTransform.gameObject;
|
||||
var baseObjects = GetComponentsInChildren<FirstPersonBaseObject>();
|
||||
var count = 0;
|
||||
m_FirstPersonBaseObjects = new GameObject[baseObjects.Length];
|
||||
for (int i = 0; i < baseObjects.Length; ++i) {
|
||||
if (baseObjects[i].AlwaysActive) {
|
||||
continue;
|
||||
}
|
||||
m_FirstPersonBaseObjects[count] = baseObjects[i].gameObject;
|
||||
m_FirstPersonBaseObjects[count].SetActive(false);
|
||||
count++;
|
||||
}
|
||||
if (count != baseObjects.Length) {
|
||||
System.Array.Resize(ref m_FirstPersonBaseObjects, count);
|
||||
}
|
||||
var inventory = m_Character.GetCachedComponent<Inventory.InventoryBase>();
|
||||
m_EquippedItems = new Item[inventory.SlotCount];
|
||||
|
||||
EventHandler.RegisterEvent<CameraController>(m_Character, "OnCharacterAttachCamera", OnAttachCamera);
|
||||
EventHandler.RegisterEvent<Item>(m_Character, "OnInventoryAddItem", OnAddItem);
|
||||
EventHandler.RegisterEvent<Vector3, Vector3, GameObject>(m_Character, "OnDeath", OnDeath);
|
||||
EventHandler.RegisterEvent(m_Character, "OnRespawn", OnRespawn);
|
||||
EventHandler.RegisterEvent<bool>(m_Character, "OnCharacterActivate", OnActivate);
|
||||
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The character has been attached to the camera. Initialze the camera-related values.
|
||||
/// </summary>
|
||||
/// <param name="cameraGameObject">The camera controller attached to the character. Can be null.</param>
|
||||
private void OnAttachCamera(CameraController cameraController)
|
||||
{
|
||||
m_CameraController = cameraController;
|
||||
m_Transform.parent = (m_CameraController != null ? m_CameraController.Transform : m_CharacterTransform);
|
||||
m_Transform.localPosition = Vector3.zero;
|
||||
m_Transform.localRotation = Quaternion.identity;
|
||||
m_Pitch = m_Yaw = 0;
|
||||
m_CameraTransform = (m_CameraController != null ? m_CameraController.Transform : null);
|
||||
enabled = IsActive();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is the component active?
|
||||
/// </summary>
|
||||
/// <returns>True if the component is active.</returns>
|
||||
private bool IsActive()
|
||||
{
|
||||
// The component should be active if any values can update the rotation.
|
||||
return m_CameraTransform != null && (Mathf.Abs(m_MinPitchLimit - m_MaxPitchLimit) < 180 || m_LockPitch ||
|
||||
Mathf.Abs(m_MinYawLimit - m_MaxYawLimit) < 360 || m_LockYaw || m_RotateWithCrosshairs || m_IgnorePositionalLookOffset ||
|
||||
m_Transform.localPosition != Vector3.zero);
|
||||
}
|
||||
|
||||
#if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER
|
||||
/// <summary>
|
||||
/// Disables the GameObject if the character is remote.
|
||||
/// </summary>
|
||||
public void Start()
|
||||
{
|
||||
// Remote players should never see the first person objects.
|
||||
var networkInfo = m_Character.GetComponentInParent<Networking.INetworkInfo>();
|
||||
if (networkInfo != null && !networkInfo.IsLocalPlayer()) {
|
||||
m_GameObject.SetActive(false);
|
||||
EventHandler.UnregisterEvent<bool>(m_Character, "OnCharacterActivate", OnActivate);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Updates the internal pitch angle while ensuring it is within the pitch limits.
|
||||
/// </summary>
|
||||
private void UpdateLockedPitchAngle()
|
||||
{
|
||||
var localRotation = MathUtility.InverseTransformQuaternion(m_CharacterTransform.rotation, m_CameraTransform.rotation).eulerAngles;
|
||||
if (Mathf.Abs(m_MinPitchLimit - m_MaxPitchLimit) < 180) {
|
||||
m_Pitch = MathUtility.ClampAngle(localRotation.x, m_MinPitchLimit, m_MaxPitchLimit);
|
||||
} else {
|
||||
m_Pitch = localRotation.x;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the internal yaw angle while ensuring it is within the yaw limits.
|
||||
/// </summary>
|
||||
private void UpdateLockedYawAngle()
|
||||
{
|
||||
var localRotation = MathUtility.InverseTransformQuaternion(m_CharacterTransform.rotation, m_CameraTransform.rotation).eulerAngles;
|
||||
if (Mathf.Abs(m_MinYawLimit - m_MaxYawLimit) < 360) {
|
||||
m_Yaw = MathUtility.ClampAngle(localRotation.y, m_MinYawLimit, m_MaxYawLimit);
|
||||
} else {
|
||||
m_Yaw = localRotation.y;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adjusts the location of the transform according to the enabled toggles.
|
||||
/// </summary>
|
||||
private void LateUpdate()
|
||||
{
|
||||
var localRotation = MathUtility.InverseTransformQuaternion(m_CharacterTransform.rotation, m_CameraTransform.rotation).eulerAngles;
|
||||
if (m_LockPitch) {
|
||||
localRotation.x = m_Pitch;
|
||||
} else if (Mathf.Abs(m_MinPitchLimit - m_MaxPitchLimit) < 180) {
|
||||
localRotation.x = MathUtility.ClampAngle(localRotation.x, m_MinPitchLimit, m_MaxPitchLimit);
|
||||
}
|
||||
if (m_LockYaw) {
|
||||
localRotation.y = m_Yaw;
|
||||
} else if (Mathf.Abs(m_MinYawLimit - m_MaxYawLimit) < 360) {
|
||||
localRotation.y = MathUtility.ClampAngle(localRotation.y, m_MinYawLimit, m_MaxYawLimit);
|
||||
}
|
||||
var rotation = MathUtility.TransformQuaternion(m_CharacterTransform.rotation, Quaternion.Euler(localRotation));
|
||||
if (m_RotateWithCrosshairs) {
|
||||
rotation = m_CameraController.GetCrosshairsDeltaRotation() * rotation;
|
||||
}
|
||||
m_Transform.rotation = Quaternion.Slerp(m_Transform.rotation, rotation, m_RotationSpeed * m_CharacterLocomotion.TimeScale * Time.timeScale * Time.deltaTime);
|
||||
|
||||
if (m_IgnorePositionalLookOffset && m_CameraController.ActiveViewType is FirstPersonController.Camera.ViewTypes.FirstPerson) {
|
||||
var firstPersonViewType = m_CameraController.ActiveViewType as FirstPersonController.Camera.ViewTypes.FirstPerson;
|
||||
var targetPosition = firstPersonViewType.GetTargetPosition() + m_CharacterTransform.TransformDirection(m_PositionOffset);
|
||||
m_Transform.position = Vector3.MoveTowards(m_Transform.position, targetPosition, m_MoveSpeed * m_CharacterLocomotion.TimeScale * Time.timeScale * Time.deltaTime);
|
||||
} else if (m_Transform.localPosition != Vector3.zero) {
|
||||
m_Transform.localPosition = Vector3.MoveTowards(m_Transform.localPosition, Vector3.zero, m_MoveSpeed * m_CharacterLocomotion.TimeScale * Time.timeScale * Time.deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The inventory has added the specified item.
|
||||
/// </summary>
|
||||
/// <param name="item">The item that was added.</param>
|
||||
private void OnAddItem(Item item)
|
||||
{
|
||||
if (m_ItemBaseObjectMap.ContainsKey(item)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var firstPersonPerspective = item.GetComponent<Items.FirstPersonPerspectiveItem>();
|
||||
if (firstPersonPerspective != null && firstPersonPerspective.Object != null) {
|
||||
// If the item contains a first person object then the item will have a parent FirstPersonBaseObject. This object should be enabled/disabled depending on
|
||||
// the item active status.
|
||||
var firstPersonBaseObject = firstPersonPerspective.Object.transform.GetComponentInParentIncludeInactive<FirstPersonBaseObject>();
|
||||
// A base object may not exist in VR.
|
||||
if (firstPersonBaseObject == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var baseObjects = new GameObject[firstPersonPerspective.AdditionalControlObjects.Length + 1];
|
||||
baseObjects[0] = firstPersonBaseObject.gameObject;
|
||||
for (int i = 0; i < firstPersonPerspective.AdditionalControlObjects.Length; ++i) {
|
||||
baseObjects[i + 1] = firstPersonPerspective.AdditionalControlObjects[i];
|
||||
}
|
||||
|
||||
m_ItemBaseObjectMap.Add(item, baseObjects);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The specified item will be equipped.
|
||||
/// </summary>
|
||||
public void StartEquip(Item item, int slotID)
|
||||
{
|
||||
m_EquippedItems[slotID] = item;
|
||||
|
||||
CheckActiveBaseObjects();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An item has been unequipped.
|
||||
/// </summary>
|
||||
public void UnequipItem(Item item, int slotID)
|
||||
{
|
||||
if (item != m_EquippedItems[slotID]) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_EquippedItems[slotID] = null;
|
||||
|
||||
CheckActiveBaseObjects();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loops through the base objects determining if it should be active.
|
||||
/// </summary>
|
||||
private void CheckActiveBaseObjects()
|
||||
{
|
||||
// Loop through the equipped items to determine which base objects should be activated.
|
||||
// Once the loop is complete do the activation/deactivation based on the equipped items.
|
||||
m_ShouldActivateObject.Clear();
|
||||
|
||||
for (int i = 0; i < m_EquippedItems.Length; ++i) {
|
||||
if (m_EquippedItems[i] != null) {
|
||||
if (!m_ItemBaseObjectMap.TryGetValue(m_EquippedItems[i], out var baseObjects)) {
|
||||
Debug.LogError($"Error: Unable to find the base object for item {m_EquippedItems[i].name}. Ensure the item specifies a base object under the First Person Perspective Item component.");
|
||||
continue;
|
||||
}
|
||||
for (int j = 0; j < baseObjects.Length; ++j) {
|
||||
m_ShouldActivateObject.Add(baseObjects[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_FirstPersonBaseObjects.Length; ++i) {
|
||||
m_FirstPersonBaseObjects[i].SetActive(m_ShouldActivateObject.Contains(m_FirstPersonBaseObjects[i]));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The character has died.
|
||||
/// </summary>
|
||||
/// <param name="position">The position of the force.</param>
|
||||
/// <param name="force">The amount of force which killed the character.</param>
|
||||
/// <param name="attacker">The GameObject that killed the character.</param>
|
||||
private void OnDeath(Vector3 position, Vector3 force, GameObject attacker)
|
||||
{
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The character has respawned.
|
||||
/// </summary>
|
||||
private void OnRespawn()
|
||||
{
|
||||
enabled = m_CameraTransform != null && (m_LockPitch || m_LockYaw || m_RotateWithCrosshairs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The character has been activated or deactivated.
|
||||
/// </summary>
|
||||
/// <param name="activate">Was the character activated?</param>
|
||||
private void OnActivate(bool activate)
|
||||
{
|
||||
m_GameObject.SetActive(activate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The GameObject was destroyed. Unregister for any registered events.
|
||||
/// </summary>
|
||||
private void OnDestroy()
|
||||
{
|
||||
EventHandler.UnregisterEvent<CameraController>(m_Character, "OnCharacterAttachCamera", OnAttachCamera);
|
||||
EventHandler.UnregisterEvent<Item>(m_Character, "OnInventoryAddItem", OnAddItem);
|
||||
EventHandler.UnregisterEvent<Vector3, Vector3, GameObject>(m_Character, "OnDeath", OnDeath);
|
||||
EventHandler.UnregisterEvent(m_Character, "OnRespawn", OnRespawn);
|
||||
EventHandler.UnregisterEvent<bool>(m_Character, "OnCharacterActivate", OnActivate);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 301e1b1251d4e0e4e9af4c3fb5f8dc98
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: -90
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d6a89269299931049aede6b6a11ec26e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,24 +0,0 @@
|
||||
/// ---------------------------------------------
|
||||
/// Ultimate Character Controller
|
||||
/// Copyright (c) Opsive. All Rights Reserved.
|
||||
/// https://www.opsive.com
|
||||
/// ---------------------------------------------
|
||||
|
||||
namespace Opsive.UltimateCharacterController.FirstPersonController.Character.Identifiers
|
||||
{
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Identifier component which identifies the first person base object.
|
||||
/// </summary>
|
||||
public class FirstPersonBaseObject : MonoBehaviour
|
||||
{
|
||||
[Tooltip("The unique ID of the first person base object.")]
|
||||
[SerializeField] protected int m_ID;
|
||||
[Tooltip("Should the base object always stay active? This is useful for first person VR.")]
|
||||
[SerializeField] protected bool m_AlwaysActive;
|
||||
|
||||
[Opsive.Shared.Utility.NonSerialized] public int ID { get { return m_ID; } set { m_ID = value; } }
|
||||
[Opsive.Shared.Utility.NonSerialized] public bool AlwaysActive { get { return m_AlwaysActive; } set { m_AlwaysActive = value; } }
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a248a6f0e7acfe14fb3bc7907fc03c5e
|
||||
timeCreated: 1529372641
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: -170
|
||||
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.FirstPersonController.Character.Identifiers
|
||||
{
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Identifier component which specifies the first person pivot object.
|
||||
/// </summary>
|
||||
public class FirstPersonObjectPivot : MonoBehaviour
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b4aed019a66bb2942b8c87772894d49e
|
||||
timeCreated: 1510186147
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 300
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 52ca10c0d83235e4393d880282f80ca2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,52 +0,0 @@
|
||||
/// ---------------------------------------------
|
||||
/// Ultimate Character Controller
|
||||
/// Copyright (c) Opsive. All Rights Reserved.
|
||||
/// https://www.opsive.com
|
||||
/// ---------------------------------------------
|
||||
|
||||
namespace Opsive.UltimateCharacterController.FirstPersonController.Character.MovementTypes
|
||||
{
|
||||
using Opsive.UltimateCharacterController.Character.MovementTypes;
|
||||
using Opsive.UltimateCharacterController.Utility;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// With the Combat movement type the character can strafe and move backwards, and is always facing in the direction of the camera.
|
||||
/// </summary>
|
||||
public class Combat : MovementType
|
||||
{
|
||||
public override bool FirstPersonPerspective { get { return true; } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the delta yaw rotation of the character.
|
||||
/// </summary>
|
||||
/// <param name="characterHorizontalMovement">The character's horizontal movement.</param>
|
||||
/// <param name="characterForwardMovement">The character's forward movement.</param>
|
||||
/// <param name="cameraHorizontalMovement">The camera's horizontal movement.</param>
|
||||
/// <param name="cameraVerticalMovement">The camera's vertical movement.</param>
|
||||
/// <returns>The delta yaw rotation of the character.</returns>
|
||||
public override float GetDeltaYawRotation(float characterHorizontalMovement, float characterForwardMovement, float cameraHorizontalMovement, float cameraVerticalMovement)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (m_LookSource == null) {
|
||||
Debug.LogError("Error: There is no look source attached to the character. Ensure the character has a look source attached. For player characters the look source is the Camera Controller, and AI agents use the Local Look Source.");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
var lookRotation = Quaternion.LookRotation(m_LookSource.LookDirection(true), m_CharacterLocomotion.Up);
|
||||
// Convert to a local character rotation and then only return the relative y rotation.
|
||||
return MathUtility.ClampInnerAngle(MathUtility.InverseTransformQuaternion(m_Transform.rotation, lookRotation).eulerAngles.y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the controller's input vector relative to the movement type.
|
||||
/// </summary>
|
||||
/// <param name="inputVector">The current input vector.</param>
|
||||
/// <returns>The updated input vector.</returns>
|
||||
public override Vector2 GetInputVector(Vector2 inputVector)
|
||||
{
|
||||
// No changes are necessary.
|
||||
return inputVector;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d31ec8e6076371458fd96e6de1ce1e4
|
||||
timeCreated: 1480847939
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,52 +0,0 @@
|
||||
/// ---------------------------------------------
|
||||
/// Ultimate Character Controller
|
||||
/// Copyright (c) Opsive. All Rights Reserved.
|
||||
/// https://www.opsive.com
|
||||
/// ---------------------------------------------
|
||||
|
||||
namespace Opsive.UltimateCharacterController.FirstPersonController.Character.MovementTypes
|
||||
{
|
||||
using Opsive.UltimateCharacterController.Character.MovementTypes;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Allows the character to rotate and move independently of the camera.
|
||||
/// </summary>
|
||||
public class FreeLook : MovementType
|
||||
{
|
||||
public override bool FirstPersonPerspective { get { return true; } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the delta yaw rotation of the character.
|
||||
/// </summary>
|
||||
/// <param name="characterHorizontalMovement">The character's horizontal movement.</param>
|
||||
/// <param name="characterForwardMovement">The character's forward movement.</param>
|
||||
/// <param name="cameraHorizontalMovement">The camera's horizontal movement.</param>
|
||||
/// <param name="cameraVerticalMovement">The camera's vertical movement.</param>
|
||||
/// <returns>The delta yaw rotation of the character.</returns>
|
||||
public override float GetDeltaYawRotation(float characterHorizontalMovement, float characterForwardMovement, float cameraHorizontalMovement, float cameraVerticalMovement)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the controller's input vector relative to the movement type.
|
||||
/// </summary>
|
||||
/// <param name="inputVector">The current input vector.</param>
|
||||
/// <returns>The updated input vector.</returns>
|
||||
public override Vector2 GetInputVector(Vector2 inputVector)
|
||||
{
|
||||
return inputVector;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Can the character look independently of the transform rotation?
|
||||
/// </summary>
|
||||
/// <param name="characterLookDirection">Is the character look direction being retrieved?</param>
|
||||
/// <returns>True if the character should look independently of the transform rotation.</returns>
|
||||
public override bool UseIndependentLook(bool characterLookDirection)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d914682014756284997b9b1a4df86ce9
|
||||
timeCreated: 1480847939
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user