public override void UpdateBehaviour(float dt) { // bool isTag = CharacterStateController.Animator.GetCurrentAnimatorStateInfo(0).tagHash == StateNameHash; switch (state) { case LedgeHangingState.Idle: if (CharacterActions.movement.Up || autoClimbUp || forceAutoClimbUp) { state = LedgeHangingState.TopUp; CharacterStateController.UseRootMotion = true; CharacterStateController.Animator.SetTrigger(topUpParameter); } else if (CharacterActions.movement.Down) { forceExit = true; } break; case LedgeHangingState.TopUp: if (CharacterStateController.Animator.GetCurrentAnimatorStateInfo(0).IsName("Exit")) { forceExit = true; CharacterActor.ForceGrounded(); } break; } }
public override void EnterBehaviour(float dt, CharacterState fromState) { CharacterActor.AlwaysNotGrounded = true; CharacterActor.IsKinematic = true; forceExit = false; for (int i = 0; i < forceAutoClimbUpStates.Length; i++) { CharacterState state = forceAutoClimbUpStates[i]; if (fromState == state) { forceAutoClimbUp = true; break; } } CharacterActor.SetBodySize(CharacterActor.DefaultBodySize); CharacterActor.Forward = Vector3.ProjectOnPlane(-CharacterActor.WallContactNormal, CharacterActor.Up); CharacterActor.Velocity = Vector3.zero; Vector3 referencePosition = 0.5f * (leftHitInfo.point + rightHitInfo.point); Vector3 headToReference = referencePosition - CharacterActor.Top; Vector3 correction = Vector3.Project(headToReference, CharacterActor.Up) + verticalOffset * CharacterActor.Up + forwardOffset * CharacterActor.Forward; targetPosition = CharacterActor.Position + correction; CharacterActor.Position = targetPosition; CharacterStateController.UseRootMotion = true; state = LedgeHangingState.Idle; }
protected void MoveInXDirection(bool grounded) { // Create a copy of character input which we can modify; float characterInputX = characterInput.x; // Ignore input if stunned if (stunTimer > 0.0f) characterInputX = 0.0f; // Ignore input if crouching if (isCrouching) characterInputX = 0.0f; // Calculate Velocity float actualDrag = 1 - (currentDrag * frameTime); // Jump Drag if (jumpCount > 0 || !grounded) { actualDrag = 1 - (jump.drag * frameTime); } // Crouch slide drag if (isCrouchSliding) { actualDrag = 1 - (crouchSlideDrag * frameTime); } if (actualDrag < 0) actualDrag = 0; // Timer which ignores character input while jumping away from a wall if (wall.canWallJump && oppositeDirectionTimer > 0.0f) { oppositeDirectionTimer -= frameTime; if (wallJumpDirection == RC_Direction.RIGHT) characterInputX = -0.5f; else if (wallJumpDirection == RC_Direction.LEFT) characterInputX = 0.5f; } if (characterInputX != 0 && (myParent == null || !myParent.overrideX) && (!grounded || !IsSwimming || swimming.canRun)) { bool walking = false; if (jumpCount > 0 || !grounded || (characterInputX > 0 && characterInputX < 1.0f) || (characterInputX < 0 && characterInputX > -1.0f)) walking = true; if (movement.movementStyle != MovementStyle.PHYSICS_LIKE && movement.jumpAtRunSpeed && (jumpCount > 0 || !grounded)) walking = false; // TODO Move these behaviours to different functions (can wait till 2.0 upgrade?) switch (movement.movementStyle) { case MovementStyle.PHYSICS_LIKE: float newVelocity = velocity.x + (frameTime * movement.acceleration * characterInputX) * (IsSwimming ? (1 - swimming.waterResistance.x) : 1); // Climbing sideways if (climbing.allowClimbing && startedClimbing) { velocity.x = newVelocity; if (velocity.x > climbing.horizontalSpeed) velocity.x = climbing.horizontalSpeed; if (velocity.x < -1 * climbing.horizontalSpeed) velocity.x = -1 * climbing.horizontalSpeed; } else if (walking) { // If going too fast just apply drag (don't just limit to walk speed else you may get odd jerks) if ((velocity.x > movement.walkSpeed && characterInputX >= 0.0f) || (velocity.x < movement.walkSpeed * -1 && characterInputX <= 0.0f)) { velocity.x = velocity.x * actualDrag; // If we went too far go back to walk speed if (velocity.x < movement.walkSpeed && characterInputX >= 0.0f) { velocity.x = movement.walkSpeed * (IsSwimming ? (1 - swimming.waterResistance.x) : 1); } else if (velocity.x > movement.walkSpeed * -1 && characterInputX <= 0.0f) { velocity.x = movement.walkSpeed * -1 * (IsSwimming ? (1 - swimming.waterResistance.x) : 1); } } else { velocity.x = newVelocity; // Limit to walk speed; if (velocity.x > movement.walkSpeed) velocity.x = movement.walkSpeed * (IsSwimming ? (1 - swimming.waterResistance.x) : 1); if (velocity.x < -1 * movement.walkSpeed) velocity.x = -1 * movement.walkSpeed * (IsSwimming ? (1 - swimming.waterResistance.x) : 1); } } else { velocity.x = newVelocity; // Limit to run speed; if (velocity.x > movement.runSpeed) velocity.x = movement.runSpeed * (IsSwimming ? (1 - swimming.waterResistance.x) : 1); if (velocity.x < -1 * movement.runSpeed) velocity.x = -1 * movement.runSpeed * (IsSwimming ? (1 - swimming.waterResistance.x) : 1); } break; case MovementStyle.DIGITAL: if (climbing.allowClimbing && startedClimbing) { velocity.x = (characterInput.x > 0 ? 1 : -1) * climbing.horizontalSpeed; } else if (walking) { velocity.x = (characterInput.x > 0 ? 1 : -1) * movement.walkSpeed * (IsSwimming ? (1 - swimming.waterResistance.x) : 1); } else { velocity.x = (characterInput.x > 0 ? 1 : -1) * movement.runSpeed * (IsSwimming ? (1 - swimming.waterResistance.x) : 1); } break; case MovementStyle.DIGITAL_WITH_SLIDE: if (climbing.allowClimbing && startedClimbing) { velocity.x = (characterInput.x > 0 ? 1 : -1) * climbing.horizontalSpeed; } else if (walking) { velocity.x = (characterInput.x > 0 ? 1 : -1) * movement.walkSpeed * (IsSwimming ? (1 - swimming.waterResistance.x) : 1); } else { velocity.x = (characterInput.x > 0 ? 1 : -1) * movement.runSpeed * (IsSwimming ? (1 - swimming.waterResistance.x) : 1); } break; } } else { switch (movement.movementStyle) { case MovementStyle.PHYSICS_LIKE: velocity.x = velocity.x * actualDrag; break; case MovementStyle.DIGITAL: velocity.x = 0; break; case MovementStyle.DIGITAL_WITH_SLIDE: velocity.x = velocity.x * actualDrag; break; } } // Apply velocity if ((myParent == null || !myParent.overrideX) && velocity.x > movement.skinSize || velocity.x * -1 > movement.skinSize) { if (targetSlope != 0.0f) myTransform.Translate (Quaternion.Euler(0,0,targetSlope) * new Vector3(velocity.x * frameTime, 0.0f, 0.0f)); else myTransform.Translate (velocity.x * frameTime, 0.0f, 0.0f); } float forceSide = 0.0f; // Ledge Hang variables int ledgeHangHitCount = 0; Collider ledgeHangCollider = null; RaycastCollider actualLedgeCollider = null; // Wall slide variables int wallSlideCount = 0; RaycastCollider actualWallCollider = null; isWallSliding = false; isWallHolding = false; for (int i = 0; i < sides.Length; i++) { RaycastHit hitSides; float additionalDistance = 0.0f; // If crouching and using autoHeightReduction skip any sides higher than provided value if (!isCrouching || !crouch.useHeightReduction || sides[i].offset.y <= crouch.ignoredSideCollidersHigherThan) { if (ledgeHanging.autoGrab && velocity.y <= 0.0f) additionalDistance = ledgeHanging.autoGrabDistance; else if (wall.canWallSlide) additionalDistance = wall.wallSlideAdditionalDistance; hitSides = sides [i].GetCollision (1 << backgroundLayer, additionalDistance); // Hit something ... if (hitSides.collider != null) { // Update ledge hang if (ledgeHanging.canLedgeHang && !grounded && velocity.y <= 0.0f) { if (sides [i].direction == RC_Direction.RIGHT && (sides [i] == highestSideColliders [1] || sides [i] == highestSideColliders [0])) { ledgeHangHitCount++; ledgeHangDirection = RC_Direction.RIGHT; ledgeHangCollider = hitSides.collider; if (sides [i] == highestSideColliders [1]) { actualLedgeCollider = highestSideColliders [1]; } else { actualLedgeCollider = highestSideColliders [0]; } } else if (sides [i].direction == RC_Direction.LEFT && (sides [i] == highestSideColliders [1] || sides [i] == highestSideColliders [0])) { ledgeHangHitCount++; ledgeHangDirection = RC_Direction.LEFT; ledgeHangCollider = hitSides.collider; if (sides [i] == highestSideColliders [1]) { actualLedgeCollider = highestSideColliders [1]; } else { actualLedgeCollider = highestSideColliders [0]; } } } /// Update wall slide if ((wall.canWallSlide || wall.canWallJump) && !grounded) { if ((sides [i] == highestSideColliders [1] || sides [i] == highestSideColliders [0])) { if (hitSides.distance <= sides [i].distance + wall.wallSlideAdditionalDistance) { wallSlideCount++; wallSlideDirection = sides[i].direction; if (sides [i] == highestSideColliders [1]) { actualWallCollider = highestSideColliders [1]; } else { actualWallCollider = highestSideColliders [0]; } } } } // Check for platforms, but only if we are within collider distance + skinSize if (hitSides.distance <= sides [i].distance + movement.skinSize) { Platform platform = hitSides.collider.gameObject.GetComponent<Platform> (); if (platform != null) { platform.DoAction (sides [i], this); } } } // Stop movement, but only if we are within collider distance if (hitSides.distance <= sides [i].distance) { float tmpForceSide = (hitSides.normal * (sides [i].distance - hitSides.distance)).x; if (tmpForceSide > Mathf.Abs (forceSide) || tmpForceSide * -1 > Mathf.Abs (forceSide)) { forceSide = tmpForceSide; // TODO Remove this after adequate testing break; } } } } // Check for ledge hang if (ledgeHanging.canLedgeHang && ledgeHangHitCount == 1 && !IsSwimming) { bool stopLedgeHang = false; if (ledgeHangDirection == RC_Direction.LEFT) { if (ledgeHanging.grabOnlyInFacingDirection && CurrentDirection >= 0) { stopLedgeHang = true; } if (actualLedgeCollider.IsColliding (1 << backgroundLayer, ledgeHanging.edgeDetectionOffset.x + ledgeHanging.autoGrabDistance, ledgeHanging.edgeDetectionOffset.y)) { stopLedgeHang = true; } if (!stopLedgeHang && ledgeHangCollider.bounds.max.y < myTransform.position.y + actualLedgeCollider.offset.y + ledgeHanging.graspPoint + ledgeHanging.graspLeeway && ledgeHangCollider.bounds.max.y > myTransform.position.y + actualLedgeCollider.offset.y + ledgeHanging.graspPoint - ledgeHanging.graspLeeway) { isLedgeHanging = true; jumpCount = 2; ledgeHangState = LedgeHangingState.TRANSITION; ledgeHangTimer = 0.0f; ledgeHangOriginalPosition = myTransform.position; ledgeHangGoalPosition = ledgeHanging.hangOffset + new Vector3 (ledgeHangCollider.bounds.max.x, ledgeHangCollider.bounds.max.y, myTransform.position.z); // Check for parent Platform platform = ledgeHangCollider.gameObject.GetComponent<Platform> (); if (platform != null) { Transform parentPlatform = platform.ParentOnStand (this); if (parentPlatform != null) { myParent = platform; if (myTransform.parent != parentPlatform) { myTransform.parent = parentPlatform; } ledgeHangGoalPosition += platform.velocity * ledgeHanging.transitionTime; } else { // Force ladder to unparent if we have a "bobble" if (myParent is Ladder) { Unparent(); } } } } } else if (ledgeHangDirection == RC_Direction.RIGHT) { if (ledgeHanging.grabOnlyInFacingDirection && CurrentDirection <= 0) { stopLedgeHang = true; } if (actualLedgeCollider.IsColliding (1 << backgroundLayer, ledgeHanging.edgeDetectionOffset.x + ledgeHanging.autoGrabDistance, ledgeHanging.edgeDetectionOffset.y)) { stopLedgeHang = true; } if (!stopLedgeHang && ledgeHangCollider.bounds.max.y < myTransform.position.y + actualLedgeCollider.offset.y + ledgeHanging.graspPoint + ledgeHanging.graspLeeway && ledgeHangCollider.bounds.max.y > myTransform.position.y + actualLedgeCollider.offset.y + ledgeHanging.graspPoint - ledgeHanging.graspLeeway) { isLedgeHanging = true; jumpCount = 2; ledgeHangState = LedgeHangingState.TRANSITION; ledgeHangTimer = 0.0f; ledgeHangOriginalPosition = myTransform.position; ledgeHangGoalPosition = ledgeHanging.hangOffset + new Vector3 (ledgeHangCollider.bounds.min.x, ledgeHangCollider.bounds.max.y, myTransform.position.z); // Check for parent Platform platform = ledgeHangCollider.gameObject.GetComponent<Platform> (); if (platform != null) { Transform parentPlatform = platform.ParentOnStand (this); if (parentPlatform != null) { myParent = platform; if (myTransform.parent != parentPlatform) { myTransform.parent = parentPlatform; } ledgeHangGoalPosition += platform.velocity * ledgeHanging.transitionTime; } } } } } // Check for wall slide if (wallSlideCount > 0 && velocity.y <= 0.0f) { isWallHolding = true; bool stopWallSlide = true; RaycastHit hit = actualWallCollider.GetCollision(1<<backgroundLayer, wall.edgeDetectionOffset.x); if (hit.collider != null) { currentWallTag = hit.collider.gameObject.tag; } else { currentWallTag = null; } if (actualWallCollider.IsColliding (1 << backgroundLayer, wall.edgeDetectionOffset.x, wall.edgeDetectionOffset.y)) { stopWallSlide = false; } if (wall.wallJumpTag != null && wall.wallJumpTag != "" && currentWallTag != wall.wallJumpTag) stopWallSlide = true; if (!stopWallSlide && wall.canWallSlide && ((wallSlideDirection == RC_Direction.RIGHT && characterInput.x > 0) || (wallSlideDirection == RC_Direction.LEFT && characterInput.x < 0))) { // Support sliding on moving platforms // RaycastHit hit = actualWallCollider.GetCollision(1<< backgroundLayer, 0.1f); isWallSliding = true; State = CharacterState.WALL_SLIDING; // Handle moving platforms if (hit.collider != null) { Platform platform = hit.collider.gameObject.GetComponent<Platform>(); if (platform != null && platform.ParentOnStand(this) != null) { myParent = platform; myTransform.parent = platform.ParentOnStand(this); } } // End cope with moving platforms } } // Move if (forceSide > movement.skinSize) { myTransform.Translate(Mathf.Max(velocity.x * frameTime * -1, forceSide), 0.0f, 0.0f); wallJumpDirection = RC_Direction.LEFT; wallJumpTimer = wall.wallJumpTime; hasPressedJumpForWallJump = false; hasPressedDirectionForWallJump = false; StopCrouch(); } else if (-1 * forceSide > movement.skinSize) { myTransform.Translate(Mathf.Min(velocity.x * frameTime * -1, forceSide), 0.0f, 0.0f); wallJumpDirection = RC_Direction.RIGHT; wallJumpTimer = wall.wallJumpTime; hasPressedJumpForWallJump = false; hasPressedDirectionForWallJump = false; StopCrouch(); } if ((forceSide > 0 && velocity.x < 0) || (forceSide < 0 && velocity.x > 0)) { velocity.x = 0.0f; } // Animation if (grounded) { if ( (velocity.x > movement.walkSpeed && characterInput.x > 0.1f) || (velocity.x < movement.walkSpeed * -1 && characterInput.x < -0.1f)) { State = CharacterState.RUNNING; } else if ( (velocity.x > maxSpeedForIdle && characterInput.x > 0.1f) || (velocity.x < -1 * maxSpeedForIdle && characterInput.x < -0.1f)){ State = CharacterState.WALKING; } else if ( velocity.x > maxSpeedForIdle || velocity.x < -1 * maxSpeedForIdle ){ State = CharacterState.SLIDING; } else { State = CharacterState.IDLE; } } // Reset Drag currentDrag = movement.drag; }
/// <summary> /// Controls movement while hanging from a ledge. /// </summary> private void DoLedgeHang() { switch (ledgeHangState) { case LedgeHangingState.TRANSITION: velocity.y = 0.0f; ledgeHangTimer += frameTime; State = CharacterState.LEDGE_HANGING; myTransform.position = Vector3.Slerp (ledgeHangOriginalPosition, ledgeHangGoalPosition, ledgeHangTimer / ledgeHanging.transitionTime); if (ledgeHangTimer > ledgeHanging.transitionTime) { ledgeHangTimer = 0.0f; ledgeHangState = LedgeHangingState.HANG; myTransform.position = ledgeHangGoalPosition; } break; case LedgeHangingState.HANG: // Fall Off if (characterInput.x > 0.0f && ledgeHangDirection == RC_Direction.LEFT) { isLedgeHanging = false; ledgeDropTimer = ledgeHanging.ledgeDropTime; State = CharacterState.FALLING; } else if (characterInput.x < 0.0f && ledgeHangDirection == RC_Direction.RIGHT) { isLedgeHanging = false; ledgeDropTimer = ledgeHanging.ledgeDropTime; State = CharacterState.FALLING; }// Drop Off else if (characterInput.y < 0.0f) { isLedgeHanging = false; ledgeDropTimer = ledgeHanging.ledgeDropTime; State = CharacterState.FALLING; } // Jump Off else if (characterInput.jumpButtonDown) { if (ledgeHanging.jumpOnlyInOppositeDirection) { oppositeDirectionTimer = ledgeHanging.oppositeDirectionTime; if (ledgeHangDirection == RC_Direction.LEFT) velocity.x = movement.walkSpeed; else if (ledgeHangDirection == RC_Direction.RIGHT) velocity.x = -1 * movement.walkSpeed; } isLedgeHanging = false; velocity.y = ledgeHanging.jumpVelocity; jumpCount = 2; Unparent(); startedClimbing = false; jumpButtonTimer = jump.jumpTimer; jumpHeldTimer = 0.0f; ledgeDropTimer = ledgeHanging.ledgeDropTime; if (ledgeHanging.jumpVelocity > 0.0f) { State = CharacterState.JUMPING; } else { State = CharacterState.FALLING; } } // Climb else if (characterInput.y > 0.0f) { ledgeHangState = LedgeHangingState.CLIMBING; ledgeHangTimer = 0.0f; } break; case LedgeHangingState.CLIMBING: ledgeHangTimer += frameTime; if (ledgeHangTimer > ledgeHanging.climbTime) { ledgeHangTimer = 0.0f; State = CharacterState.LEDGE_CLIMB_FINISHED; ledgeHangState = LedgeHangingState.FINISHED; } else { State = CharacterState.LEDGE_CLIMBING; } break; case LedgeHangingState.FINISHED: isLedgeHanging = false; if (ledgeHangDirection == RC_Direction.RIGHT) { myTransform.Translate (ledgeHanging.climbOffset); } else if (ledgeHangDirection == RC_Direction.LEFT) { myTransform.Translate (new Vector3(-1 * ledgeHanging.climbOffset.x, ledgeHanging.climbOffset.y, ledgeHanging.climbOffset.z)); } State = CharacterState.IDLE; break; } }
public override void UpdateBehaviour(float dt) { bool isTag = CharacterStateController.Animator.GetCurrentAnimatorStateInfo(0).tagHash == StateNameHash; switch (state) { // case LedgeHangingState.Entering: // CharacterActor.Position = targetPosition; // CharacterStateController.UseRootMotion = true; // state = LedgeHangingState.Idle; // break; // // case LedgeHangingState.WaitingForAnimator: // // // The transition has ended. // // if( isTag ) // // { // // } // // break; case LedgeHangingState.Idle: if (CharacterActions.movement.Up || autoClimbUp || forceAutoClimbUp) { state = LedgeHangingState.TopUp; CharacterStateController.UseRootMotion = true; CharacterStateController.Animator.SetTrigger(topUpParameter); } else if (CharacterActions.movement.Down) { forceExit = true; } break; case LedgeHangingState.TopUp: // CharacterStateController.Animator.MatchTarget( ( leftHitInfo.point + rightHitInfo.point ) / 2f , AvatarTarget.Root , 0.7f , 0.95f ); if (CharacterStateController.Animator.GetCurrentAnimatorStateInfo(0).IsName("Exit")) { forceExit = true; CharacterActor.ForceGrounded(); } // if( !isTag ) // { // forceExit = true; // CharacterActor.ForceGrounded(); // } break; } }