private void TriggerMovementSounds(FootstepSurfaceInfo surfaceInfo, bool isLeftFoot) { MovementType movementType; if (isCrouched) { if (currentAnimatorVelocity >= runLimit + crouchedRunAdjustment) { if (isGoingUpTheStairs) { movementType = MovementType.RunCrouchedStairsUp; } else if (isGoingDownTheStairs) { movementType = MovementType.RunCrouchedStairsDown; } else { movementType = MovementType.RunCrouched; } } else { if (isGoingUpTheStairs) { movementType = MovementType.WalkCrouchedStairsUp; } else if (isGoingDownTheStairs) { movementType = MovementType.WalkCrouchedStairsDown; } else { movementType = MovementType.WalkCrouched; } } } else if (currentAnimatorVelocity >= runLimit) { if (isGoingUpTheStairs) { movementType = MovementType.RunStairsUp; } else if (isGoingDownTheStairs) { movementType = MovementType.RunStairsDown; } else { movementType = MovementType.Run; } } else { if (isGoingUpTheStairs) { movementType = MovementType.WalkStairsUp; } else if (isGoingDownTheStairs) { movementType = MovementType.WalkStairsDown; } else { movementType = MovementType.Walk; } } if (isLeftFoot) { PlayFootstep(movementType, surfaceInfo.surfaceType, leftFootPosition); if (foleyPosition != null) { PlayFoley(movementType, foleyPosition); } if (surfaceInfo.layerType != SurfaceLayerType.None) { PlayLayer(movementType, surfaceInfo.layerType, leftFootPosition); } } else { PlayFootstep(movementType, surfaceInfo.surfaceType, rightFootPosition); if (foleyPosition != null) { PlayFoley(movementType, foleyPosition); } if (surfaceInfo.layerType != SurfaceLayerType.None) { PlayLayer(movementType, surfaceInfo.layerType, rightFootPosition); } } }
private void FootstepUpdate() { // In addition to aborting the regular footstep update check when the player is not grounded one should add all the other applicable game specific checks here. // <- Could involve movement types such as climbing, vaulting or any kind of deterministic animation sequence with their own sfx triggering requirements. if (!isGrounded) { previousLeftFootDirection = 0; previousRightFootDirection = 0; previousLeftFootHeight = 0; previousRightFootHeight = 0; hasRaisedLeftFoot = false; hasRaisedRightFoot = false; isMoving = false; StopAllCoroutines(); sidleCooldownInProgress = false; return; } float leftHeight = GetFootHeight(true); float rightHeight = GetFootHeight(false); if (leftHeight == int.MaxValue || rightHeight == int.MaxValue || leftHeight < 0 || rightHeight < 0) { // Feet height checks failed. return; } #if UNITY_EDITOR var roundLeft = Math.Round(leftHeight, 2); leftHeightDebug = roundLeft.ToString(); var roundRight = Math.Round(rightHeight, 2); rightHeightDebug = roundRight.ToString(); var roundVelocity = Math.Round(currentAnimatorVelocity, 2); velocityDebug = roundVelocity.ToString(); #endif bool footstepTriggered = false; int leftFootDirection; int rightFootDirection; if (leftHeight > previousLeftFootHeight) { leftFootDirection = 1; } else if (leftHeight < previousLeftFootHeight) { leftFootDirection = -1; } else { leftFootDirection = previousLeftFootDirection; } if (rightHeight > previousRightFootHeight) { rightFootDirection = 1; } else if (rightHeight < previousRightFootHeight) { rightFootDirection = -1; } else { rightFootDirection = previousRightFootDirection; } if (leftHeight > thresholdHeight && !hasRaisedLeftFoot) { hasRaisedLeftFoot = true; } else if (isCrouched && leftHeight > (thresholdHeight + crouchedThresholdAdjustment) && !hasRaisedLeftFoot) { hasRaisedLeftFoot = true; } else if (leftHeight < distanceWhenGrounded && hasRaisedLeftFoot) { hasRaisedLeftFoot = false; if (leftFootDirection < 0) { if (currentAnimatorVelocity > walkLimit) { footstepTriggered = true; FootstepSurfaceInfo surfaceInfo = CheckCurrentSurface(leftFootPosition); TriggerMovementSounds(surfaceInfo, true); } else if (isCrouched && currentAnimatorVelocity >= (walkLimit + crouchedWalkAdjustment)) { footstepTriggered = true; FootstepSurfaceInfo surfaceInfo = CheckCurrentSurface(leftFootPosition); TriggerMovementSounds(surfaceInfo, true); } } } if (rightHeight > thresholdHeight && !hasRaisedRightFoot) { hasRaisedRightFoot = true; } else if (isCrouched && rightHeight > (thresholdHeight + crouchedThresholdAdjustment) && !hasRaisedRightFoot) { hasRaisedRightFoot = true; } else if (rightHeight < distanceWhenGrounded && hasRaisedRightFoot) { hasRaisedRightFoot = false; if (rightFootDirection < 0) { if (currentAnimatorVelocity > walkLimit) { footstepTriggered = true; FootstepSurfaceInfo surfaceInfo = CheckCurrentSurface(rightFootPosition); TriggerMovementSounds(surfaceInfo, false); } else if (isCrouched && currentAnimatorVelocity >= (walkLimit + crouchedWalkAdjustment)) { footstepTriggered = true; FootstepSurfaceInfo surfaceInfo = CheckCurrentSurface(rightFootPosition); TriggerMovementSounds(surfaceInfo, false); } } } previousLeftFootDirection = leftFootDirection; previousRightFootDirection = rightFootDirection; previousLeftFootHeight = leftHeight; previousRightFootHeight = rightHeight; if (footstepTriggered) { return; } // SIDLE: // Animator velocity threshold for the character to be considered to be moving is adjustable. // <- The character needs to have stopped before the siddle sfx can be triggered again. // <- In addition, there is a cooldown period before the siddle sfx can be triggered again. // <- This is because momentary hovering around the threshold value can lead to 'machine gun' -triggering of the sfx. // Since the sidle triggering is not foot-specific, as a compromise we will use the surface and position data of the left foot here. // <- Should not matter too much, unless the character is very large, in which case splitting the feet siddling sounds to foot-specific // events could come into consideration. if (currentAnimatorVelocity > movingThresholdVelocity && !isMoving && !sidleCooldownInProgress) { FootstepSurfaceInfo surfaceInfo = CheckCurrentSurface(leftFootPosition); if (isCrouched) { if (foleyPosition != null) { PlayFoley(MovementType.SidleCrouched, foleyPosition); } PlayFootstep(MovementType.SidleCrouched, surfaceInfo.surfaceType, leftFootPosition); PlayLayer(MovementType.SidleCrouched, surfaceInfo.layerType, leftFootPosition); } else { if (foleyPosition != null) { PlayFoley(MovementType.Sidle, foleyPosition); } PlayFootstep(MovementType.Sidle, surfaceInfo.surfaceType, leftFootPosition); PlayLayer(MovementType.Sidle, surfaceInfo.layerType, leftFootPosition); } isMoving = true; sidleCooldownInProgress = true; StartCoroutine(Cooldown(sidleCooldownDuration)); } else if (currentAnimatorVelocity < movingThresholdVelocity && isMoving) { isMoving = false; } }