Somewhat working breaking rope

This commit is contained in:
Michal Pikulski
2025-09-21 23:25:51 +02:00
parent af5dfbb7a2
commit ef3b50b99f
3 changed files with 335 additions and 138 deletions

View File

@@ -1,45 +1,233 @@
using UnityEngine;
using GogoGaga.OptimizedRopesAndCables;
using UnityEngine;
public class RopeEndPhysicsFollower : MonoBehaviour
{
[Tooltip("Tag of the object this endpoint should follow (e.g., 'player' or 'rock')")]
[Header("Target Settings")]
[Tooltip("Tag of the object this endpoint should follow")]
public string targetTag;
[Tooltip("How quickly the endpoint follows the target")] public float followSpeed = 5f;
[Tooltip("How much trailing (0 = instant, 1 = very slow)")] public float trailing = 0.2f;
[Tooltip("Amplitude of fake gravity/flap")] public float oscillationAmplitude = 0.15f;
[Tooltip("Frequency of fake gravity/flap")] public float oscillationFrequency = 2f;
[Tooltip("If true, disable vertical oscillation (useful for player attachment)")]
public bool disableOscillation = true;
[Tooltip("How quickly the endpoint follows the target")]
public float followSpeed = 5f;
[Tooltip("How much trailing (0 = instant, 1 = very slow)")]
public float trailing = 0.2f;
[Header("Physics Simulation")]
[Tooltip("Enable/disable gravity effect")]
public bool useGravity = true;
[Tooltip("Gravity strength")]
public float gravityStrength = 9.8f;
[Tooltip("How strongly the rope attempts to hang vertically")]
public float verticalHangStrength = 2f;
[Tooltip("Damping for physics movement (higher = less bouncy)")]
public float damping = 0.3f;
[Tooltip("Initial downward impulse when gravity is enabled")]
public float initialFallImpulse = 2.0f;
[Tooltip("Force a Y position reset on start to ensure falling occurs")]
public bool forceYReset = true;
// Private variables
private Transform target;
private Vector3 velocity;
private Vector3 offset;
private float oscillationTime;
private Vector2 physicsVelocity;
private Vector2 offset;
private Vector3 lastTargetPosition;
private bool initialized = false;
private bool debugLog = true;
// Rope reference to get the actual rope length
private Rope attachedRope;
private float maxDistance;
void Start()
{
// Find the Rope component on the same GameObject or parent
attachedRope = GetComponent<Rope>();
if (attachedRope == null)
{
attachedRope = GetComponentInParent<Rope>();
}
// If we still couldn't find it, look for it on a child GameObject
if (attachedRope == null)
{
attachedRope = GetComponentInChildren<Rope>();
}
// Look for a rope attached to this endpoint
if (attachedRope == null)
{
// Find any rope that has this transform as an endpoint
Rope[] allRopes = FindObjectsOfType<Rope>();
foreach (var rope in allRopes)
{
if (rope.EndPoint == transform || rope.StartPoint == transform)
{
attachedRope = rope;
break;
}
}
}
// Set max distance based on rope length if we found a rope
if (attachedRope != null)
{
maxDistance = attachedRope.ropeLength;
if (debugLog) Debug.Log($"[RopeEndPhysicsFollower] Found attached rope with length: {maxDistance}");
}
else
{
// Default fallback value if no rope is found
maxDistance = 2f;
if (debugLog) Debug.Log("[RopeEndPhysicsFollower] No attached rope found, using default max distance");
}
if (!string.IsNullOrEmpty(targetTag))
{
GameObject found = GameObject.FindGameObjectWithTag(targetTag);
if (found) target = found.transform;
}
offset = transform.position - (target ? target.position : Vector3.zero);
oscillationTime = Random.value * Mathf.PI * 2f; // randomize phase
// Initialize offset and velocities
if (target)
{
// Only store horizontal offset, not vertical
Vector2 offsetVec = transform.position - target.position;
offset.x = offsetVec.x;
offset.y = 0; // Don't preserve vertical offset for gravity simulation
lastTargetPosition = target.position;
// Apply initial falling impulse if using gravity
if (useGravity)
{
physicsVelocity = new Vector2(0, -initialFallImpulse);
// Force an initial position change to ensure things start moving
if (forceYReset)
{
Vector3 pos = transform.position;
pos.y = target.position.y; // Reset to target's Y position
transform.position = pos;
}
if (debugLog) Debug.Log($"[RopeEndPhysicsFollower] Initialized with tag {targetTag}, gravity enabled, initial Y velocity: {physicsVelocity.y}");
}
}
else
{
offset = Vector2.zero;
lastTargetPosition = transform.position;
if (debugLog) Debug.Log($"[RopeEndPhysicsFollower] No target found with tag {targetTag}");
}
velocity = Vector3.zero;
initialized = true;
}
void Update()
{
if (!target) return;
// Smooth follow with trailing
Vector3 desired = target.position + offset;
transform.position = Vector3.SmoothDamp(transform.position, desired, ref velocity, trailing, followSpeed);
// Calculate deltaTime for physics stability
float deltaTime = Time.deltaTime;
// Add fake gravity/flap (only if oscillation is enabled)
if (!disableOscillation)
// Get target velocity
Vector3 targetVelocity = (target.position - lastTargetPosition) / deltaTime;
lastTargetPosition = target.position;
// Basic position the endpoint should be at based on target position and horizontal offset
Vector3 basePosition = target.position + new Vector3(offset.x, 0, 0);
// Apply physics simulation if enabled
if (useGravity)
{
oscillationTime += Time.deltaTime * oscillationFrequency;
float yOsc = Mathf.Sin(oscillationTime) * oscillationAmplitude;
transform.position += new Vector3(0, yOsc, 0);
// Get the straight-line distance between target and this transform
float currentDistance = Vector2.Distance(
new Vector2(transform.position.x, transform.position.y),
new Vector2(target.position.x, target.position.y)
);
// Current position relative to target
Vector2 relativePos = new Vector2(
transform.position.x - target.position.x,
transform.position.y - target.position.y
);
// Normalized direction from target to this transform
Vector2 directionToTarget = relativePos.normalized;
// Apply forces:
// 1. Gravity - always pulls down
physicsVelocity.y -= gravityStrength * deltaTime;
// 2. Vertical hanging force - try to align X with target when stationary
if (Mathf.Abs(targetVelocity.x) < 0.1f)
{
float xOffset = transform.position.x - target.position.x;
physicsVelocity.x -= xOffset * verticalHangStrength * deltaTime;
}
// 3. Rope length constraint - apply a force toward the target if we're exceeding the rope length
if (currentDistance > maxDistance)
{
// Calculate constraint force proportional to how much we're exceeding the rope length
float exceededDistance = currentDistance - maxDistance;
// Apply a stronger constraint force the more we exceed the max distance
Vector2 constraintForce = -directionToTarget * exceededDistance * 10f;
// Apply to velocity
physicsVelocity += constraintForce * deltaTime;
if (debugLog && Time.frameCount % 60 == 0)
{
Debug.Log($"[RopeEndPhysicsFollower] Exceeding max distance: {exceededDistance}, applying constraint");
}
}
// Apply damping to physics velocity
physicsVelocity *= (1f - damping * deltaTime);
// Log physics state periodically for debugging
if (debugLog && Time.frameCount % 60 == 0)
{
Debug.Log($"[RopeEndPhysicsFollower] Y position: {transform.position.y}, Y velocity: {physicsVelocity.y}, Distance: {currentDistance}/{maxDistance}");
}
// Apply physics velocity to position
Vector3 newPos = transform.position;
newPos.x += physicsVelocity.x * deltaTime;
newPos.y += physicsVelocity.y * deltaTime;
transform.position = newPos;
// Final distance check - hard constraint to ensure we never exceed the rope length
// This prevents numerical instability from causing the rope to stretch
float finalDistance = Vector2.Distance(
new Vector2(transform.position.x, transform.position.y),
new Vector2(target.position.x, target.position.y)
);
if (finalDistance > maxDistance)
{
// Calculate the direction from target to this transform
Vector2 direction = new Vector2(
transform.position.x - target.position.x,
transform.position.y - target.position.y
).normalized;
// Set position to be exactly at the maximum distance
Vector3 constrainedPos = new Vector3(
target.position.x + direction.x * maxDistance,
target.position.y + direction.y * maxDistance,
transform.position.z
);
transform.position = constrainedPos;
}
}
else
{
// Original smooth follow behavior without physics
transform.position = Vector3.SmoothDamp(transform.position, basePosition, ref velocity, trailing, followSpeed);
}
}
@@ -47,6 +235,55 @@ public class RopeEndPhysicsFollower : MonoBehaviour
{
targetTag = tag;
GameObject found = GameObject.FindGameObjectWithTag(targetTag);
if (found) target = found.transform;
if (found)
{
target = found.transform;
lastTargetPosition = target.position;
// Only update horizontal offset to maintain current vertical position
if (initialized)
{
Vector2 newOffset = transform.position - target.position;
offset.x = newOffset.x;
// Don't update offset.y to allow gravity to work
}
else
{
Vector2 newOffset = transform.position - target.position;
offset.x = newOffset.x;
offset.y = 0; // Don't preserve vertical offset for gravity simulation
}
// Apply initial falling impulse if using gravity
if (useGravity)
{
physicsVelocity = new Vector2(physicsVelocity.x, -initialFallImpulse);
if (debugLog) Debug.Log($"[RopeEndPhysicsFollower] Reset Y velocity to {physicsVelocity.y} after target change");
}
}
}
// Debug method to force reset the physics
public void ForceResetPhysics()
{
if (target)
{
// Reset velocity with a strong downward impulse
physicsVelocity = new Vector2(0, -initialFallImpulse * 2f);
// Reset position to be at the same level as the target
Vector3 pos = transform.position;
pos.y = target.position.y;
transform.position = pos;
if (debugLog) Debug.Log($"[RopeEndPhysicsFollower] Physics forcibly reset, new Y velocity: {physicsVelocity.y}");
}
}
// Method to manually set the maximum distance
public void SetMaxDistance(float distance)
{
maxDistance = distance;
if (debugLog) Debug.Log($"[RopeEndPhysicsFollower] Max distance manually set to: {maxDistance}");
}
}