/// <summary> /// (Called by KinematicCharacterMotor during its update cycle) /// This is called before the character begins its movement update /// </summary> public override void BeforeCharacterUpdate(float deltaTime) { // Handle detecting water surfaces { // Do a character overlap test to detect water surfaces if (Motor.CharacterOverlap(Motor.TransientPosition, Motor.TransientRotation, _probedColliders, WaterLayer, QueryTriggerInteraction.Collide) > 0) { // If a water surface was detected if (_probedColliders[0] != null) { // If the swimming reference point is inside the box, make sure we are in swimming state if (Physics.ClosestPoint(SwimmingReferencePoint.position, _probedColliders[0], _probedColliders[0].transform.position, _probedColliders[0].transform.rotation) == SwimmingReferencePoint.position) { if (CurrentCharacterState == CharacterState.Default) { TransitionToState(CharacterState.Swimming); _waterZone = _probedColliders[0]; } } // otherwise; default state else { if (CurrentCharacterState == CharacterState.Swimming) { TransitionToState(CharacterState.Default); } } } } } }
/// <summary> /// (Called by KinematicCharacterMotor during its update cycle) /// This is called after the character has finished its movement update /// </summary> public override void AfterCharacterUpdate(float deltaTime) { switch (CurrentCharacterState) { case CharacterState.Default: { // Handle jump-related values { // Handle jumping pre-ground grace period if (_jumpRequested && _timeSinceJumpRequested > JumpPreGroundingGraceTime) { _jumpRequested = false; } if (AllowJumpingWhenSliding ? Motor.GroundingStatus.FoundAnyGround : Motor.GroundingStatus.IsStableOnGround) { // If we're on a ground surface, reset jumping values if (!_jumpedThisFrame) { _jumpConsumed = false; } _timeSinceLastAbleToJump = 0f; } else { // Keep track of time since we were last able to jump (for grace period) _timeSinceLastAbleToJump += deltaTime; } } // Handle uncrouching if (_isCrouching && !_shouldBeCrouching) { // Do an overlap test with the character's standing height to see if there are any obstructions Motor.SetCapsuleDimensions(0.5f, 2f, 1f); if (Motor.CharacterOverlap( Motor.TransientPosition, Motor.TransientRotation, _probedColliders, Motor.CollidableLayers, QueryTriggerInteraction.Ignore) > 0) { // If obstructions, just stick to crouching dimensions Motor.SetCapsuleDimensions(0.5f, 1f, 0.5f); } else { // If no obstructions, uncrouch MeshRoot.localScale = new Vector3(1f, 1f, 1f); _isCrouching = false; } } break; } } }
public override void AfterCharacterUpdate(float deltaTime) { // Handle jumping pre-ground grace period if (jumpRequested && timeSinceJumpRequested > JumpPreGroundingGraceTime) { jumpRequested = false; } if (AllowJumpingWhenSliding ? Motor.GroundingStatus.FoundAnyGround : Motor.GroundingStatus.IsStableOnGround) { // If we're on a ground surface, reset jumping values if (!jumpedThisFrame) { doubleJumpConsumed = false; jumpConsumed = false; } timeSinceLastAbleToJump = 0f; } else { // Keep track of time since we were last able to jump (for grace period) timeSinceLastAbleToJump += deltaTime; } // Handle landing and leaving ground if (Motor.GroundingStatus.IsStableOnGround && !Motor.LastGroundingStatus.IsStableOnGround) { OnLanded(); } else if (!Motor.GroundingStatus.IsStableOnGround && Motor.LastGroundingStatus.IsStableOnGround) { OnLeaveStableGround(); } // Handle uncrouching if (IsCrouching && !shouldBeCrouching) { // Do an overlap test with the character's standing height to see if there are any obstructions Motor.SetCapsuleDimensions(C.BaseCharacterRadius, C.BaseCharacterHeight, 0f); if (Motor.CharacterOverlap( Motor.TransientPosition, Motor.TransientRotation, probedColliders, Motor.CollidableLayers, QueryTriggerInteraction.Ignore) > 0) { // If obstructions, just stick to crouching dimensions Motor.SetCapsuleDimensions(C.BaseCharacterRadius, C.BaseCharacterHeight, C.BaseCharacterHeight / 2f); } else { // If no obstructions, uncrouch C.MeshRoot.localScale = new Vector3(1f, 1f, 1f); IsCrouching = false; } } }
/// <summary> /// (Called by KinematicCharacterMotor during its update cycle) /// This is called after the character has finished its movement update /// </summary> public override void AfterCharacterUpdate(float deltaTime) { switch (CurrentCharacterState) { case CharacterState.Default: { // Handle jump-related values { // Handle jumping pre-ground grace period if (_jumpRequested && _timeSinceJumpRequested > JumpPreGroundingGraceTime) { _jumpRequested = false; } if (AllowJumpingWhenSliding ? Motor.GroundingStatus.FoundAnyGround : Motor.GroundingStatus.IsStableOnGround) { // If we're on a ground surface, reset jumping values if (!_jumpedThisFrame) { _doubleJumpConsumed = false; _jumpConsumed = false; } _timeSinceLastAbleToJump = 0f; } else { // Keep track of time since we were last able to jump (for grace period) _timeSinceLastAbleToJump += deltaTime; } } // Handle uncrouching if (_isCrouching && !_shouldBeCrouching) { // Do an overlap test with the character's standing height to see if there are any obstructions Motor.SetCapsuleDimensions(0.5f, 2f, 1f); if (Motor.CharacterOverlap( Motor.TransientPosition, Motor.TransientRotation, _probedColliders, Motor.CollidableLayers, QueryTriggerInteraction.Ignore) > 0) { // If obstructions, just stick to crouching dimensions Motor.SetCapsuleDimensions(0.5f, 1f, 0.5f); } else { // If no obstructions, uncrouch MeshRoot.localScale = new Vector3(1f, 1f, 1f); _isCrouching = false; } } break; } case CharacterState.Climbing: { switch (_climbingState) { case ClimbingState.Climbing: // Detect getting off ladder during climbing _activeLadder.ClosestPointOnLadderSegment(Motor.TransientPosition, out _onLadderSegmentState); if (Mathf.Abs(_onLadderSegmentState) > 0.05f) { _climbingState = ClimbingState.DeAnchoring; // If we're higher than the ladder top point if (_onLadderSegmentState > 0) { _ladderTargetPosition = _activeLadder.TopReleasePoint.position; _ladderTargetRotation = _activeLadder.TopReleasePoint.rotation; } // If we're lower than the ladder bottom point else if (_onLadderSegmentState < 0) { _ladderTargetPosition = _activeLadder.BottomReleasePoint.position; _ladderTargetRotation = _activeLadder.BottomReleasePoint.rotation; } } break; case ClimbingState.Anchoring: case ClimbingState.DeAnchoring: // Detect transitioning out from anchoring states if (_anchoringTimer >= AnchoringDuration) { if (_climbingState == ClimbingState.Anchoring) { _climbingState = ClimbingState.Climbing; } else if (_climbingState == ClimbingState.DeAnchoring) { TransitionToState(CharacterState.Default); } } // Keep track of time since we started anchoring _anchoringTimer += deltaTime; break; } break; } } }
/// <summary> /// This is called every frame by MyPlayer in order to tell the character what its inputs are /// </summary> public void SetInputs(ref PlayerCharacterInputs inputs) { // Handle ladder transitions _ladderUpDownInput = inputs.MoveAxisForward; if (inputs.ClimbLadder) { if (Motor.CharacterOverlap(Motor.TransientPosition, Motor.TransientRotation, _probedColliders, InteractionLayer, QueryTriggerInteraction.Collide) > 0) { if (_probedColliders[0] != null) { // Handle ladders MyLadder ladder = _probedColliders[0].gameObject.GetComponent <MyLadder>(); if (ladder) { // Transition to ladder climbing state if (CurrentCharacterState == CharacterState.Default) { _activeLadder = ladder; TransitionToState(CharacterState.Climbing); } // Transition back to default movement state else if (CurrentCharacterState == CharacterState.Climbing) { _climbingState = ClimbingState.DeAnchoring; _ladderTargetPosition = Motor.TransientPosition; _ladderTargetRotation = _rotationBeforeClimbing; } } } } } // Clamp input Vector3 moveInputVector = Vector3.ClampMagnitude(new Vector3(inputs.MoveAxisRight, 0f, inputs.MoveAxisForward), 1f); // Calculate camera direction and rotation on the character plane Vector3 cameraPlanarDirection = Vector3.ProjectOnPlane(inputs.CameraRotation * Vector3.forward, Motor.CharacterUp).normalized; if (cameraPlanarDirection.sqrMagnitude == 0f) { cameraPlanarDirection = Vector3.ProjectOnPlane(inputs.CameraRotation * Vector3.up, Motor.CharacterUp).normalized; } Quaternion cameraPlanarRotation = Quaternion.LookRotation(cameraPlanarDirection, Motor.CharacterUp); switch (CurrentCharacterState) { case CharacterState.Default: { // Move and look inputs _moveInputVector = cameraPlanarRotation * moveInputVector; _lookInputVector = cameraPlanarDirection; // Jumping input if (inputs.JumpDown) { _timeSinceJumpRequested = 0f; _jumpRequested = true; } // Crouching input if (inputs.CrouchDown) { _shouldBeCrouching = true; if (!_isCrouching) { _isCrouching = true; Motor.SetCapsuleDimensions(0.5f, 1f, 0.5f); MeshRoot.localScale = new Vector3(1f, 0.5f, 1f); } } else if (inputs.CrouchUp) { _shouldBeCrouching = false; } break; } } }
public override void AfterCharacterUpdate(float deltaTime) { // Handle jump-related values { // Handle jumping pre-ground grace period if (_jumpRequested && _timeSinceJumpRequested > JumpPreGroundingGraceTime) { _jumpRequested = false; } if (AllowJumpingWhenSliding ? Motor.GroundingStatus.FoundAnyGround : Motor.GroundingStatus.IsStableOnGround) { // If we're on a ground surface, reset jumping values if (!_jumpedThisFrame) { _doubleJumpConsumed = false; _jumpConsumed = false; } _timeSinceLastAbleToJump = 0f; } else { // Keep track of time since we were last able to jump (for grace period) _timeSinceLastAbleToJump += deltaTime; } } // Grounding considerations if (Motor.GroundingStatus.IsStableOnGround && !Motor.LastGroundingStatus.IsStableOnGround) { if (!IsPreFrameMustUnground) { Callback_OnLanded?.Invoke(); } IsPreFrameMustUnground = false; if (IsJumped) { Callback_OnJumpLanded?.Invoke(); } IsJumped = false; IsDoubbleJumped = false; } else if (!Motor.GroundingStatus.IsStableOnGround && Motor.LastGroundingStatus.IsStableOnGround) { Callback_OnLeaveStableGround?.Invoke(); } if (AllowJumpingWhenSliding ? Motor.GroundingStatus.FoundAnyGround : Motor.GroundingStatus.IsStableOnGround) { //_jumpConsumed = false; _timeSinceLastAbleToJump = 0f; } else { _timeSinceLastAbleToJump += deltaTime; } // Handle uncrouching if (_isTryingToUncrouch) { // Do an overlap test with the character's standing height to see if there are any obstructions SetDimensions(Radius, Height); if (Motor.CharacterOverlap( Motor.TransientPosition, Motor.TransientRotation, _probedColliders, Motor.CollidableLayers, QueryTriggerInteraction.Ignore) > 0) { // If obstructions, just stick to crouching dimensions SetDimensions(Radius, HalfHeight); } else { // If no obstructions, uncrouch //MeshRoot.localScale = new Vector3(1f, 1f, 1f); _isTryingToUncrouch = false; } } // Reset root motion deltas RootMotionPositionDelta = Vector3.zero; RootMotionRotationDelta = Quaternion.identity; }