/// <summary> /// Returns the actor's depth under water. This is based on the water surface's /// position-y and the actor's root /// </summary> /// <returns>Depth the actor us under water or -1 if we don't think they are</returns> public float GetDepth() { float lDepth = -1f; RaycastHit lHitInfo; Vector3 lStart = mTransform.position + (Vector3.up * mMaxSurfaceTest); // Check surface height based on the raycast if (RaycastExt.SafeRaycast(lStart, Vector3.down, out lHitInfo, mMaxSurfaceTest, mWaterLayers, mTransform, null, false)) { mWaterSurface = lHitInfo.collider.gameObject.transform; } // TRT 01/25/2017 - When exiting water, the ray may still hit the water plane and store the surface. // This can lead to us thinking low ground with no water has water. So, if we don't hit water // stop storing it. else if (mWaterSurface != null) { Vector3 lWaterPoint = mTransform.InverseTransformPoint(mWaterSurface.position); if (lWaterPoint.y < 0f) { mWaterSurface = null; } } // As a sanity check, let's see if we're really far under water if (mWaterSurface != null) { lDepth = mWaterSurface.position.y - mTransform.position.y; } //Utilities.Debug.Log.ScreenWrite("Depth:" + lDepth.ToString("f3"), 12); return(lDepth); }
/// <summary> /// Checks if the shape currently overlaps any colliders /// </summary> /// <param name="rPositionDelta">Movement to add to the current position</param> /// <param name="rLayerMask">Layer mask for determing what we'll collide with</param> /// <returns>Boolean that says if a hit occured or not</returns> public override List <BodyShapeHit> CollisionOverlap(Vector3 rPositionDelta, Quaternion rRotationDelta, int rLayerMask) { List <BodyShapeHit> lHits = new List <BodyShapeHit>(); Vector3 lBodyShapePos1 = rPositionDelta + (_Transform == null ? _Parent.position + ((_Parent.rotation * rRotationDelta) * _Offset) : _Transform.position + ((_Transform.rotation * rRotationDelta) * _Offset)); Vector3 lBodyShapePos2 = rPositionDelta + (_EndTransform == null ? _Parent.position + ((_Parent.rotation * rRotationDelta) * _EndOffset) : _EndTransform.position + ((_EndTransform.rotation * rRotationDelta) * _EndOffset)); Vector3 lPosition = lBodyShapePos1 + ((lBodyShapePos2 - lBodyShapePos1) / 2f); float lOverlapRadius = (Vector3.Distance(lBodyShapePos1, lBodyShapePos2) / 2f) + _Radius; GeometryExt.Ignore = _Parent; Collider[] lColliders = null; int lColliderHits = RaycastExt.SafeOverlapSphere(lPosition, lOverlapRadius, out lColliders, rLayerMask, _Parent); for (int i = 0; i < lColliderHits; i++) { Transform lCurrentTransform = lColliders[i].transform; if (lCurrentTransform == _Transform) { continue; } if (lCurrentTransform == _EndTransform) { continue; } if (_CharacterController != null && _CharacterController.IsIgnoringCollision(lColliders[i])) { continue; } // Once we get here, we have a valid collider Vector3 lLinePoint = Vector3.zero; Vector3 lColliderPoint = Vector3.zero; GeometryExt.ClosestPoints(lBodyShapePos1, lBodyShapePos2, _Radius, lColliders[i], ref lLinePoint, ref lColliderPoint, rLayerMask); if (lLinePoint != Vector3Ext.Null && lColliderPoint != Vector3Ext.Null) { float lDistance = Vector3.Distance(lLinePoint, lColliderPoint); if (lDistance < _Radius + 0.001f) { BodyShapeHit lHit = BodyShapeHit.Allocate(); lHit.StartPosition = lBodyShapePos1; lHit.EndPosition = lBodyShapePos2; lHit.HitCollider = lColliders[i]; lHit.HitOrigin = lLinePoint; lHit.HitPoint = lColliderPoint; lHit.HitDistance = lDistance - _Radius - 0.001f; lHits.Add(lHit); } } } GeometryExt.Ignore = null; GeometryExt.IgnoreArray = null; return(lHits); }
/// <summary> /// Tests if this motion should be started. However, the motion /// isn't actually started. /// </summary> /// <returns></returns> public override bool TestActivate() { // If we're not startable, this is easy if (!mIsStartable) { return(false); } // If we're working as an NPC, this changes things a bit. We're being controlled // by our velocity. So, use that to determine if we're heading up. if (mActorController.UseTransformPosition) { if (!mActorController.IsGrounded) { // If we're moving up, we're probably jumping Vector3 lVerticalVelocity = Vector3.Project(mActorController.State.Velocity, mActorController._Transform.up); if (Vector3.Dot(lVerticalVelocity, mActorController._Transform.up) > 0f) { if (mMotionLayer.ActiveMotion == null || mMotionLayer.ActiveMotion.Category != EnumMotionCategories.CLIMB) { return(true); } } } return(false); } // If we're not grounded, this is easy if (!mActorController.IsGrounded) { return(false); } // Ensure we have input to test if (_ActionAlias.Length == 0 || mMotionController._InputSource == null) { return(false); } // Test the action alias if (!mMotionController._InputSource.IsJustPressed(_ActionAlias)) { return(false); } // Perform an upward raycast to determine if something is overhead. If it is, we need // to prevent or stop a jump if (_RequiredOverheadDistance > 0f) { if (RaycastExt.SafeRaycast(mActorController._Transform.position, mActorController._Transform.up, _RequiredOverheadDistance, mActorController._CollisionLayers, mActorController._Transform)) { return(false); } } // Allow the jump return(true); }
/// <summary> /// Shoot a ray to determine if we found a ladder /// </summary> /// <returns>Boolean that says if we've found an acceptable edge</returns> public virtual bool TestForClimbUp() { // If there is active input pulling us away from the object, stop if (Mathf.Abs(mMotionController._InputSource.InputFromAvatarAngle) > 100f) { return(false); } // Test for an edge if (!RaycastExt.GetForwardEdge(mActorController._Transform, _MaxDistance, _MaxHeight, _ClimbableLayers, out mRaycastHitInfo)) { return(false); } // Ensure the edge is in range Vector3 lLocalHitPoint = mActorController._Transform.InverseTransformPoint(mRaycastHitInfo.point); if (lLocalHitPoint.y + mActorController.State.GroundSurfaceDistance < _MinHeight - 0.01f) { return(false); } if (lLocalHitPoint.z < _MinDistance) { return(false); } // If we got here, we're good return(true); }
/// <summary> /// Casts a ray out of the reticle (camera) to determine if we hit something. /// </summary> /// <param name="rHitInfo">RaycastHit with the hit information</param> /// <param name="rMinDistance">Distance from the camera to start the ray</param> /// <param name="rMaxDistance">Max distance to shoot the ray</param> /// <param name="rRadius">Radius of the ray (use 0 for a simple ray)</param> /// <param name="rLayerMask">Layers to collide with</param> /// <param name="rIgnore">Object that we'll ignore collision with</param> /// <returns>Returns true if a collision occurs or false if not</returns> public virtual bool Raycast(out RaycastHit rHitInfo, float rMinDistance, float rMaxDistance, float rRadius, int rLayerMask = -1, Transform rIgnore = null) { if (RaycastRoot == null && Camera.main != null) { RaycastRoot = Camera.main.transform; } Transform lOrigin = (RaycastRoot != null ? RaycastRoot : null); if (lOrigin == null) { lOrigin = transform; } Vector3 lStart = lOrigin.position + (lOrigin.forward * rMinDistance); Vector3 lDirection = lOrigin.forward; rHitInfo = RaycastExt.EmptyHitInfo; if (rRadius > 0f) { if (RaycastExt.SafeSphereCast(lStart, lDirection, rRadius, out rHitInfo, rMaxDistance - rMinDistance, rLayerMask, rIgnore, null, true)) { return(true); } } // We do this even after the sphere cast so that we select the position in the reticle if (RaycastExt.SafeRaycast(lStart, lDirection, out rHitInfo, rMaxDistance, rLayerMask, rIgnore, null, true)) { return(true); } return(false); }
/// <summary> /// Called each frame that the action is active /// </summary> public override void Update() { if (mProjector != null) { RaycastHit lHitInfo; Transform lOwner = _Spell.Owner.transform; Ray lRay = Camera.main.ScreenPointToRay(UnityEngine.Input.mousePosition); if (RaycastExt.SafeRaycast(lRay.origin, lRay.direction, out lHitInfo, 200f, -1, lOwner)) { float lAngle = Vector3.Angle(lHitInfo.normal, lOwner.up); if (!_RequireMatchingUp || Mathf.Abs(lAngle) <= 2f) { Vector3 lToPoint = lHitInfo.point - lOwner.position; float lToPointDistance = lToPoint.magnitude; mForward = lToPoint.normalized; lToPointDistance = Mathf.Clamp(lToPointDistance, (MinDistance > 0f ? MinDistance : lToPointDistance), MaxDistance); mPosition = lOwner.position + (mForward * lToPointDistance); mProjector.transform.rotation = Quaternion.LookRotation(-lOwner.up, mForward); mProjector.transform.position = mPosition + (Vector3.up * 1f); } } // Determine if we're done choosing a point if (_Spell.SpellInventory != null && _Spell.SpellInventory._InputSource != null) { if (_Spell.SpellInventory._InputSource.IsJustPressed(_ActionAlias)) { OnSelected(null, mPosition); } } } }
/// <summary> /// Casts a ray using the reticle and then validates the position /// </summary> /// <returns></returns> protected virtual bool Raycast(bool rUseReticle = true) { bool lRayHit = false; Transform lOwner = mOwner.transform; if (rUseReticle && TargetingReticle.Instance != null) { RaycastHit[] lHitInfos; int lHitCount = TargetingReticle.Instance.RaycastAll(out lHitInfos, _MinDistance, _MaxDistance, _Radius, _CollisionLayers, lOwner); if (lHitCount > 0) { if (_Tags == null || _Tags.Length == 0) { lRayHit = true; mHitInfo = lHitInfos[0]; } else { for (int i = 0; i < lHitCount; i++) { IAttributeSource lAttributeSource = lHitInfos[i].collider.gameObject.GetComponent <IAttributeSource>(); if (lAttributeSource != null && lAttributeSource.AttributesExist(_Tags)) { lRayHit = true; mHitInfo = lHitInfos[i]; break; } } } } } else { Ray lRay = Camera.main.ScreenPointToRay(UnityEngine.Input.mousePosition); if (RaycastExt.SafeRaycast(lRay.origin, lRay.direction, out mHitInfo, _MaxDistance, _CollisionLayers, lOwner)) { if (_Tags == null || _Tags.Length == 0) { lRayHit = true; } else { IAttributeSource lAttributeSource = mHitInfo.collider.gameObject.GetComponent <IAttributeSource>(); if (lAttributeSource != null && lAttributeSource.AttributesExist(_Tags)) { lRayHit = true; } } } } return(lRayHit); }
/// <summary> /// Test if we have an impact. If we have a prediction factor an impact may not occur, but we /// will store the prediction. /// </summary> /// <param name="rMovementDirection"></param> /// <param name="rMovementDistance"></param> /// <param name="rPrecictFactor"></param> /// <returns></returns> protected virtual bool TestImpact(Vector3 rStartPosition, Vector3 rMovementDirection, float rMovementDistance, float rPredictFactor = 2f) { #if OOTII_DEBUG Debug.DrawLine(rStartPosition, rStartPosition + (rMovementDirection * Mathf.Max(rMovementDistance * rPredictFactor, 0.1f)), Color.red); #endif // Test for a hit RaycastHit lRaycastHit; if (RaycastExt.SafeRaycast(rStartPosition, rMovementDirection, out lRaycastHit, Mathf.Max(rMovementDistance * rPredictFactor, 0.1f), -1, _Transform)) { // Ensure we're not hitting another projectile if (lRaycastHit.collider.gameObject.GetComponent <IProjectileCore>() != null) { return(false); } // This is really a predictive hit ans we extended the Distance a bit Collider lHitCollider = lRaycastHit.collider; Vector3 lHitPoint = lRaycastHit.point + (rMovementDirection * _EmbedDistance); Transform lHitTransform = GetClosestTransform(lHitPoint, lHitCollider.transform); Vector3 lHitPosition = lHitTransform.InverseTransformPoint(lHitPoint); float lDistance = Vector3.Distance(lHitPosition, mLaunchPosition); if (lDistance < _MinRange) { mHasExpired = true; //mSpellAction.OnFailure(); return(false); } #if OOTII_DEBUG mWorldHitPoint = lHitPoint; GraphicsManager.DrawLine(rStartPosition, rStartPosition + (rMovementDirection * Mathf.Max(rMovementDistance * rPredictFactor, 0.1f)), Color.red); #endif // If we're within the actual Distance, we will consider this a real hit if (lRaycastHit.distance - rMovementDistance <= 0f) { mLastImpact.Collider = lHitCollider; mLastImpact.Point = lHitPoint; mLastImpact.Normal = lRaycastHit.normal; mLastImpact.Vector = rMovementDirection; mLastImpact.Distance = lRaycastHit.distance; mLastImpact.Index = mImpactCount; OnImpact(mLastImpact); return(true); } } return(false); }
/// <summary> /// Updates the motion over time. This is called by the controller /// every update cycle so animations and stages can be updated. /// </summary> /// <param name="rDeltaTime">Time since the last frame (or fixed update call)</param> /// <param name="rUpdateIndex">Index of the update to help manage dynamic/fixed updates. [0: Invalid update, >=1: Valid update]</param> public override void Update(float rDeltaTime, int rUpdateIndex) { mMovement = Vector3.zero; // Add movement based on the dive if ((mMotionLayer._AnimatorStateID == STATE_run_to_dive && mMotionLayer._AnimatorStateNormalizedTime > 0.5f) || mMotionLayer._AnimatorTransitionID == TRANS_run_to_dive_SwimIdlePose || mMotionLayer._AnimatorTransitionID == TRANS_run_to_dive_TreadIdlePose) { mMovement = mMomentum * _InWaterMomentumFactor * rDeltaTime; } // Test if it's time to play the splash if (!mHasHitWater) { if ((mMotionLayer._AnimatorStateID == STATE_run_to_dive && mMotionLayer._AnimatorStateNormalizedTime > 0.65f) || (mMotionLayer._AnimatorStateID == STATE_StandingDive && mMotionLayer._AnimatorStateNormalizedTime > 0.65f)) { float lDepth = mSwimmerInfo.GetDepth(); if (!mHasSplashed && lDepth > 0.1f) { mHasSplashed = true; mSwimmerInfo.CreateSplash(mMotionController._Transform.position + (mMotionController._Transform.forward * 1f)); } if (lDepth > _RecoverDepth) { mHasHitWater = true; mSwimmerInfo.EnterWater(); mMotionController.SetAnimatorMotionPhase(mMotionLayer._AnimatorLayerIndex, PHASE_ENTER_WATER, true); } } } else { // We still want gravity to take effect to a little while... just to represent momentum if (mMomentum.y == 0f && mMotionLayer._AnimatorTransitionID != 0 && mMotionLayer._AnimatorTransitionNormalizedTime < 0.5f) { mMovement.y = mMovement.y + (UnityEngine.Physics.gravity.y * _InWaterMomentumFactor * 0.25f * rDeltaTime); } } // This is a safety check to ensure we don't run aground if ((mMotionLayer._AnimatorStateID == STATE_run_to_dive && mMotionLayer._AnimatorStateNormalizedTime > 0.65f)) { if (RaycastExt.SafeRaycast(mMotionController._Transform.position + (Vector3.up * mSwimmerInfo.EnterDepth * 0.1f), Vector3.down, 0.12f, mActorController.GroundingLayers, mMotionController._Transform, null, false)) { Deactivate(); } } }
/// <summary> /// Test if we hit a wall based on how we're moving. /// </summary> /// <param name="rMovement">Movement we're trying to make</param> /// <param name="rWallHitInfo">Returned information about the wall (if there is a hit)</param> /// <returns>Determines if we actually hit a wall</returns> protected bool TestForWallCollision(Vector3 rMovement, out RaycastHit rWallHitInfo) { Vector3 lRayStart = transform.position + (transform.up * 0.7f); Vector3 lRayDirection = rMovement.normalized; float lRayDistance = 0.8f; if (RaycastExt.SafeRaycast(lRayStart, lRayDirection, out rWallHitInfo, lRayDistance)) { return(true); } return(false); }
/// <summary> /// Find a position by targeting down /// </summary> /// <returns></returns> public virtual bool FindPosition() { Transform lOwner = _Spell.Owner.transform; float lStep = (_MinDistance - _MinDistance) / 5f; // Start at the center and spiral out for (float lDistance = _MaxDistance; lDistance >= _MinDistance; lDistance = lDistance - lStep) { //GraphicsManager.DrawLine(mMotionController.CameraTransform.position, mMotionController.CameraTransform.TransformPoint(lPosition), (lCount == 0 ? Color.red : lColor), null, 5f); RaycastHit lHitInfo; Vector3 lStart = lOwner.position + _StartOffset + (lOwner.forward * lDistance); Vector3 lDirection = -lOwner.up; if (RaycastExt.SafeRaycast(lStart, lDirection, out lHitInfo, _MaxDistance, _CollisionLayers, lOwner)) { // Grab the gameobject this collider belongs to GameObject lGameObject = lHitInfo.collider.gameObject; // Don't count the ignore if (lGameObject.transform == lOwner) { continue; } if (_Tags != null && _Tags.Length > 0) { IAttributeSource lAttributeSource = lGameObject.GetComponent <IAttributeSource>(); if (lAttributeSource == null || !lAttributeSource.AttributesExist(_Tags)) { continue; } } // Determine if we're done choosing a point if (_Spell.Data.Positions == null) { _Spell.Data.Positions = new List <Vector3>(); } _Spell.Data.Positions.Clear(); _Spell.Data.Positions.Add(lHitInfo.point); return(true); } } // Return the target hit return(false); }
/// <summary> /// Determines the cameras vertical distance to the ground /// </summary> /// <returns></returns> public float GetGroundDistance() { Vector3 lStart = RigController._Transform.position; float lDistance = (_GroundDistance > 0f ? _GroundDistance * 20f : 100f); RaycastHit lHitInfo; if (RaycastExt.SafeRaycast(lStart, Vector3.down, out lHitInfo, lDistance, _GroundLayers, _Anchor)) { return(lHitInfo.distance); } return(0f); }
/// <summary> /// Provides a place to set the properties of the animator /// </summary> /// <param name="rInput">Vector3 representing the input</param> /// <param name="rMove">Vector3 representing the amount of movement taking place (in world space)</param> /// <param name="rRotate">Quaternion representing the amount of rotation taking place</param> protected override void SetAnimatorProperties(Vector3 rInput, Vector3 rMovement, Quaternion rRotation) { if (mInputSource == null || !mInputSource.IsEnabled) { return; } // Jump based on space bool lIsInJump = !mActorController.State.IsGrounded; if (mInputSource.IsJustPressed("Jump")) { if (!lIsInJump && !mIsInJumpToWall) { lIsInJump = true; // We need to check if we're actually jumping towards a wall RaycastHit lHitInfo; if (RaycastExt.SafeRaycast(transform.position + (transform.up * JumpToWallHeight), transform.forward, out lHitInfo, JumpToWallDistance)) { mIsInJumpToWall = true; mJumpToWallPoint = lHitInfo.point; mJumpToWallNormal = lHitInfo.normal; mJumpToWallElapsedTime = 0f; mSavedOrientToGroundSpeed = mActorController.OrientToGroundSpeed; mActorController.OrientToGroundSpeed = JumpToWallOrientSpeed; } // Perform the jump mActorController.AddImpulse(transform.up * _JumpForce); } } // Direction of the camera float lDirection = 0f; // We do the inverse tilt so we calculate the rotation in "natural up" space vs. "actor up" space. Quaternion lInvTilt = QuaternionExt.FromToRotation(mActorController._Transform.up, Vector3.up); // Forward direction of the actor in "natural up" Vector3 lControllerForward = lInvTilt * mActorController._Transform.forward; // Get the angular difference between the camera-based input and the spider's "natural-up" forward lDirection = NumberHelper.GetHorizontalAngle(lControllerForward, rInput); mAnimator.SetFloat("Direction", lDirection); mAnimator.SetFloat("Speed", rInput.magnitude); mAnimator.SetBool("Jump", lIsInJump); }
/// <summary> /// Event function for when we arrive at the destination. In this instance, /// we're going to find a random position and move there. /// </summary> protected override void OnArrived() { bool lIsPositionValid = false; Vector3 lNewPosition = Vector3.zero; for (int i = 0; i < 20; i++) { lNewPosition = GetRandomPosition(); if (Vector3.Distance(lNewPosition, transform.position) < 2f) { continue; } if (lNewPosition.x < -45 || lNewPosition.x > 45) { lNewPosition = new Vector3(12, 0, 24); } if (lNewPosition.z < -45 || lNewPosition.z > 45) { lNewPosition = new Vector3(12, 0, 24); } Collider[] lColliders = null; int lHits = RaycastExt.SafeOverlapSphere(lNewPosition, mActorController.OverlapRadius * 2f, out lColliders, -1, transform); // Ensure the position isn't someplace we can't get to if (lColliders == null || lHits == 0) { lIsPositionValid = true; } else if (lHits == 1 && lColliders[0] is TerrainCollider) { lIsPositionValid = true; } if (lIsPositionValid) { break; } } // If we have a valid position, set it if (lIsPositionValid) { TargetPosition = lNewPosition; } }
/// <summary> /// Sphere cast collisions are slower to perform, but will test for collisions that /// may be parallel to the bone axis due to the radius of the bone. We do this check /// after the previous one. /// </summary> /// <param name="rIndex"></param> /// <returns>Determines if a collision occured or not</returns> private bool ProcessBoneCollisions(int rIndex) { // Ensure the ignore list is built if (mBoneTransforms.Count == 0 && mBones.Count > 0) { if (mBones[0].Parent != null) { mBoneTransforms.Add(mBones[0].Parent._Transform); } for (int i = 0; i < mBones.Count; i++) { mBoneTransforms.Add(mBones[i]._Transform); } } // Test for collisions float lBoneRadius = 0.02f; float lBoneLength = _BoneInfo[rIndex].Length; Vector3 lBoneVector = _BoneInfo[rIndex + 1].Position - _BoneInfo[rIndex].Position; Vector3 lBoneForward = lBoneVector.normalized; Vector3 lBoneStart = _BoneInfo[rIndex].Position + (lBoneForward * lBoneRadius); //Vector3 lBoneEnd = _BoneInfo[rIndex].Position + (lBoneForward * (lBoneLength - lBoneRadius)); // Test if there is a collision within the start of the bone. If so we don't need to go on. Collider[] lColliders = null; int lHits = RaycastExt.SafeOverlapSphere(lBoneStart, lBoneRadius, out lColliders, _CollisionLayers, null, mBoneTransforms); if (lColliders != null && lHits > 0) { return(true); } // Do a sphere cast down the length of the bone RaycastHit[] lHitArray = null; lHits = RaycastExt.SafeSphereCastAll(lBoneStart, lBoneForward, lBoneRadius, out lHitArray, lBoneLength - (lBoneRadius * 2f), _CollisionLayers, null, mBoneTransforms); if (lHits > 0) { return(true); } // No collision return(false); }
/// <summary> /// Casts a ray out of the reticle (camera) and returns all the items that the ray hits /// </summary> /// <param name="rHitInfos">RaycastHit info that describes what are hit</param> /// <param name="rMinDistance">Starting distance of the ray</param> /// <param name="rMaxDistance">Max distance of the ray</param> /// <param name="rRadius">Radius to use in order to cast a sphere</param> /// <param name="rLayerMask">Mask of things to hit</param> /// <param name="rIgnore">Transform to ignore</param> /// <param name="rIgnoreList">List of transforms to ignore</param> /// <returns>Number of items the ray hits</returns> public virtual int RaycastAll(out RaycastHit[] rHitInfos, float rMinDistance, float rMaxDistance, float rRadius, int rLayerMask = -1, Transform rIgnore = null, List <Transform> rIgnoreList = null) { if (RaycastRoot == null && Camera.main != null) { RaycastRoot = Camera.main.transform; } Transform lOrigin = (RaycastRoot != null ? RaycastRoot : null); if (lOrigin == null) { lOrigin = transform; } Vector3 lStart = lOrigin.position + (lOrigin.forward * rMinDistance); Vector3 lDirection = lOrigin.forward; int lHitCount = 0; if (rRadius <= 0) { lHitCount = RaycastExt.SafeRaycastAll(lStart, lDirection, out rHitInfos, rMaxDistance - rMinDistance, rLayerMask, rIgnore, rIgnoreList, true); } else { lHitCount = RaycastExt.SafeSphereCastAll(lStart, lDirection, rRadius, out rHitInfos, rMaxDistance - rMinDistance, rLayerMask, rIgnore, rIgnoreList, true); } if (lHitCount > 1) { Array.Sort(rHitInfos, 0, lHitCount, RaycastExt.HitDistanceComparer); } //Graphics.GraphicsManager.DrawLine(lStart, lStart + (lDirection * (rMaxDistance - rMinDistance)), Color.red); //for (int i = 0; i < lHitCount; i++) //{ // Graphics.GraphicsManager.DrawPoint(rHitInfos[i].point, Color.red); //} return(lHitCount); }
/// <summary> /// Checks if the shape currently overlaps any colliders /// </summary> /// <param name="rPositionDelta">Movement to add to the current position</param> /// <param name="rLayerMask">Layer mask for determing what we'll collide with</param> /// <returns>Boolean that says if a hit occured or not</returns> public override List <BodyShapeHit> CollisionOverlap(Vector3 rPositionDelta, Quaternion rRotationDelta, int rLayerMask) { List <BodyShapeHit> lHits = new List <BodyShapeHit>(); Vector3 lBodyShapePos1 = rPositionDelta + (_Transform == null ? _Parent.position + ((_Parent.rotation * rRotationDelta) * _Offset) : _Transform.position + ((_Transform.rotation * rRotationDelta) * _Offset)); Collider[] lColliders = null; int lColliderHits = RaycastExt.SafeOverlapSphere(lBodyShapePos1, _Radius, out lColliders, -1, _Parent); for (int i = 0; i < lColliderHits; i++) { if (lColliders[i].transform == _Transform) { continue; } if (_CharacterController != null && _CharacterController.IsIgnoringCollision(lColliders[i])) { continue; } // Once we get here, we have a valid collider Vector3 lLinePoint = lBodyShapePos1; Vector3 lColliderPoint = GeometryExt.ClosestPoint(lBodyShapePos1, _Radius, lColliders[i]); float lDistance = Vector3.Distance(lLinePoint, lColliderPoint); if (lDistance < _Radius + 0.001f) { BodyShapeHit lHit = BodyShapeHit.Allocate(); lHit.StartPosition = lBodyShapePos1; lHit.HitCollider = lColliders[i]; lHit.HitOrigin = lLinePoint; lHit.HitPoint = lColliderPoint; lHit.HitDistance = lDistance - _Radius - 0.001f; lHits.Add(lHit); } } return(lHits); }
/// <summary> /// Provides a place to set the properties of the animator /// </summary> /// <param name="rInput">Vector3 representing the input</param> /// <param name="rMove">Vector3 representing the amount of movement taking place (in world space)</param> /// <param name="rRotate">Quaternion representing the amount of rotation taking place</param> protected override void SetAnimatorProperties(Vector3 rInput, Vector3 rMovement, Quaternion rRotation) { if (mInputSource == null || !mInputSource.IsEnabled) { return; } // Jump based on space bool lIsInJump = !mActorController.State.IsGrounded; if (mInputSource.IsJustPressed("Jump")) { if (!lIsInJump && !mIsInJumpToWall) { lIsInJump = true; // We need to check if we're actually jumping towards a wall RaycastHit lHitInfo; if (RaycastExt.SafeRaycast(transform.position + (transform.up * JumpToWallHeight), transform.forward, out lHitInfo, JumpToWallDistance)) { mIsInJumpToWall = true; mJumpToWallPoint = lHitInfo.point; mJumpToWallNormal = lHitInfo.normal; mJumpToWallElapsedTime = 0f; mSavedOrientToGroundSpeed = mActorController.OrientToGroundSpeed; mActorController.OrientToGroundSpeed = JumpToWallOrientSpeed; } // Perform the jump mActorController.AddImpulse(transform.up * _JumpForce); } } // Tell the animator what to do next mAnimator.SetFloat("Speed", rInput.magnitude); mAnimator.SetFloat("Direction", Mathf.Atan2(rInput.x, rInput.z) * 180.0f / 3.14159f); mAnimator.SetBool("Jump", lIsInJump); }
/// <summary> /// Shoot a ray to determine if we found a ladder /// </summary> /// <returns>Boolean that says if we've found an acceptable edge</returns> public virtual bool TestForClimbUp() { // If there is active input pulling us away from the object, stop if (mMotionController._InputSource != null && Mathf.Abs(mMotionController._InputSource.InputFromAvatarAngle) > 100f) { return(false); } #if UNITY_EDITOR if (ShowDebug) { Transform lRoot = mActorController._Transform; GraphicsManager.DrawLine(lRoot.position + new Vector3(0f, _MinHeight, 0f) + (mMotionController.transform.forward * _MinDistance), lRoot.position + new Vector3(0f, _MinHeight, 0f) + (mMotionController.transform.forward * _MaxDistance), Color.green, null, 2f); GraphicsManager.DrawLine(lRoot.position + new Vector3(0f, _MaxHeight, 0f) + (mMotionController.transform.forward * _MinDistance), lRoot.position + new Vector3(0f, _MaxHeight, 0f) + (mMotionController.transform.forward * _MaxDistance), Color.green, null, 2f); } #endif // Test for an edge if (!RaycastExt.GetForwardEdge(mActorController._Transform, _MaxDistance, _MaxHeight, _ClimbableLayers, out mRaycastHitInfo)) { return(false); } // Ensure the edge is in range Vector3 lLocalHitPoint = mActorController._Transform.InverseTransformPoint(mRaycastHitInfo.point); if (lLocalHitPoint.y + mActorController.State.GroundSurfaceDistance < _MinHeight - 0.01f) { return(false); } if (lLocalHitPoint.z < _MinDistance) { return(false); } // If we got here, we're good return(true); }
/// <summary> /// Test if we're about to run off a ledge, but there's a wall under us we can orient too instead /// </summary> /// <param name="rMovement">Movement we're trying to make</param> /// <param name="rWallHitInfo">Returned information about the wall (if there is a hit)</param> /// <returns>Determines if we actually hit a wall</returns> protected bool TestForWallAt90DegreeDrop(Vector3 rMovement, out RaycastHit rWallHitInfo) { if (mActorController.IsGrounded) { rWallHitInfo = RaycastExt.EmptyHitInfo; return(false); } Vector3 lRayStart = transform.position + (transform.up * 0.7f); Vector3 lRayDirection = rMovement.normalized; float lRayDistance = 0.8f; // First, test if there's a wall in front of us. If so, we can stop as that means there is no drop if (RaycastExt.SafeRaycast(lRayStart, lRayDirection, out rWallHitInfo, lRayDistance)) { return(false); } lRayStart = transform.position + (rMovement * 0.1f) + (transform.up * 0.7f); lRayDirection = -transform.up; lRayDistance = 0.8f; // Next, test if there's an empty floor ahead of us if (!RaycastExt.SafeRaycast(lRayStart, lRayDirection, out rWallHitInfo, lRayDistance)) { lRayStart = transform.position + (rMovement * 0.1f) - (transform.up * 0.1f); lRayDirection = -rMovement.normalized; lRayDistance = 0.8f; if (RaycastExt.SafeRaycast(lRayStart, lRayDirection, out rWallHitInfo, lRayDistance)) { return(true); } } return(false); }
/// <summary> /// Determines if the swimmer is in shallow water /// </summary> /// <param name="rGroundLayers">Ground Layer masks to testing the ground</param> /// <returns></returns> public bool IsInShallowWater(int rGroundLayers = -1) { if (mTransform == null) { return(false); } float lDepth = GetDepth(); if (lDepth > mEnterDepth) { return(false); } // Test if the ground is close Vector3 lStart = mTransform.position + (Vector3.up * mEnterDepth); if (RaycastExt.SafeRaycast(lStart, Vector3.down, mEnterDepth + 0.1f, rGroundLayers, mTransform, null, false)) { return(true); } return(false); }
/// <summary> /// Casts out a shape to see if a collision will occur. The resulting array /// should NOT be persisted. It is re-used by this function over and over to /// reduce memory allocation. /// </summary> /// <param name="rPositionDelta">Movement to add to the current position</param> /// <param name="rDirection">Direction of the cast</param> /// <param name="rDistance">Distance of the case</param> /// <param name="rLayerMask">Layer mask for determing what we'll collide with</param> /// <returns>Returns an array of BodyShapeHit values representing all the hits that take place</returns> public override BodyShapeHit[] CollisionCastAll(Vector3 rPositionDelta, Vector3 rDirection, float rDistance, int rLayerMask) { Vector3 lBodyShapePos1 = rPositionDelta + (_Transform == null ? _Parent.position + (_Parent.rotation * _Offset) : _Transform.position + (_Transform.rotation * _Offset)); // Clear any existing body shape hits. They are released by the calloer for (int i = 0; i < mBodyShapeHitArray.Length; i++) { mBodyShapeHitArray[i] = null; } // Use the non-allocating version if we can int lHitCount = 0; #if UNITY_4_0 || UNITY_4_0_1 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_4 || UNITY_4_5 || UNITY_4_6 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2 mRaycastHitArray = UnityEngine.Physics.SphereCastAll(lBodyShapePos1, _Radius, rDirection, rDistance + EPSILON, rLayerMask); if (mRaycastHitArray != null) { lHitCount = mRaycastHitArray.Length; mBodyShapeHitArray = new BodyShapeHit[lHitCount]; } #else lHitCount = UnityEngine.Physics.SphereCastNonAlloc(lBodyShapePos1, _Radius, rDirection, mRaycastHitArray, rDistance + EPSILON, rLayerMask, QueryTriggerInteraction.Ignore); #endif int lBodyShapeHitsIndex = 0; for (int i = 0; i < lHitCount; i++) { if (mRaycastHitArray[i].collider.isTrigger) { continue; } if (_CharacterController != null && _CharacterController.IsIgnoringCollision(mRaycastHitArray[i].collider)) { continue; } Transform lCurrentTransform = mRaycastHitArray[i].collider.transform; if (lCurrentTransform == _Transform) { continue; } // Ensure we're not colliding with a transform in our chain bool lIsValidHit = true; while (lCurrentTransform != null) { if (lCurrentTransform == _Parent) { lIsValidHit = false; break; } lCurrentTransform = lCurrentTransform.parent; } if (!lIsValidHit) { continue; } // Once we get here, we have a valid collider BodyShapeHit lBodyShapeHit = BodyShapeHit.Allocate(); lBodyShapeHit.StartPosition = lBodyShapePos1; lBodyShapeHit.Shape = this; lBodyShapeHit.Hit = mRaycastHitArray[i]; lBodyShapeHit.HitOrigin = lBodyShapePos1; lBodyShapeHit.HitCollider = mRaycastHitArray[i].collider; lBodyShapeHit.HitPoint = mRaycastHitArray[i].point; lBodyShapeHit.HitNormal = mRaycastHitArray[i].normal; // This distance is the distance between the surfaces and not the start! lBodyShapeHit.HitDistance = mRaycastHitArray[i].distance; // With the sphere cast all, we can recieve hits for colliders that // start by intruding on the sphere. In this case, the distance is "0". So, // we'll find the true distance ourselves. if (mRaycastHitArray[i].distance == 0f) { Vector3 lColliderPoint = Vector3.zero; //if (lBodyShapeHit.HitCollider is TerrainCollider) //{ // lColliderPoint = GeometryExt.ClosestPoint(lBodyShapePos1, rDirection * rDistance, _Radius, (TerrainCollider)lBodyShapeHit.HitCollider, rLayerMask); //} //else //{ lColliderPoint = GeometryExt.ClosestPoint(lBodyShapePos1, _Radius, lBodyShapeHit.HitCollider, rLayerMask); //} // If we don't have a valid point, we will skip if (lColliderPoint == Vector3.zero) { BodyShapeHit.Release(lBodyShapeHit); continue; } // If the hit is further than our radius, we can skip Vector3 lHitVector = lColliderPoint - lBodyShapePos1; //if (lHitVector.magnitude > _Radius + EPSILON) //{ // BodyShapeHit.Release(lBodyShapeHit); // continue; //} // Setup the remaining info lBodyShapeHit.HitOrigin = lBodyShapePos1; lBodyShapeHit.HitPoint = lColliderPoint; // We want distance between the surfaces. We have the start point and // surface collider point. So, we remove our radius to get to the surface. lBodyShapeHit.HitDistance = lHitVector.magnitude - _Radius; lBodyShapeHit.HitPenetration = (lBodyShapeHit.HitDistance < 0f); // Shoot a ray for the normal RaycastHit lRaycastHitInfo; if (RaycastExt.SafeRaycast(lBodyShapePos1, lHitVector.normalized, out lRaycastHitInfo, Mathf.Max(lBodyShapeHit.HitDistance + _Radius, _Radius + 0.01f))) { lBodyShapeHit.HitNormal = lRaycastHitInfo.normal; } // If the ray is so close that we can't get a result we can end up here else if (lBodyShapeHit.HitDistance < EPSILON) { lBodyShapeHit.HitNormal = (lBodyShapePos1 - lColliderPoint).normalized; } } // Add the collision info if (lBodyShapeHit != null) { // We can't really trust the hit normal we have since it probably came from an edge. So, we'll // shoot a ray along our movement path. This will give us a better angle to look at. However, if we're // falling (probably from gravity), we don't want to replace the edge value. if (rDirection != Vector3.down) { RaycastHit lRaycastHitInfo; if (RaycastExt.SafeRaycast(lBodyShapeHit.HitPoint - (rDirection * rDistance), rDirection, out lRaycastHitInfo, rDistance + _Radius, -1, _Parent)) { lBodyShapeHit.HitNormal = lRaycastHitInfo.normal; } } // Store the distance between the hit point and our character's root lBodyShapeHit.HitRootDistance = _Parent.InverseTransformPoint(lBodyShapeHit.HitPoint).y; // Add the valid hit to our array mBodyShapeHitArray[lBodyShapeHitsIndex] = lBodyShapeHit; lBodyShapeHitsIndex++; } } // Return this array. The array should not be kept return(mBodyShapeHitArray); }
/// <summary> /// Tests if this motion should be started. However, the motion /// isn't actually started. /// </summary> /// <returns></returns> public override bool TestActivate() { if (!mIsStartable) { return(false); } if (!mMotionController.IsGrounded) { return(false); } // Raycast if needed if (_IsInteractableRaycastEnabled) { bool lIsFound = false; RaycastHit lHitInfo; // Even though we have a raycast root, we really want to shoot from the camera. We'll check distance with the root later. Vector3 lStart = (mMotionController.CameraTransform != null ? mMotionController.CameraTransform.position : mMotionController._Transform.position); Vector3 lForward = (mMotionController.CameraTransform != null ? mMotionController.CameraTransform.forward : mMotionController._Transform.forward); if (RaycastExt.SafeRaycast(lStart, lForward, out lHitInfo, _InteractableDistance * 5f, _InteractableLayers, mMotionController._Transform)) { bool lIsValid = true; if (mRaycastRoot != null) { float lDistance = Vector3.Distance(lHitInfo.point, mRaycastRoot.position); if (lDistance > _InteractableDistance) { lIsValid = false; } } if (lIsValid) { IInteractableCore lCore = lHitInfo.collider.gameObject.GetComponent <IInteractableCore>(); if (lCore == null) { lCore = lHitInfo.collider.gameObject.GetComponentInParent <IInteractableCore>(); } if (!_IsInteractableCoreRequired && lCore == null) { lIsFound = true; Interactable = lHitInfo.collider.gameObject; } else { if (lCore == null) { lIsValid = false; } if (lIsValid && !lCore.IsEnabled) { lIsValid = false; } if (lIsValid && !lCore.TestActivator(mMotionController._Transform)) { lIsValid = false; } if (lIsValid && lCore.RaycastCollider != null && lHitInfo.collider != lCore.RaycastCollider) { lIsValid = false; } if (lIsValid) { lIsFound = true; InteractableCore = lCore; InteractableCore.StartFocus(); mActiveForm = lCore.Form; } } } } // Deselect any interactable if none is found if (!lIsFound && _IsInteractableRaycastEnabled) { Interactable = null; } } // Test if we're supposed to activate if (_ActionAlias.Length > 0 && mMotionController._InputSource != null) { if (mMotionController._InputSource.IsJustPressed(_ActionAlias)) { // Check if the interactable is going to prepare thier activator. If so, we // aren't going to activate here. The activator will trigger the activation. if (InteractableCore != null) { if (InteractableCore.ForcePosition || InteractableCore.ForceRotation) { mMotionController.StartCoroutine(MoveToTargetInternal(InteractableCore)); } else { return(true); } } // Since we're just dealing with a simple game object, activate else if (Interactable != null) { return(true); } } } return(false); }
/// <summary> /// Tests if this motion should be started. However, the motion /// isn't actually started. /// </summary> /// <returns></returns> public override bool TestActivate() { if (!mIsStartable) { return(false); } if (mMotionController._InputSource != null && mMotionController._InputSource.IsJustPressed(ActionAlias)) { // Grab the swimmer info if it doesn't exist if (mSwimmerInfo == null) { mSwimmerInfo = SwimmerInfo.GetSwimmerInfo(mMotionController._Transform); } if (mSwimStrafe == null) { mSwimStrafe = mMotionController.GetMotion <Swim_Strafe>(); } // We need to test if there is water to dive in to // This is only valid if we're in the right stance if (mActorController.State.Stance != EnumControllerStance.SWIMMING) { float lWaterDistance = 0f; float lObstacleDistance = float.MaxValue; // Do a test to see if there is actually water to jump into RaycastHit lHitInfo; float lMinDepth = Mathf.Max(MinDepth, mSwimmerInfo.EnterDepth * 1.5f); float lDepthTest = Mathf.Max(lMinDepth * 1.6f, mSwimmerInfo.MaxSurfaceTest); float lTestDistance = TestDistance; if (mMotionController.State.InputMagnitudeTrend.Value > 0.5f) { lTestDistance = MovingTestDistance; } // Check if we can dive forward without hitting something Vector3 lStart = mMotionController._Transform.position + (Vector3.up * mSwimmerInfo.EnterDepth); if (!RaycastExt.SafeRaycast(lStart, mMotionController._Transform.forward, lTestDistance, mActorController.CollisionLayers, mMotionController._Transform, null, false)) { // Check surface height based on the raycast lStart = mMotionController._Transform.position + (mMotionController._Transform.forward * lTestDistance) + (Vector3.up * mSwimmerInfo.EnterDepth * 1.5f); if (RaycastExt.SafeRaycast(lStart, Vector3.down, out lHitInfo, lDepthTest, mSwimmerInfo.WaterLayers, mMotionController._Transform, null, false)) { lWaterDistance = lHitInfo.distance; // Ensure nothing is blocking the water if (RaycastExt.SafeRaycast(lStart, Vector3.down, out lHitInfo, lDepthTest, mActorController.GroundingLayers, mMotionController._Transform, null, false)) { lObstacleDistance = Mathf.Min(lHitInfo.distance, lObstacleDistance); } if (RaycastExt.SafeRaycast(lStart, Vector3.down, out lHitInfo, lDepthTest, mActorController.CollisionLayers, mMotionController._Transform, null, false)) { lObstacleDistance = Mathf.Min(lHitInfo.distance, lObstacleDistance); } // Ensure there's enough depth to swim in float lMaxDepthFound = lObstacleDistance - lWaterDistance; if (lMaxDepthFound > lMinDepth) { return(true); } } } } } // Return the final result return(false); }
/// <summary> /// Shoot rays to determine if a horizontal edge exists that /// we may be able to grab onto. It needs to be within the range /// of the avatar's feelers. /// </summary> /// <returns>Boolean that says if we've found an acceptable edge</returns> public virtual bool TestForClimbUp() { // If there is active input pulling us away from the object, stop if (mMotionController._InputSource != null && Mathf.Abs(mMotionController._InputSource.InputFromAvatarAngle) > 100f) { return(false); } //Vector3 lRayStart = Vector3.zero; //float lTargetDistance = mMaxDistance; // Root position for the test //Transform lRoot = mActorController._Transform; // Determine the ray positions //float lEdgeTop = mMaxHeight; //float lEdgeBottom = mMinHeight; // Debug //Debug.DrawLine(lRoot.position + new Vector3(0f, lEdgeTop, 0f), lRoot.position + new Vector3(0f, lEdgeTop, 0f) + mMotionController.transform.forward * lTargetDistance, Color.red); //Debug.DrawLine(lRoot.position + new Vector3(0f, lEdgeBottom, 0f), lRoot.position + new Vector3(0f, lEdgeBottom, 0f) + mMotionController.transform.forward * lTargetDistance, Color.red); // Debug #if UNITY_EDITOR if (ShowDebug) { Transform lRoot = mActorController._Transform; GraphicsManager.DrawLine(lRoot.position + new Vector3(0f, _MinHeight, 0f) + (mMotionController.transform.forward * _MinDistance), lRoot.position + new Vector3(0f, _MinHeight, 0f) + (mMotionController.transform.forward * _MaxDistance), Color.green, null, 2f); GraphicsManager.DrawLine(lRoot.position + new Vector3(0f, _MaxHeight, 0f) + (mMotionController.transform.forward * _MinDistance), lRoot.position + new Vector3(0f, _MaxHeight, 0f) + (mMotionController.transform.forward * _MaxDistance), Color.green, null, 2f); } #endif // Shoot forward and ensure below the edge is blocked //lRayStart = mActorController._Transform.position + (mActorController._Transform.up * lEdgeBottom); //if (!RaycastExt.SafeRaycast(lRayStart, mActorController._Transform.forward, lTargetDistance, mClimbableLayers, mActorController._Transform, out mRaycastHitInfo)) //{ // return false; //} //// If it's too close, we're done //if (mRaycastHitInfo.distance < mMinDistance) //{ // return false; //} // Find the exact edge that is infront of us if (!RaycastExt.GetForwardEdge(mActorController._Transform, _MaxDistance, _MaxHeight, _ClimbableLayers, out mRaycastHitInfo)) { return(false); } // Ensure the edge is in range Vector3 lLocalHitPoint = mActorController._Transform.InverseTransformPoint(mRaycastHitInfo.point); if (lLocalHitPoint.y + mActorController.State.GroundSurfaceDistance < _MinHeight - 0.01f) { return(false); } if (lLocalHitPoint.z < _MinDistance) { return(false); } // Finally, one last check to make sure the area under the edge is NOT clear RaycastHit lMidHitInfo; Vector3 lRayStart = mActorController._Transform.position + (mActorController._Transform.up * _MinHeight); if (!RaycastExt.SafeRaycast(lRayStart, mActorController._Transform.forward, out lMidHitInfo, _MaxDistance, _ClimbableLayers, mActorController._Transform)) { return(false); } // If we have hand positions, ensure that they collide with something as well. Otherwise, // the hand will look like it's floating in the air. if (_HandGrabOffset > 0) { RaycastHit lHandHitInfo; // Check the right hand Vector3 lRightHandPosition = mRaycastHitInfo.point + (mRaycastHitInfo.normal * 1f) + (mActorController._Transform.rotation * new Vector3(_HandGrabOffset, 0f, 0f)); if (!RaycastExt.SafeRaycast(lRightHandPosition, -mRaycastHitInfo.normal, out lHandHitInfo, 1.1f, _ClimbableLayers, mActorController._Transform)) { return(false); } // Check the left hand Vector3 lLeftHandPosition = mRaycastHitInfo.point + (mRaycastHitInfo.normal * 1f) + (mActorController._Transform.rotation * new Vector3(-_HandGrabOffset, 0f, 0f)); if (!RaycastExt.SafeRaycast(lLeftHandPosition, -mRaycastHitInfo.normal, out lHandHitInfo, 1.1f, _ClimbableLayers, mActorController._Transform)) { return(false); } } // If we got here, we found an edge return(true); }
/// <summary> /// Attempt to find a target that we can focus on. This approach uses a spiralling raycast /// </summary> /// <returns></returns> public virtual Transform FindTarget() { float lMaxRadius = 8f; float lMaxDistance = 20f; float lRevolutions = 2f; float lDegreesPerStep = 27f; float lSteps = lRevolutions * (360f / lDegreesPerStep); float lRadiusPerStep = lMaxRadius / lSteps; float lAngle = 0f; float lRadius = 0f; Vector3 lPosition = Vector3.zero; //float lColorPerStep = 1f / lSteps; //Color lColor = Color.white; Transform lOwner = _Spell.Owner.transform; // We want our final revolution to be max radius. So, increase the steps lSteps = lSteps + (360f / lDegreesPerStep) - 1f; // Start at the center and spiral out int lCount = 0; for (lCount = 0; lCount < lSteps; lCount++) { lPosition.x = lRadius * Mathf.Cos(lAngle * Mathf.Deg2Rad); lPosition.y = lRadius * Mathf.Sin(lAngle * Mathf.Deg2Rad); lPosition.z = lMaxDistance; //GraphicsManager.DrawLine(mMotionController.CameraTransform.position, mMotionController.CameraTransform.TransformPoint(lPosition), (lCount == 0 ? Color.red : lColor), null, 5f); RaycastHit lHitInfo; Vector3 lStart = lOwner.position + _StartOffset; Vector3 lDirection = lOwner.forward; if (RaycastExt.SafeRaycast(lStart, lDirection, out lHitInfo, _MaxDistance, _CollisionLayers, lOwner)) { // Grab the gameobject this collider belongs to GameObject lGameObject = lHitInfo.collider.gameObject; // Don't count the ignore if (lGameObject.transform == lOwner) { continue; } if (lHitInfo.collider is TerrainCollider) { continue; } if (_Tags != null && _Tags.Length > 0) { IAttributeSource lAttributeSource = lGameObject.GetComponent <IAttributeSource>(); if (lAttributeSource == null || !lAttributeSource.AttributesExist(_Tags)) { continue; } } if (RequiresRigidbody && lGameObject.GetComponent <Rigidbody>() == null) { continue; } if (RequiresActorCore && lGameObject.GetComponent <ActorCore>() == null) { continue; } if (RequiresCombatant && lGameObject.GetComponent <ICombatant>() == null) { continue; } // Store the target if (_Spell.Data.Targets == null) { _Spell.Data.Targets = new List <GameObject>(); } _Spell.Data.Targets.Clear(); _Spell.Data.Targets.Add(lGameObject); return(lGameObject.transform); } // Increment the spiral lAngle += lDegreesPerStep; lRadius = Mathf.Min(lRadius + lRadiusPerStep, lMaxRadius); //lColor.r = lColor.r - lColorPerStep; //lColor.g = lColor.g - lColorPerStep; } // Return the target hit return(null); }
/// <summary> /// Shoot rays to determine if a horizontal edge exists that /// we may be able to grab onto. It needs to be within the range /// of the avatar's feelers. /// </summary> /// <returns>Boolean that says if we've found an acceptable edge</returns> public virtual bool TestForClimbUp() { float lTargetDistance = _MaxDistance; // Root position for the test Transform lTransform = mActorController._Transform; // Determine the ray positions //float lEdgeTop = _MaxHeight; //float lEdgeBottom = _MinHeight; // Debug //Debug.DrawLine(lRoot.position + new Vector3(0f, lEdgeTop, 0f), lRoot.position + new Vector3(0f, lEdgeTop, 0f) + mMotionController.transform.forward * lTargetDistance, Color.red); //Debug.DrawLine(lRoot.position + new Vector3(0f, lEdgeBottom, 0f), lRoot.position + new Vector3(0f, lEdgeBottom, 0f) + mMotionController.transform.forward * lTargetDistance, Color.red); // Shoot forward and ensure below the edge is blocked Vector3 lRayStart = lTransform.position + (lTransform.up * _MinHeight); Vector3 lRayDirection = lTransform.forward; float lRayDistance = _MaxDistance; if (!RaycastExt.SafeRaycast(lRayStart, lRayDirection, out mRaycastHitInfo, lRayDistance, _ClimbableLayers, lTransform)) { return(false); } float lHitDepth = mRaycastHitInfo.distance; // If it's too close, we're done if (lHitDepth < _MinDistance) { return(false); } // Shoot forward and ensure above the edge is open lRayStart = lTransform.position + (lTransform.up * _MaxHeight); if (RaycastExt.SafeRaycast(lRayStart, lRayDirection, out mRaycastHitInfo, lRayDistance, _ClimbableLayers, lTransform)) { return(false); } // Now that we know there is an edge, determine it's exact position. // First, we sink into the collision point a tad. Then, we use our // collision point and start above it (where the top ray failed). Finally, // we shoot a ray down lRayStart = lRayStart + (lTransform.forward * (lHitDepth + 0.01f)); lRayDirection = -lTransform.up; lRayDistance = _MaxHeight; if (!RaycastExt.SafeRaycast(lRayStart, -mMotionController.transform.up, out mRaycastHitInfo, _MaxHeight - _MinHeight + 0.01f, _ClimbableLayers, mActorController._Transform)) { return(false); } Vector3 lLocalHitPoint = lTransform.InverseTransformPoint(mRaycastHitInfo.point); // Finally we shoot one last ray forward. We do this because we want the collision // data to be about the wall facing the avatar, not the wall facing the // last ray (which was shot down). lRayStart = lTransform.position + (lTransform.up * (lLocalHitPoint.y - 0.01f)); lRayDirection = lTransform.forward; lRayDistance = _MaxDistance; if (!RaycastExt.SafeRaycast(lRayStart, mMotionController.transform.forward, out mRaycastHitInfo, lTargetDistance, _ClimbableLayers, mActorController._Transform)) { return(false); } // Finally, test for the depth. This needs to be a fence or shallow wall. RaycastHit lDepthHitInfo; lRayStart = mRaycastHitInfo.point + (mActorController.transform.forward * 0.3f) + (mActorController._Transform.up * 0.2f); if (RaycastExt.SafeRaycast(lRayStart, -mMotionController.transform.up, out lDepthHitInfo, _MinHeight, _ClimbableLayers, mActorController._Transform)) { return(false); } // If we got here, we found an edge return(true); }
/// <summary> /// Shoots a ray into the character that simply tests for a hit /// </summary> /// <param name="rOrigin"></param> /// <param name="rDirection"></param> /// <param name="rRange"></param> /// <param name="rForce"></param> public BoneControllerBone Raycast(Vector3 rOrigin, Vector3 rVelocity, float rRange, bool rStopIfBlocked, ref Vector3 lHitPoint) { int lHitIndex = 0; BoneControllerBone lHitBone = null; //float lSpeed = rVelocity.magnitude; Vector3 lDirection = rVelocity.normalized; // Cast the ray and see if we hit a bone we care about //RaycastHit[] lHits = RaycastExt.SafeRaycastAll(rOrigin, lDirection, rRange).OrderBy(h => h.distance).ToArray(); //RaycastHit[] lHits = RaycastExt.SafeRaycastAll(rOrigin, lDirection, rRange, false); RaycastHit[] lHits = null; int lHitCount = RaycastExt.SafeRaycastAll(rOrigin, lDirection, out lHits, rRange); if (lHitCount > 1) { RaycastExt.Sort(lHits, lHitCount); } for (lHitIndex = 0; lHitIndex < lHitCount; lHitIndex++) { if (_UseAllBones) { lHitBone = mSkeleton.GetBone(lHits[lHitIndex].collider.transform) as BoneControllerBone; } else { for (int i = 0; i < mBones.Count; i++) { // Grab the collider on the bone (if there is one) Collider lBaseCollider = mBones[i]._Transform.GetComponent <Collider>(); if (lHits[lHitIndex].collider == lBaseCollider) { lHitBone = mBones[i]; break; } } } if (lHitBone != null) { break; } // If our first hit isn't a bone, then we're blocked if (rStopIfBlocked) { return(null); } } // Start grabbing the hit info if (lHitBone != null) { lHitPoint = lHits[lHitIndex].point; } // Return the fact if we hit a bone or not return(lHitBone); }
/// <summary> /// Called when the action is first activated /// <param name="rPreviousSpellActionState">State of the action prior to this one activating</param> public override void Activate(int rPreviousSpellActionState = -1, object rData = null) { base.Activate(rPreviousSpellActionState, rData); mActivations = 0; mBatchDelay = 0f; mLastBatchTime = 0f; if (_Spell != null && _Spell.Data != null) { SpellData lSpellData = _Spell.Data; Transform lCenter = null; Vector3 lCenterPosition = PositionOffset; GetBestPosition(SearchRootIndex, rData, _Spell.Data, PositionOffset, out lCenter, out lCenterPosition); // Ignore any existing targets for future tests if (IgnorePreviousTargets && lSpellData.Targets != null && lSpellData.Targets.Count > 0) { if (lSpellData.PreviousTargets == null) { lSpellData.PreviousTargets = new List <GameObject>(); } for (int i = 0; i < lSpellData.Targets.Count; i++) { if (!lSpellData.PreviousTargets.Contains(lSpellData.Targets[i])) { lSpellData.PreviousTargets.Add(lSpellData.Targets[i]); } } } // Remove any existing targets if (Replace) { if (lSpellData.Targets != null) { lSpellData.Targets.Clear(); lSpellData.Targets = null; } } // Find new targets if (lCenterPosition != Vector3Ext.Null) { Collider[] lColliders = null; List <GameObject> lTargets = null; int lCount = RaycastExt.SafeOverlapSphere(lCenterPosition, Radius, out lColliders, CollisionLayers, _Spell.Owner.transform, null, true); if (lColliders != null && lCount > 0) { if (lTargets == null) { lTargets = new List <GameObject>(); } for (int i = 0; i < lCount; i++) { GameObject lGameObject = lColliders[i].gameObject; if (lGameObject == _Spell.Owner) { continue; } if (RequireRigidbody && lGameObject.GetComponent <Rigidbody>() == null) { continue; } if (RequireActorCore && lGameObject.GetComponent <ActorCore>() == null) { continue; } if (RequireCombatant && lGameObject.GetComponent <ICombatant>() == null) { continue; } if (lSpellData.PreviousTargets != null && lSpellData.PreviousTargets.Contains(lGameObject)) { continue; } if (_Tags != null && _Tags.Length > 0) { IAttributeSource lAttributeSource = lGameObject.GetComponent <IAttributeSource>(); if (lAttributeSource == null || !lAttributeSource.AttributesExist(_Tags)) { continue; } } if (!lTargets.Contains(lGameObject)) { lTargets.Add(lGameObject); } } // Sort the list based on distance if (lTargets.Count > 1) { lTargets = lTargets.OrderBy(x => Vector3.Distance(lCenterPosition, x.transform.position)).ToList <GameObject>(); } } // Choose what we want if (lTargets != null && lTargets.Count > 0) { if (lSpellData.Targets == null) { lSpellData.Targets = new List <GameObject>(); } // Any or nearest if (SearchTypeIndex == 0 || SearchTypeIndex == 1) { for (int i = 0; i < lTargets.Count; i++) { if (!lSpellData.Targets.Contains(lTargets[i])) { lSpellData.Targets.Add(lTargets[i]); } if (MaxTargets > 0 && lSpellData.Targets.Count >= MaxTargets) { break; } } } // Furthest else if (SearchTypeIndex == 2) { for (int i = lTargets.Count - 1; i >= 0; i--) { if (!lSpellData.Targets.Contains(lTargets[i])) { lSpellData.Targets.Insert(0, lTargets[i]); } if (MaxTargets > 0 && lSpellData.Targets.Count >= MaxTargets) { break; } } } } } if (lSpellData.Targets != null && lSpellData.Targets.Count > 0) { if (!UseBatches) { OnSuccess(); return; } } } // Immediately deactivate if (!UseBatches) { OnFailure(); } }
/// <summary> /// Gets the focus position given the new/predicted camera rotation /// </summary> /// <param name="rCameraRotation"></param> /// <returns></returns> public virtual Vector3 GetFocusPosition(Quaternion rCameraRotation) { Transform lAnchorTransform = Anchor; Vector3 lAnchorOffset = AnchorOffset; Vector3 lAnchorPosition = AnchorPosition; Vector3 lNewFocusPosition = (rCameraRotation.Right() * _Offset.x) + (rCameraRotation.Forward() * _Offset.z); if (lAnchorTransform != null) { // We need to see if the AnchorPosition is reachable from the Anchor if (RigController.IsCollisionsEnabled && _IsCollisionEnabled && lAnchorOffset.sqrMagnitude > 0f) { Vector3 lToAnchor = lAnchorPosition - lAnchorTransform.position; Vector3 lAnchorDirection = lToAnchor.normalized; float lAnchorDistance = lToAnchor.magnitude; RaycastHit lFocusHit; float lDistanceOffset = lAnchorDistance * 0.5f; if (RaycastExt.SafeSphereCast(lAnchorTransform.position + (lAnchorDirection * lDistanceOffset), lAnchorDirection, RigController.CollisionRadius * 1.1f, out lFocusHit, lAnchorDistance - lDistanceOffset, RigController.CollisionLayers, AnchorRoot)) { if (lFocusHit.distance > 0f) { mAnchorOffsetDistance = lFocusHit.distance + lDistanceOffset; } } lAnchorOffset = lAnchorDirection * mAnchorOffsetDistance; lNewFocusPosition = lAnchorTransform.position + lAnchorOffset + lNewFocusPosition; } else { lNewFocusPosition = lAnchorTransform.position + ((RigController.RotateAnchorOffset ? lAnchorTransform.rotation : Quaternion.identity) * lAnchorOffset) + lNewFocusPosition; } #if UNITY_EDITOR Vector3 lSegmentEnd = lNewFocusPosition; #endif lNewFocusPosition = lNewFocusPosition + ((RigController.RotateAnchorOffset ? lAnchorTransform.up : Vector3.up) * _Offset.y); #if UNITY_EDITOR if (RigController.EditorShowDebug) { Graphics.GraphicsManager.DrawCapsule(lAnchorTransform.position, lAnchorPosition, RigController.CollisionRadius * 1.1f, Color.white); Graphics.GraphicsManager.DrawCapsule(lAnchorPosition, lSegmentEnd, RigController.CollisionRadius * 1.1f, Color.gray); Graphics.GraphicsManager.DrawCapsule(lSegmentEnd, lNewFocusPosition, RigController.CollisionRadius * 1.1f, Color.gray); } #endif //float lCameraDistance = Vector3.Distance(lNewFocusPosition, RigController._Transform.position); //float lCameraDistancePercent = Mathf.Clamp01(MaxDistance > 0f ? lCameraDistance / MaxDistance : 1f); //if (lCameraDistancePercent < 1f) //{ // Vector3 lToFocus = lNewFocusPosition - lAnchorPosition; // lNewFocusPosition = lAnchorPosition + (lToFocus.normalized * (lToFocus.magnitude * lCameraDistancePercent)); //} // We need to see if the focus position is reachable. If there // is a collision, we need to pull it in. if (RigController.IsCollisionsEnabled && _IsCollisionEnabled && Offset.sqrMagnitude > 0f) { Vector3 lToFocus = lNewFocusPosition - lAnchorPosition; Vector3 lFocusDirection = lToFocus.normalized; float lFocusDistance = lToFocus.magnitude; RaycastHit lFocusHit; if (RaycastExt.SafeSphereCast(lAnchorPosition, lFocusDirection, RigController.CollisionRadius * 1.1f, out lFocusHit, lFocusDistance, RigController.CollisionLayers, AnchorRoot)) { lNewFocusPosition = lAnchorPosition + (lFocusDirection * lFocusHit.distance); } } } #if UNITY_EDITOR if (RigController.EditorShowDebug) { Graphics.GraphicsManager.DrawPoint(lNewFocusPosition, Color.green); } #endif return(lNewFocusPosition); }