public bool CanApplyEntireMovement(CapsuleTransform a_Transform) { if (a_Transform.GetPosition() != m_Position) { if (!a_Transform.CanMove(m_Position - a_Transform.GetPosition(), true)) { return(false); } } if (a_Transform.GetUpDirection() != m_UpDirection) { if (!a_Transform.CanRotate(m_UpDirection, m_RotateMethod)) { return(false); } } if (a_Transform.GetLength() != m_Length) { if (!a_Transform.CanBeResized(m_Length, m_ResizeMethod)) { return(false); } } return(true); }
//Calld by GroundedAnimatedAbilityModule to move a capsuletransform along the path public void ApplyMotion(CapsuleTransform a_Transform, float a_Time) { if (m_CurrentPathNode == null) { return; } if (m_CurrentIndex == 0) { m_CurrentPathNode.ApplyEntireMovement(a_Transform); return; } if (m_CurrentPathNode.m_Duration == 0) { m_CurrentPathNode.ApplyEntireMovement(a_Transform); } else { float factor = Mathf.Clamp01(a_Time / m_CurrentPathNode.m_Duration); CapsuleMovementPathNode newPoint = new CapsuleMovementPathNode(); newPoint.InterpolationBetweenTwoNodes(m_PathNodes[m_CurrentIndex - 1], m_CurrentPathNode, factor); newPoint.ApplyEntireMovement(a_Transform); } }
//Query whether this module can be active, given the current state of the character controller (velocity, isGrounded etc.) //Called every frame when inactive (to see if it could be) and when active (to see if it should not be) public override bool IsApplicable() { if (m_ControlledCollider.IsGrounded() || (DoesInputExist("Crouch") && GetButtonInput("Crouch").m_IsPressed) || GetDirInput("Move").m_Direction == DirectionInput.Direction.Down || m_CharacterController.DidJustJump()) { return(false); } //Prevent overriding walljumps if (GetDirInput("Move").IsInThisDirection(m_ControlledCollider.GetEdgeCastInfo().GetWallNormal())) { return(false); } if ((m_CharacterController.GetJumpIsCached()) && m_ControlledCollider.IsTouchingEdge()) { //Move up to avoid transitioning into a wallrun. //Also used to prevent small distance jumps. CEdgeCastInfo edgeInfo = m_ControlledCollider.GetEdgeCastInfo(); CapsuleTransform copy = m_ControlledCollider.GetCapsuleTransformCopy(); Vector3 headStartDisplacement = edgeInfo.GetEdgeNormal() * (m_ControlledCollider.GetRadius() + 0.1f) + edgeInfo.GetWallNormal() * 0.015f; if (!copy.CanMove(headStartDisplacement, true)) { return(false); } copy.Move(headStartDisplacement); m_ProposedNewPosition = copy.GetPosition(); return(true); } return(false); }
//Apply the final transform state (when exiting the path) public void ApplyFinalNode(CapsuleTransform a_Transform) { if (m_PathNodes.Count == 0) { return; } m_PathNodes[m_PathNodes.Count - 1].ApplyEntireMovement(a_Transform); }
public CapsuleTransform CreateCopy() { CapsuleTransform copy = new CapsuleTransform(); copy.OverrideValues(this); copy.m_IsCopy = true; return(copy); }
//Creation functions for the path public CapsuleMovementPathNode CreateFirstNode(CapsuleTransform a_Transform) { CapsuleMovementPathNode node = new CapsuleMovementPathNode(); node.CopyFromTransform(a_Transform); AddNode(node); m_CurrentPathNode = node; m_CurrentIndex = 0; return(node); }
public void OverrideValues(CapsuleTransform a_ToCopyFrom) { m_CapsuleCollider = a_ToCopyFrom.m_CapsuleCollider; m_Position = a_ToCopyFrom.m_Position; m_UpDirection = a_ToCopyFrom.m_UpDirection; m_Length = a_ToCopyFrom.m_Length; m_LastResizeMethod = a_ToCopyFrom.m_LastResizeMethod; if (!m_IsCopy && m_CapsuleCollider != null) { m_CapsuleCollider.transform.position = m_Position; m_CapsuleCollider.transform.rotation = GetRotation(); } }
public void ApplyEntireMovement(CapsuleTransform a_Transform) { if (a_Transform.GetPosition() != m_Position) { a_Transform.SetPosition(m_Position); } if (a_Transform.GetUpDirection() != m_UpDirection) { a_Transform.Rotate(m_UpDirection, m_RotateMethod); } if (a_Transform.GetLength() != m_Length) { a_Transform.SetLength(m_Length, m_ResizeMethod); } }
//Check if the entire path can be applied public bool IsPossible(CapsuleTransform a_Transform) { CapsuleTransform transform = a_Transform.CreateCopy(); bool isPossible = true; for (int i = 0; i < m_PathNodes.Count; i++) { if (m_PathNodes[i].CanApplyEntireMovement(transform)) { m_PathNodes[i].ApplyEntireMovement(transform); } else { return(false); } } return(isPossible); }
protected override void GeneratePath() { CEdgeCastInfo info = m_ControlledCollider.GetEdgeCastInfo(); m_Path.Clear(); CapsuleTransform copy = m_ControlledCollider.GetCapsuleTransformCopy(); //First node is in edgehang alignment, moving away from the edge mildly CapsuleMovementPathNode newNode = m_Path.CreateFirstNode(copy); Vector3 upCenter = info.GetProposedHeadPoint(); upCenter += m_ControlledCollider.GetEdgeCastInfo().GetWallNormal() * 0.03f; copy.SetUpCenter(upCenter); copy.Rotate(info.GetUpDirection(), RotateMethod.FromTop); newNode = m_Path.DuplicateAndAddLastNode(); newNode.CopyFromTransform(copy); //Second node moves up along local up, until the bottom can slide over the edge newNode = m_Path.DuplicateAndAddLastNode(); newNode.m_Duration = m_MoveUpTime; float contactDot = Vector3.Dot(info.GetEdgeNormal(), info.GetEdgePoint()); float bottomDot = Vector3.Dot(info.GetEdgeNormal(), copy.GetDownCenter()) - m_ControlledCollider.GetRadius() - m_MoveUpMargin; float normalDot = Vector3.Dot(info.GetEdgeNormal(), copy.GetUpDirection()); float rawDistance = contactDot - bottomDot; float distance = rawDistance / normalDot; newNode.m_Position += copy.GetUpDirection() * distance; newNode.ApplyEntireMovement(copy); //Third node snaps the capsule to the "upright" position for the ground it's going to stand on newNode = m_Path.DuplicateAndAddLastNode(); newNode.m_Duration = 0.0f; newNode.m_UpDirection = (m_CharacterController.GetAlignsToGround()) ? info.GetEdgeNormal() : Vector3.up; newNode.m_RotateMethod = RotateMethod.FromBottom; newNode.ApplyEntireMovement(copy); newNode.m_Position = copy.GetPosition(); //Final node moves the capsule over the ground newNode = m_Path.DuplicateAndAddLastNode(); newNode.m_Duration = m_MoveSideTime; Vector3 direction = CState.GetDirectionAlongNormal(-info.GetWallNormal(), info.GetEdgeNormal()); newNode.m_Position += direction * m_MoveSideDistance; newNode.ApplyEntireMovement(copy); }
//Apply a capsule transform to the actual collider object. Update contextual information public void ApplyCapsuleTransform(CapsuleTransform a_CapsuleTransform) { m_CapsuleTransform.OverrideValues(a_CapsuleTransform); UpdateContextInfo(); }
//Called for every fixedupdate that this module is active public override void FixedUpdateModule() { float currentTransitionTime = Time.time - m_TransitionStartTime; //A MovingColPoint is used to move and rotate the animation path when the collider it starts on is moved //This allows animated abilities to work on moving colliders if (m_ReferencePoint != null) { //Position CapsuleTransform copy = m_ControlledCollider.GetCapsuleTransformCopy(); Vector3 diff = m_ReferencePoint.m_Transform.position - m_ReferencePoint.m_PrevPoint; copy.Move(diff); m_Path.Move(diff); //Rotation Quaternion rotationDifference = Quaternion.FromToRotation(m_ReferencePoint.m_PrevRot * Vector3.up, m_ReferencePoint.m_Transform.rotation * Vector3.up); copy.Rotate(rotationDifference * copy.GetUpDirection(), RotateMethod.FromCenter); m_Path.Rotate(rotationDifference, m_ReferencePoint.m_PrevPoint); //Offset for rotation Vector3 newRelativePoint = rotationDifference * m_ReferencePoint.m_PointRelativeToThis; Vector3 relativeDifference = newRelativePoint - m_ReferencePoint.m_PointRelativeToThis; copy.Move(relativeDifference); m_ReferencePoint.m_PrevPoint = m_ReferencePoint.m_Transform.position; m_ReferencePoint.m_PrevRot = m_ReferencePoint.m_Transform.rotation; m_ReferencePoint.m_PointRelativeToThis = m_ReferencePoint.m_Transform.position - m_ControlledCollider.GetCapsuleTransform().GetPosition(); m_ReferencePoint.m_Normal = Quaternion.FromToRotation(m_ReferencePoint.m_PrevRot * Vector3.up, m_ReferencePoint.m_Transform.rotation * Vector3.up) * m_ReferencePoint.m_Normal; if (copy.CanExistHere()) { m_ControlledCollider.ApplyCapsuleTransform(copy); } else { m_WasInterrupted = true; return; } } m_ControlledCollider.SetVelocity(Vector2.zero); //Move along the nodes as long as they can be applied (either they are finished or their duration is 0) while (!m_Path.IsDone() && m_Path.m_CurrentPathNode != null && (m_Path.m_CurrentPathNode.m_Duration == 0 || Time.time - m_TransitionStartTime >= m_Path.m_CurrentPathNode.m_Duration)) { if (m_Path.m_CurrentPathNode.CanApplyEntireMovement(m_ControlledCollider.GetCapsuleTransform())) { m_TransitionStartTime += m_Path.m_CurrentPathNode.m_Duration; currentTransitionTime -= m_Path.m_CurrentPathNode.m_Duration; m_Path.m_CurrentPathNode.ApplyEntireMovement(m_ControlledCollider.GetCapsuleTransform()); m_Path.IncrementCurrentNode(); } else { m_WasInterrupted = true; return; } } //Move along the current node if (m_Path.CanApplyMotion(m_ControlledCollider.GetCapsuleTransform(), currentTransitionTime)) { m_Path.ApplyMotion(m_ControlledCollider.GetCapsuleTransform(), currentTransitionTime); } else { m_WasInterrupted = true; return; } m_ControlledCollider.UpdateContextInfo(); }
public void CopyFromTransform(CapsuleTransform a_Transform) { m_Position = a_Transform.GetPosition(); m_UpDirection = a_Transform.GetUpDirection(); m_Length = a_Transform.GetLength(); }
//This context uses raycasts to determine if a ledge is present. //The raycasts originate from above and to the left/right of the character, and are casted downwards public void UpdateWithCollisions() { RaycastHit rightHit; RaycastHit leftHit; bool rightHasHit = false; bool leftHasHit = false; m_HasHitEdge = false; Vector3 upCenter = m_CapsuleCollider.GetUpCenter(); Vector3 upDirection = m_CapsuleCollider.GetUpDirection(); Vector3 transformRight = m_CapsuleCollider.transform.right; //Orient against wall to the side, even if not able to wallsliding //This allows the character to grab onto ledges on slanted walls CSideCastInfo sideInfo = m_CapsuleCollider.GetSideCastInfo(); if (sideInfo.m_HasHitSide) { Vector3 sideNormal = sideInfo.GetSideNormal(); Vector3 sidePoint = sideInfo.GetSidePoint(); Vector3 sideUp = CState.GetDirectionAlongNormal(Vector3.up, sideNormal); RaycastHit properSideHit; if (Physics.Raycast(sidePoint + sideNormal * 0.2f + sideUp * 0.05f, -sideNormal, out properSideHit, 0.3f, m_CapsuleCollider.GetLayerMask())) { Vector3 newPoint = properSideHit.point; Vector3 newNormal = properSideHit.normal; Vector3 newUp = CState.GetDirectionAlongNormal(Vector3.up, newNormal); Vector3 pivot = upCenter - newPoint; Quaternion rotation = Quaternion.FromToRotation(upDirection, newUp); Vector3 newUpCenter = newPoint + rotation * pivot; CapsuleTransform transform = m_CapsuleCollider.GetCapsuleTransformCopy(); transform.SetUpDirection(newUp); transform.SetUpCenter(newUpCenter); upCenter = newUpCenter; upDirection = newUp; transformRight = transform.GetRightDirection(); } } //Determine the starting positions of the raycasts Vector3 centerStart = upCenter + upDirection * (m_CapsuleCollider.GetRadius() + m_CapsuleCollider.GetEdgeCastVerticalMargin()); Vector3 sideOffSet = transformRight * (m_CapsuleCollider.GetRadius() + m_CapsuleCollider.GetEdgeCastHorizontalDistance()); Vector3 grabLength = -upDirection * (m_CapsuleCollider.GetEdgeCastVerticalDistance() + m_CapsuleCollider.GetEdgeCastVerticalMargin()); //The raycasts originate from above and to the left/right of the character, and are casted downwards //The angle of the slope they hit can't be too steep, or the character will slide off. if (Physics.Raycast(centerStart + sideOffSet, -upDirection, out rightHit, grabLength.magnitude, m_CapsuleCollider.GetLayerMask())) { if (Vector3.Angle(rightHit.normal, upDirection) < m_CapsuleCollider.GetMaxGrabAngle()) { rightHasHit = true; } } if (Physics.Raycast(centerStart - sideOffSet, -upDirection, out leftHit, grabLength.magnitude, m_CapsuleCollider.GetLayerMask())) { if (Vector3.Angle(leftHit.normal, upDirection) < m_CapsuleCollider.GetMaxGrabAngle()) { leftHasHit = true; } } if (rightHasHit || leftHasHit) { RaycastHit castToUse; Vector3 wallProbeDirection; if (rightHasHit && leftHasHit) { if (leftHit.distance < rightHit.distance) { castToUse = leftHit; wallProbeDirection = -transformRight; } else { castToUse = rightHit; wallProbeDirection = transformRight; } } else if (rightHasHit) { castToUse = rightHit; wallProbeDirection = transformRight; } else { castToUse = leftHit; wallProbeDirection = -transformRight; } //Check if the character can even hold on. The distance between the character and the start of the raycastpoint should not be blocked by a collider (no grabbing on ledges on the other side of walls if (Physics.Raycast(centerStart, wallProbeDirection, m_CapsuleCollider.GetRadius() + m_CapsuleCollider.GetEdgeCastHorizontalDistance(), m_CapsuleCollider.GetLayerMask())) { return; } wallProbeDirection = CState.GetDirectionAlongNormal(wallProbeDirection, castToUse.normal); //After detecting that collider can hang onto edge, orient properly against surface RaycastHit probeHit; Vector3 probedNormal = CState.GetDirectionAlongNormal(-wallProbeDirection, Vector3.up); if (Physics.Raycast(upCenter, wallProbeDirection, out probeHit, m_CapsuleCollider.GetRadius() + m_CapsuleCollider.GetEdgeAlignProbeDistance(), m_CapsuleCollider.GetLayerMask())) { float angle = Vector3.Angle(probeHit.normal, Vector3.up); if (angle < m_CapsuleCollider.GetMaxEdgeAlignAngle()) { probedNormal = probeHit.normal; } } Vector3 newUpDirection = CState.GetDirectionAlongNormal(Vector3.up, probedNormal); Vector3 headPoint = castToUse.point + probedNormal * (m_CapsuleCollider.GetRadius() + m_CapsuleCollider.GetEdgeCastHorizontalDistance()) - newUpDirection * m_CapsuleCollider.GetRadius(); //Block check //Check if something is obstructing the proposed headposition (and fix if so) RaycastHit blockHit; if (Physics.Raycast(headPoint, -probedNormal, out blockHit, m_CapsuleCollider.GetRadius(), m_CapsuleCollider.GetLayerMask())) { if (blockHit.distance < m_CapsuleCollider.GetRadius()) { Vector3 alongNormal = -CState.GetDirectionAlongNormal(wallProbeDirection, castToUse.normal); castToUse.point += alongNormal * (m_CapsuleCollider.GetRadius() - blockHit.distance) / Vector3.Dot(alongNormal, blockHit.normal); headPoint = castToUse.point + probedNormal * (m_CapsuleCollider.GetRadius() + m_CapsuleCollider.GetEdgeCastHorizontalDistance()) - newUpDirection * m_CapsuleCollider.GetRadius(); } } //Rotation check, check if character is aligned to wall or dangling //then check if that rotation can be achieved CapsuleTransform copy = m_CapsuleCollider.GetCapsuleTransformCopy(); copy.SetUpCenter(headPoint); if (Vector3.Angle(Vector3.up, probedNormal) > m_CapsuleCollider.GetMaxWallAngle() || Vector3.Angle(Vector3.up, probedNormal) < m_CapsuleCollider.GetMaxGroundedAngle()) { return; } if (copy.CanRotate(newUpDirection, RotateMethod.FromTop)) { m_EdgePoint = castToUse.point; m_EdgeNormal = castToUse.normal; m_EdgeTransform = castToUse.transform; m_WallNormal = probedNormal; m_UpDirection = newUpDirection; m_ProposedHeadPosition = headPoint; m_HasHitEdge = true; } } }