// Copyright (C) 2023 Nicholas Maltbie
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
// associated documentation files (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, publish, distribute,
// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System.Collections.Generic;
using nickmaltbie.OpenKCC.Character;
using nickmaltbie.OpenKCC.Utils;
using UnityEngine;
namespace nickmaltbie.OpenKCC.MoleKCCSample
{
///
/// MovementEngine for the mole character to allow the player to
/// walk up and down walls directly.
///
public class MoleMovementEngine : KCCMovementEngine
{
///
/// Have no angle decay in movement up walls.
///
public override float AnglePower => 1.0f;
///
/// Surface normal for overriding up direction.
///
protected Vector3 GroundNormal { get; set; } = -Physics.gravity.normalized;
///
/// Max speed that the player can snap down at.
///
public override float MaxSnapDownSpeed => Mathf.Infinity;
///
/// Override the up direction for the mole.
///
public override Vector3 Up => GroundNormal;
///
public override IEnumerable GetMovement(Vector3 movement)
{
foreach (KCCBounce bounce in base.GetMovement(movement))
{
if (bounce.action == KCCUtils.MovementAction.Bounce)
{
if (bounce.hit == null)
{
yield return bounce;
continue;
}
// If we bounce off a wall perpendicular to the current surface
Vector3 normal = bounce.hit.normal;
float remainingSpeed = bounce.initialMomentum.magnitude - bounce.Movement.magnitude;
if (normal == Vector3.zero)
{
normal = GroundNormal;
}
bool perpendicular = Vector3.Dot(normal, bounce.initialMomentum) <= KCCUtils.Epsilon;
if (perpendicular)
{
bounce.remainingMomentum = GroundNormal * remainingSpeed;
}
else
{
// Rotate the remaining movement
bounce.remainingMomentum = Quaternion.LookRotation(bounce.hit.normal) *
bounce.initialMomentum.normalized * remainingSpeed;
}
GroundNormal = normal;
}
yield return bounce;
}
// Only snap down if the player was grounded before they started
// moving and are not currently trying to move upwards.
if (GroundedState.StandingOnGround && !MovingUp(movement))
{
Vector3 delta = KCCUtils.GetSnapDelta(
transform.position,
transform.rotation,
-Up,
1.0f,
ColliderCast,
layerMask,
skinWidth);
transform.position += Vector3.ClampMagnitude(delta, MaxSnapDownSpeed * unityService.fixedDeltaTime);
}
// Compute the new grounded state
CheckGrounded(false, false);
GroundNormal = GroundedState.SurfaceNormal;
if (GroundNormal == Vector3.zero)
{
GroundNormal = -Physics.gravity.normalized;
}
}
}
}