nantoaqui
New member
Hello there!
I've been trying to create a Grapple Hook ability for the past few weeks but i don't know how to make it work on UCC.
I followed these articles and manage to implement the pendulum using a normal rigidbody. For some reason the same logic results in weird behavior when applied to the player.
Here's the code for the pendulum force calculation:
Here's the code applied to a rigidbody:
As you can see it works perfectly fine.
Now the similar logic applied to UCC:
The output:
I would expect the character to have a similar behavior as the rigidbody.
Could you help me solve this problem?
Thank you!
I've been trying to create a Grapple Hook ability for the past few weeks but i don't know how to make it work on UCC.
I followed these articles and manage to implement the pendulum using a normal rigidbody. For some reason the same logic results in weird behavior when applied to the player.
Swinging Physics for Player Movement (As Seen in Spider-Man 2 and Energy Hook)
Hi! I'm Jamie Fristrom of Happion Laboratories. You may remember me from such games as Die By The Sword, Spider-Man 2, and Schizoid... all of which, in one form or another, included rope. I've...
gamedevelopment.tutsplus.com
Here's the code for the pendulum force calculation:
Code:
using UnityEngine;
namespace ChromaSuit.Locomotion
{
public class WebSwingingPendulum
{
public float ArcForce = 2f;
public float VelocityClampMin = 20f;
public float VelocityClampMax = 30f;
public float StretchWebFactor = 20f;
public float SwingingMinAngle = -60f;
public float SwingingMaxAngle = 60f;
public float StretchWebAngle = 90f;
public float BottomSpeed = 50f;
private Vector3 playerLocation = Vector3.zero;
private Vector3 playerVelocity = Vector3.zero;
private Transform player;
public void SetValues(Transform Player, Vector3 velocity)
{
player = Player;
playerLocation = player.position;
playerVelocity = velocity;
}
public Vector3 CalculateWebSwinForce(Vector3 anchorPoint)
{
var playerPosition = playerLocation + playerVelocity * Time.deltaTime;
var dir = playerPosition - anchorPoint;
var tetherLength = dir.magnitude;
//var force = SwingArcForce(anchorPoint);
var force = SwingArcForce(anchorPoint) + SpeedsUpSwingAtBottom(anchorPoint);
// force = force.normalized * _pendulum.Radius;
return force;
}
private Vector3 SwingArcForce(Vector3 anchorPosition)
{
var playerPosition = playerLocation;
var dir = playerPosition - anchorPosition;
var force = ArcForce / dir.magnitude;
force = Mathf.Clamp(force, 1f, 4f);
return SwingArcForceFormula(anchorPosition, VelocityClampMin, VelocityClampMax, force);
}
public Vector3 ClampMagnitude(Vector3 v, float max, float min)
{
double sm = v.sqrMagnitude;
if (sm > (double) max * (double) max) return v.normalized * max;
else if (sm < (double) min * (double) min) return v.normalized * min;
return v;
}
private Vector3 SwingArcForceFormula(Vector3 anchorPosition, float velocityClampMin, float velocityClampMax,
float reduceForceByFactorX)
{
var clampedVelocity = ClampMagnitude(playerVelocity, velocityClampMin, velocityClampMax);
//var clampedVelocity = Vector3.ClampMagnitude(playerVelocity, velocityClampMax);
var playerPosition = playerLocation;
var dir = anchorPosition - playerPosition;
var projection = Vector3.Dot(clampedVelocity, dir);
var x = projection * dir.normalized;
x = x * -2;
x = x / reduceForceByFactorX;
var stretchedWeb = StretchWeb(anchorPosition) * dir.normalized;
return x + stretchedWeb;
}
private float StretchWeb(Vector3 anchorPosition)
{
var swingingAngle = CalculateSwingingAngle(anchorPosition);
return (swingingAngle / StretchWebAngle) * StretchWebFactor;
}
private Vector3 SpeedsUpSwingAtBottom(Vector3 anchorPosition)
{
var velocity = playerVelocity.normalized * BottomSpeed;
var swingingAngle = CalculateSwingingAngle(anchorPosition);
//Debug.Log($"swingingAngle: {swingingAngle}");
if (swingingAngle > SwingingMinAngle && swingingAngle < SwingingMaxAngle)
{
return velocity;
}
return Vector3.zero;
}
private const float RAD_TO_DEG = 57.2957795130823f;
public float AngleOffAroundAxis(Vector3 v, Vector3 forward, Vector3 axis, bool clockwise = false)
{
Vector3 right;
if (clockwise)
{
right = Vector3.Cross(forward, axis);
forward = Vector3.Cross(axis, right);
}
else
{
right = Vector3.Cross(axis, forward);
forward = Vector3.Cross(right, axis);
}
return Mathf.Atan2(Vector3.Dot(v, right), Vector3.Dot(v, forward)) * RAD_TO_DEG;
}
private float CalculateSwingingAngle(Vector3 anchorPosition)
{
Vector3 targetDir = anchorPosition - playerLocation;
Vector3 forward = player.up;
float angle = Vector3.SignedAngle(targetDir, forward, player.right);
return angle;
}
}
}
Here's the code applied to a rigidbody:
Code:
using ChromaSuit.Locomotion;
using UnityEngine;
public class Test22 : MonoBehaviour
{
public Rigidbody Bob;
public Transform Anchor;
public float ArcForce = 2f;
public float VelocityClampMin = 20f;
public float VelocityClampMax = 30f;
public float StretchWebFactor = 20f;
public float SwingingMinAngle = -60f;
public float SwingingMaxAngle = 60f;
public float StretchWebAngle = 90f;
public float BottomSpeed = 50f;
private WebSwingingPendulum webSwingingPendulum;
private bool m_Tethered = false;
private float m_TetherLength;
private Vector3 m_TetherPoint;
void Start()
{
m_Tethered = true;
m_TetherPoint = Anchor.position;
m_TetherLength = Vector3.Distance(m_TetherPoint, Bob.position);
webSwingingPendulum = new WebSwingingPendulum();
}
void FixedUpdate()
{
webSwingingPendulum.ArcForce = ArcForce;
webSwingingPendulum.VelocityClampMin = VelocityClampMin;
webSwingingPendulum.VelocityClampMax = VelocityClampMax;
webSwingingPendulum.StretchWebFactor = StretchWebFactor;
webSwingingPendulum.SwingingMinAngle = SwingingMinAngle;
webSwingingPendulum.SwingingMaxAngle = SwingingMaxAngle;
webSwingingPendulum.StretchWebAngle = StretchWebAngle;
webSwingingPendulum.BottomSpeed = BottomSpeed;
webSwingingPendulum.SetValues(Bob.transform, Bob.velocity);
var force = webSwingingPendulum.CalculateWebSwinForce(Anchor.transform.position);
Bob.AddForce(force);
Vector3 directionToGrapple = Vector3.Normalize(m_TetherPoint - Bob.position);
float currentDistanceToGrapple = Vector3.Distance(m_TetherPoint, Bob.position);
if (currentDistanceToGrapple > m_TetherLength)
{
Bob.position = m_TetherPoint - directionToGrapple * m_TetherLength;
}
}
}
As you can see it works perfectly fine.
Now the similar logic applied to UCC:
Code:
using Opsive.UltimateCharacterController.Character.Abilities;
using UnityEngine;
namespace ChromaSuit.Locomotion
{
public class GrappleHook : Ability
{
public Transform Anchor;
public float ArcForce = 2f;
public float VelocityClampMin = 20f;
public float VelocityClampMax = 30f;
public float StretchWebFactor = 20f;
public float SwingingMinAngle = -60f;
public float SwingingMaxAngle = 60f;
public float StretchWebAngle = 90f;
public float BottomSpeed = 50f;
private WebSwingingPendulum webSwingingPendulum;
private bool m_Tethered = false;
private float m_TetherLength;
private Vector3 m_TetherPoint;
protected override void AbilityStarted()
{
m_Tethered = true;
m_TetherPoint = Anchor.position;
m_TetherLength = Vector3.Distance(m_TetherPoint, m_Transform.position);
webSwingingPendulum = new WebSwingingPendulum();
}
public override void Update()
{
base.Update();
webSwingingPendulum.ArcForce = ArcForce;
webSwingingPendulum.VelocityClampMin = VelocityClampMin;
webSwingingPendulum.VelocityClampMax = VelocityClampMax;
webSwingingPendulum.StretchWebFactor = StretchWebFactor;
webSwingingPendulum.SwingingMinAngle = SwingingMinAngle;
webSwingingPendulum.SwingingMaxAngle = SwingingMaxAngle;
webSwingingPendulum.StretchWebAngle = StretchWebAngle;
webSwingingPendulum.BottomSpeed = BottomSpeed;
webSwingingPendulum.SetValues(m_Transform, m_CharacterLocomotion.Velocity);
var force = webSwingingPendulum.CalculateWebSwinForce(Anchor.transform.position);
AddForce(force, 0, true, true);
}
public override void LateUpdate()
{
Vector3 directionToGrapple = Vector3.Normalize(m_TetherPoint - m_Transform.position);
float currentDistanceToGrapple = Vector3.Distance(m_TetherPoint, m_Transform.position);
if (currentDistanceToGrapple > m_TetherLength)
{
m_Transform.position = m_TetherPoint - directionToGrapple * m_TetherLength;
}
}
}
}
The output:
I would expect the character to have a similar behavior as the rigidbody.
Could you help me solve this problem?
Thank you!