/// <summary>
        /// Does the actual rotation and height changes. This method is separate from FixedUpdate so the rotation/height can be determined
        /// during server reconciliation on the network.
        /// </summary>
        public void UpdateRotationHeight()
        {
            m_CharacterLocomotion.EnableColliderCollisionLayer(false);
            // The first end cap may change positions.
            if (m_FirstEndCapTarget.localPosition != m_FirstEndCapLocalPosition)
            {
                m_Transform.position       = m_FirstEndCapTarget.position;
                m_FirstEndCapLocalPosition = m_FirstEndCapTarget.localPosition;
            }

            Vector3 localDirection;

            if (m_RotateCollider)
            {
                // Update the rotation of the CapsuleCollider so it is rotated in the same direction as the end cap targets.
                var direction = m_SecondEndCapTarget.position - m_FirstEndCapTarget.position;
                localDirection = MathUtility.InverseTransformDirection(direction, m_CharacterTransform.rotation);
                var targetRotation = m_Transform.localRotation * Quaternion.FromToRotation(m_Transform.localRotation * Vector3.up, localDirection.normalized);
                m_Transform.rotation = MathUtility.TransformQuaternion(m_CharacterTransform.rotation, targetRotation);
            }

            // After the CapsuleCollider has rotated determine the new height of the CapsuleCollider. This can be done by determining the current
            // end cap locations and then getting the offset from the start end cap offsets.
            Vector3 firstEndCap, secondEndCap;

            MathUtility.CapsuleColliderEndCaps(m_CapsuleCollider, m_Transform.position, m_Transform.rotation, out firstEndCap, out secondEndCap);
            var firstEndCapOffset  = m_Transform.InverseTransformDirection(m_FirstEndCapTarget.position - firstEndCap);
            var secondEndCapOffset = m_Transform.InverseTransformDirection(m_SecondEndCapTarget.position - secondEndCap);
            var offset             = m_SecondEndCapOffset - m_FirstEndCapOffset;

            localDirection = ((secondEndCapOffset - firstEndCapOffset) - offset);

            // Determine if the new height would cause any collisions. If it does not then apply the height changes. A negative height change will never cause any
            // collisions so the OverlapCapsule does not need to be checked. A valid capsule collider height is always greater than 2 times the radius of the collider.
            var heightMultiplier = MathUtility.CapsuleColliderHeightMultiplier(m_CapsuleCollider);
            var targetHeight     = m_CapsuleCollider.height + localDirection.y / heightMultiplier;

            if (targetHeight >= m_CapsuleCollider.radius * 2 && (localDirection.y < 0 || !m_CharacterLocomotion.UsingVerticalCollisionDetection ||
                                                                 Physics.OverlapCapsuleNonAlloc(firstEndCap, secondEndCap + m_CharacterLocomotion.Up * localDirection.y,
                                                                                                m_CapsuleCollider.radius * MathUtility.ColliderRadiusMultiplier(m_CapsuleCollider), m_OverlapColliders, m_CharacterLayerManager.SolidObjectLayers,
                                                                                                QueryTriggerInteraction.Ignore) == 0))
            {
                // Adjust the CapsuleCollider height and center to account for the new offset.
                m_CapsuleCollider.height = targetHeight;
                var center = m_CapsuleCollider.center;
                center.y += localDirection.y / (heightMultiplier * 2);
                m_CapsuleCollider.center = center;
            }
            m_CharacterLocomotion.EnableColliderCollisionLayer(true);
        }
Пример #2
0
        /// <summary>
        /// Determines the values of the cast.
        /// </summary>
        /// <param name="index">The index of the target position to retrieve.</param>
        /// <param name="direction">A reference to the target direction.</param>
        /// <param name="position">A reference to the target position.</param>
        /// <param name="normal">A reference to the target normal.</param>
        /// <returns>True if the cast is valid.</returns>
        private bool DetermineCastValues(int index, ref Vector3 direction, ref Vector3 position, ref Vector3 normal)
        {
            if (m_Direction == CastDirection.Forward || m_Direction == CastDirection.Indicate) {
                Vector3 castPosition;
                if (m_Direction == CastDirection.Forward) {
                    direction = m_UseLookSource ? m_LookSource.LookDirection(m_MagicItemPerspectiveProperties.OriginLocation.position, false, m_CharacterLayerManager.SolidObjectLayers, true, true) : m_CharacterTransform.forward;
                    castPosition = m_UseLookSource ? m_LookSource.LookPosition(true) : m_CharacterTransform.position;
                } else { // Indicate.
                    direction = m_LookSource.LookDirection(false);
                    castPosition = m_LookSource.LookPosition(false);
                }

                m_CharacterLocomotion.EnableColliderCollisionLayer(false);
                if (Physics.SphereCast(castPosition - direction * m_Radius, m_Radius, direction, out var raycastHit, m_MaxDistance, m_DetectLayers, QueryTriggerInteraction.Ignore)) {
                    // The Cast Actions may indicate that the position is invalid.
                    if (m_CastActions != null) {
                        for (int i = 0; i < m_CastActions.Length; ++i) {
                            if (!m_CastActions[i].IsValidTargetPosition(raycastHit.point, raycastHit.normal)) {
                                return false;
                            }
                        }
                    }

                    position = raycastHit.point;
                    normal = raycastHit.normal;
                }
                m_CharacterLocomotion.EnableColliderCollisionLayer(true);
                return m_Direction == CastDirection.Indicate ? (raycastHit.distance > 0) : true;
            } else if (m_Direction == CastDirection.Target) {
                if (index >= m_TargetAngles.Length || m_TargetAngles[index] == int.MaxValue) {
                    return false;
                }
                var targetTransform = m_TargetColliders[index].transform;
                PivotOffset pivotOffset;
                if ((pivotOffset = targetTransform.gameObject.GetCachedComponent<PivotOffset>()) != null) {
                    position = targetTransform.TransformPoint(pivotOffset.Offset);
                } else {
                    position = targetTransform.position;
                }
                direction = (position - m_MagicItemPerspectiveProperties.OriginLocation.position).normalized;
                normal = targetTransform.up;
                return true;
            }

            // None direction.
            direction = m_CharacterTransform.forward;
            position = m_CharacterTransform.position;
            normal = m_CharacterTransform.up;
            return true;
        }
Пример #3
0
        /// <summary>
        /// Returns the direction that the character is looking.
        /// </summary>
        /// <param name="lookPosition">The position that the character is looking from.</param>
        /// <param name="characterLookDirection">Is the character look direction being retrieved?</param>
        /// <param name="layerMask">The LayerMask value of the objects that the look direction can hit.</param>
        /// <param name="useRecoil">Should recoil be included in the look direction?</param>
        /// <returns>The direction that the character is looking.</returns>
        public Vector3 LookDirection(Vector3 lookPosition, bool characterLookDirection, int layerMask, bool useRecoil)
        {
            var collisionLayerEnabled = m_CharacterLocomotion.CollisionLayerEnabled;

            m_CharacterLocomotion.EnableColliderCollisionLayer(false);

            // Cast a ray from the look source point in the forward direction. The look direction is then the vector from the look position to the hit point.
            RaycastHit hit;
            Vector3    direction;

            if (Physics.Raycast(m_LookPosition, m_LookDirection, out hit, m_LookDirectionDistance, layerMask, QueryTriggerInteraction.Ignore))
            {
                direction = (hit.point - lookPosition).normalized;
            }
            else
            {
                direction = m_LookDirection;
            }

            m_CharacterLocomotion.EnableColliderCollisionLayer(collisionLayerEnabled);
            return(direction);
        }
Пример #4
0
        /// <summary>
        /// Throws the throwable object.
        /// </summary>
        public void ThrowItem()
        {
            if (m_Thrown)
            {
                return;
            }

#if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER
            // The object has been thrown. If the ItemAction is on the server then that object should be spawned on the network.
            // Non-server actions should disable the mesh renderers so the object can take its place. The mesh renderers will be enabled again in a separate call.
            if (m_NetworkInfo != null)
            {
                EnableObjectMeshRenderers(false);
                if (!m_NetworkInfo.IsServer())
                {
                    ObjectPool.Destroy(m_InstantiatedThrownObject);
                    m_InstantiatedThrownObject = null;
                    return;
                }
            }
#endif

            m_InstantiatedThrownObject.transform.parent = null;
            // The collider was previously disabled. Enable it again when it is thrown.
            var collider = m_InstantiatedThrownObject.GetCachedComponent <Collider>();
            collider.enabled = true;

            // When the item is used the trajectory object should start moving on its own.
            // The throwable item may be on the other side of an object (especially in the case of separate arms for the first person perspective). Perform a linecast
            // to ensure the throwable item doesn't move through any objects.
            var collisionEnabled = m_CharacterLocomotion.CollisionLayerEnabled;
            m_CharacterLocomotion.EnableColliderCollisionLayer(false);
            if (!m_CharacterLocomotion.ActiveMovementType.UseIndependentLook(false) &&
                Physics.Linecast(m_CharacterLocomotion.LookSource.LookPosition(), m_InstantiatedTrajectoryObject.transform.position, out m_RaycastHit,
                                 m_ImpactLayers, QueryTriggerInteraction.Ignore))
            {
                m_InstantiatedTrajectoryObject.transform.position = m_RaycastHit.point;
            }
            m_CharacterLocomotion.EnableColliderCollisionLayer(collisionEnabled);

            var trajectoryTransform = m_ThrowableItemPerpectiveProperties.TrajectoryLocation != null ? m_ThrowableItemPerpectiveProperties.TrajectoryLocation : m_CharacterTransform;
            var lookDirection       = m_LookSource.LookDirection(trajectoryTransform.TransformPoint(m_TrajectoryOffset), false, m_ImpactLayers, true, true);
#if ULTIMATE_CHARACTER_CONTROLLER_VR
            if (m_VRThrowableItem != null && m_CharacterLocomotion.FirstPersonPerspective)
            {
                m_Velocity = m_VRThrowableItem.GetVelocity();
            }
#endif
            var velocity = MathUtility.TransformDirection(m_Velocity, Quaternion.LookRotation(lookDirection, m_CharacterLocomotion.Up));
            // Prevent the item from being thrown behind the character. This can happen if the character is looking straight up and there is a positive
            // y velocity. Gravity will cause the thrown object to go in the opposite direction.
            if (Vector3.Dot(velocity.normalized, m_CharacterTransform.forward) < 0 && m_CharacterTransform.InverseTransformDirection(velocity.normalized).y > 0)
            {
                velocity = m_CharacterTransform.up * velocity.magnitude;
            }
            m_InstantiatedTrajectoryObject.Initialize(m_CharacterLocomotion.Alive ? (velocity + (m_CharacterTransform.forward * m_CharacterLocomotion.LocalVelocity.z)) : Vector3.zero, Vector3.zero, m_Character, false);

#if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER
            if (m_NetworkInfo != null)
            {
                NetworkObjectPool.NetworkSpawn(m_ThrownObject, m_InstantiatedThrownObject, true);
            }
#endif
            // Optionally change the layer after the object has been thrown. This allows the object to change from the first person Overlay layer
            // to the Default layer after it has cleared the character's hands.
            if (m_StartLayer != m_ThrownLayer)
            {
                Scheduler.ScheduleFixed(m_LayerChangeDelay, ChangeThrownLayer, m_InstantiatedThrownObject);
            }
#if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER
            if (m_NetworkInfo != null && !m_NetworkInfo.IsLocalPlayer())
            {
                m_Thrown = true;
            }
#endif
        }
Пример #5
0
        /// <summary>
        /// Fade any objects that get in the way between the character and the camera.
        /// </summary>
        /// <param name="immediateFade">Should the fade material be changed immediately?</param>
        private void FadeObstructingObjects(bool immediateFade)
        {
            if (!m_ObstructingObjectsFade)
            {
                return;
            }

            // Disable any obstructing colliders so the sphere cast can detect which objects are obstructing.
            for (int i = 0; i < m_ObstructingCollidersCount; ++i)
            {
                m_ObstructingColliders[i].enabled = true;
            }
            m_ObstructingCollidersCount = 0;

            var characterPosition = m_CharacterTransform.TransformPoint(m_TransformOffset);
            var direction         = (m_Transform.position - characterPosition);
            var start             = characterPosition - direction.normalized * m_CollisionRadius;

            m_CharacterLocomotion.EnableColliderCollisionLayer(false);
            // Fire a sphere to prevent the camera from colliding with other objects.
            var hitCount = Physics.SphereCastNonAlloc(start, m_CollisionRadius, direction.normalized, m_RaycastsHit,
                                                      direction.magnitude, m_CharacterLayerManager.IgnoreInvisibleCharacterWaterLayers, QueryTriggerInteraction.Ignore);

            m_CharacterLocomotion.EnableColliderCollisionLayer(true);

            m_ObstructionHitSet.Clear();
            if (hitCount > 0)
            {
                // Loop through all of the hit colliders. For any collider that has been hit get all of the renderers. The materials that are used on the
                // renderers then need to be checked to determine if they can be faded. If the material can be faded place it in a set which will then
                // be checked in the next block to determine if the material should be faded.
                for (int i = 0; i < hitCount; ++i)
                {
                    var renderers   = m_RaycastsHit[i].transform.gameObject.GetCachedComponents <Renderer>();
                    var obstructing = false;
                    for (int j = 0; j < renderers.Length; ++j)
                    {
                        var materials = renderers[j].materials;
                        for (int k = 0; k < materials.Length; ++k)
                        {
                            if (!m_CanObstructionFade.TryGetValue(materials[k], out var canFade))
                            {
                                // Objects can fade if they have the color property and can be transparent.
                                canFade = materials[k].HasProperty(m_ColorID) && (m_AutoSetMode || materials[k].renderQueue >= (int)UnityEngine.Rendering.RenderQueue.Transparent);
                                m_CanObstructionFade.Add(materials[k], canFade);
                            }

                            if (canFade)
                            {
                                var material = materials[k];
                                // Any material contained within the hit set should be faded.
                                m_ObstructionHitSet.Add(material);
                                if (m_DisableCollider && !obstructing)
                                {
                                    obstructing = CanMaterialFade(material);
                                }

                                // The same material may be applied to multiple renderers.
                                if (!m_OriginalMaterialValuesMap.ContainsKey(material))
                                {
                                    // Don't set the mode automatically just because it has the property - not all objects in the environment should fade.
                                    if (m_AutoSetMode && material.HasProperty(OriginalMaterialValue.ModeID))
                                    {
                                        m_MaterialModeSet.Add(material);
                                    }
                                    var originalMaterialValues = GenericObjectPool.Get <OriginalMaterialValue>();
                                    originalMaterialValues.Initialize(material, m_ColorID, m_MaterialModeSet.Contains(material));
                                    m_OriginalMaterialValuesMap.Add(material, originalMaterialValues);

                                    m_ObstructingMaterials[m_ObstructingMaterialsCount] = material;
                                    m_ObstructingMaterialsCount++;

                                    EnableFadeMaterial(material);
                                }
                            }
                        }
                    }

                    // If the object is faded then the collider has the option of being disabled to prevent it from causing collisions.
                    if (m_DisableCollider && obstructing)
                    {
                        m_RaycastsHit[i].collider.enabled = false;
                        m_ObstructingColliders[m_ObstructingCollidersCount] = m_RaycastsHit[i].collider;
                        m_ObstructingCollidersCount++;
                    }
                }
            }

            // Once the obstructing objects have been found they should be faded. Note that this can cause a lot of overdraw so the FadeObject method can be
            // overridden to provide a custom effect such as the one described on https://madewith.unity.com/stories/dissolving-the-world-part-1.
            for (int i = m_ObstructingMaterialsCount - 1; i >= 0; --i)
            {
                if (!FadeMaterial(m_ObstructingMaterials[i], m_ObstructionHitSet.Contains(m_ObstructingMaterials[i]), immediateFade))
                {
                    RemoveObstructingMaterial(i);
                }
            }
        }