Somewhat working breaking rope
This commit is contained in:
@@ -433,12 +433,15 @@ MonoBehaviour:
|
|||||||
breakPosition: 0.5
|
breakPosition: 0.5
|
||||||
breakEffect: {fileID: 0}
|
breakEffect: {fileID: 0}
|
||||||
breakSound: {fileID: 0}
|
breakSound: {fileID: 0}
|
||||||
fallSpeed: 2
|
|
||||||
swingAmount: 0.5
|
|
||||||
ropeFollowSpeed: 5
|
ropeFollowSpeed: 5
|
||||||
ropeTrailing: 0.2
|
ropeTrailing: 0.2
|
||||||
|
ropeGravityStrength: 9.8
|
||||||
|
ropeMaxHangDistance: 2
|
||||||
|
ropeVerticalHangStrength: 2
|
||||||
ropeOscillationAmplitude: 0.15
|
ropeOscillationAmplitude: 0.15
|
||||||
ropeOscillationFrequency: 2
|
ropeOscillationFrequency: 2
|
||||||
|
ropeDamping: 0.3
|
||||||
|
initialSeparationDistance: 0.1
|
||||||
--- !u!1 &224729330
|
--- !u!1 &224729330
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -1313,12 +1316,15 @@ MonoBehaviour:
|
|||||||
breakPosition: 0.5
|
breakPosition: 0.5
|
||||||
breakEffect: {fileID: 0}
|
breakEffect: {fileID: 0}
|
||||||
breakSound: {fileID: 0}
|
breakSound: {fileID: 0}
|
||||||
fallSpeed: 2
|
|
||||||
swingAmount: 0.5
|
|
||||||
ropeFollowSpeed: 5
|
ropeFollowSpeed: 5
|
||||||
ropeTrailing: 0.2
|
ropeTrailing: 0.2
|
||||||
|
ropeGravityStrength: 9.8
|
||||||
|
ropeMaxHangDistance: 2
|
||||||
|
ropeVerticalHangStrength: 2
|
||||||
ropeOscillationAmplitude: 0.15
|
ropeOscillationAmplitude: 0.15
|
||||||
ropeOscillationFrequency: 2
|
ropeOscillationFrequency: 2
|
||||||
|
ropeDamping: 0.3
|
||||||
|
initialSeparationDistance: 0.1
|
||||||
--- !u!1 &1063641111
|
--- !u!1 &1063641111
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -1807,12 +1813,15 @@ MonoBehaviour:
|
|||||||
breakPosition: 0.5
|
breakPosition: 0.5
|
||||||
breakEffect: {fileID: 0}
|
breakEffect: {fileID: 0}
|
||||||
breakSound: {fileID: 0}
|
breakSound: {fileID: 0}
|
||||||
fallSpeed: 2
|
|
||||||
swingAmount: 0.5
|
|
||||||
ropeFollowSpeed: 5
|
ropeFollowSpeed: 5
|
||||||
ropeTrailing: 0.2
|
ropeTrailing: 0.2
|
||||||
|
ropeGravityStrength: 9.8
|
||||||
|
ropeMaxHangDistance: 2
|
||||||
|
ropeVerticalHangStrength: 2
|
||||||
ropeOscillationAmplitude: 0.1
|
ropeOscillationAmplitude: 0.1
|
||||||
ropeOscillationFrequency: 3
|
ropeOscillationFrequency: 3
|
||||||
|
ropeDamping: 0.3
|
||||||
|
initialSeparationDistance: 0.1
|
||||||
--- !u!1 &1679185997
|
--- !u!1 &1679185997
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -2041,7 +2050,7 @@ GameObject:
|
|||||||
- component: {fileID: 2106431004}
|
- component: {fileID: 2106431004}
|
||||||
m_Layer: 0
|
m_Layer: 0
|
||||||
m_Name: Rock
|
m_Name: Rock
|
||||||
m_TagString: Untagged
|
m_TagString: Rock
|
||||||
m_Icon: {fileID: 0}
|
m_Icon: {fileID: 0}
|
||||||
m_NavMeshLayer: 0
|
m_NavMeshLayer: 0
|
||||||
m_StaticEditorFlags: 0
|
m_StaticEditorFlags: 0
|
||||||
|
|||||||
@@ -19,25 +19,30 @@ public class RopeBreaker : MonoBehaviour
|
|||||||
[Tooltip("Sound to play when rope breaks (optional)")]
|
[Tooltip("Sound to play when rope breaks (optional)")]
|
||||||
[SerializeField] private AudioClip breakSound;
|
[SerializeField] private AudioClip breakSound;
|
||||||
|
|
||||||
[Header("Animation Settings")]
|
|
||||||
[Tooltip("How quickly the rope ends fall after breaking")]
|
|
||||||
[SerializeField] private float fallSpeed = 2f;
|
|
||||||
|
|
||||||
[Tooltip("How much the rope ends swing after breaking")]
|
|
||||||
[SerializeField] private float swingAmount = 0.5f;
|
|
||||||
|
|
||||||
[Header("Physics Settings")]
|
[Header("Physics Settings")]
|
||||||
[Tooltip("Follow speed for the rope physics simulation")]
|
[Tooltip("Follow speed for the rope physics simulation")]
|
||||||
[SerializeField] private float ropeFollowSpeed = 5f;
|
[SerializeField] private float ropeFollowSpeed = 5f;
|
||||||
|
|
||||||
[Tooltip("Trailing amount for the rope physics simulation")]
|
[Tooltip("Trailing amount for the rope physics simulation")]
|
||||||
[SerializeField] private float ropeTrailing = 0.2f;
|
[SerializeField] private float ropeTrailing = 0.2f;
|
||||||
|
|
||||||
|
[Tooltip("Gravity strength applied to hanging rope end")]
|
||||||
|
[SerializeField] private float ropeGravityStrength = 9.8f;
|
||||||
|
|
||||||
[Tooltip("Oscillation amplitude for the rope physics simulation")]
|
[Tooltip("How strongly the rope tries to hang vertically")]
|
||||||
[SerializeField] private float ropeOscillationAmplitude = 0.15f;
|
[SerializeField] private float ropeVerticalHangStrength = 2f;
|
||||||
|
|
||||||
[Tooltip("Oscillation frequency for the rope physics simulation")]
|
[Tooltip("Damping for physics movement (higher = less bouncy)")]
|
||||||
[SerializeField] private float ropeOscillationFrequency = 2f;
|
[SerializeField] private float ropeDamping = 0.3f;
|
||||||
|
|
||||||
|
[Tooltip("Initial separation distance between rope ends when broken")]
|
||||||
|
[SerializeField] private float initialSeparationDistance = 0.1f;
|
||||||
|
|
||||||
|
[Tooltip("Initial downward impulse for falling rope end")]
|
||||||
|
[SerializeField] private float initialFallImpulse = 2.0f;
|
||||||
|
|
||||||
|
[Tooltip("Force Y position reset on start to ensure proper falling")]
|
||||||
|
[SerializeField] private bool forceYReset = true;
|
||||||
|
|
||||||
// Private references
|
// Private references
|
||||||
private Rope originalRope;
|
private Rope originalRope;
|
||||||
@@ -86,7 +91,7 @@ public class RopeBreaker : MonoBehaviour
|
|||||||
CreateBreakPointTransform(breakPointPosition);
|
CreateBreakPointTransform(breakPointPosition);
|
||||||
|
|
||||||
// Create two new rope GameObjects
|
// Create two new rope GameObjects
|
||||||
CreateRopeSegments();
|
CreateRopeSegments(breakPointPosition);
|
||||||
|
|
||||||
// Hide the original rope
|
// Hide the original rope
|
||||||
originalLineRenderer.enabled = false;
|
originalLineRenderer.enabled = false;
|
||||||
@@ -94,9 +99,6 @@ public class RopeBreaker : MonoBehaviour
|
|||||||
// Play effects
|
// Play effects
|
||||||
PlayBreakEffects(breakPointPosition);
|
PlayBreakEffects(breakPointPosition);
|
||||||
|
|
||||||
// Start the animation coroutine
|
|
||||||
StartCoroutine(AnimateRopeBreak());
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,15 +118,38 @@ public class RopeBreaker : MonoBehaviour
|
|||||||
follower.targetTag = "Player";
|
follower.targetTag = "Player";
|
||||||
follower.followSpeed = ropeFollowSpeed;
|
follower.followSpeed = ropeFollowSpeed;
|
||||||
follower.trailing = ropeTrailing;
|
follower.trailing = ropeTrailing;
|
||||||
follower.oscillationAmplitude = ropeOscillationAmplitude;
|
follower.useGravity = false; // Player rope end doesn't use gravity
|
||||||
follower.oscillationFrequency = ropeOscillationFrequency;
|
|
||||||
follower.disableOscillation = true; // Disable oscillation for player-attached rope
|
// Create second break point
|
||||||
|
GameObject secondBreakObj = new GameObject("RopeBreakPoint_Second");
|
||||||
|
secondBreakTransform = secondBreakObj.transform;
|
||||||
|
secondBreakTransform.position = breakPointPosition;
|
||||||
|
secondBreakTransform.SetParent(transform.parent);
|
||||||
|
|
||||||
|
// Add physics behavior to second break point
|
||||||
|
RopeEndPhysicsFollower secondFollower = secondBreakObj.AddComponent<RopeEndPhysicsFollower>();
|
||||||
|
secondFollower.targetTag = "Rock";
|
||||||
|
secondFollower.followSpeed = ropeFollowSpeed;
|
||||||
|
secondFollower.trailing = ropeTrailing;
|
||||||
|
secondFollower.useGravity = true; // Enable gravity for hanging rope end
|
||||||
|
secondFollower.gravityStrength = ropeGravityStrength;
|
||||||
|
secondFollower.verticalHangStrength = ropeVerticalHangStrength;
|
||||||
|
secondFollower.damping = ropeDamping;
|
||||||
|
secondFollower.initialFallImpulse = initialFallImpulse;
|
||||||
|
secondFollower.forceYReset = forceYReset;
|
||||||
|
|
||||||
|
// Create initial separation
|
||||||
|
Vector3 direction = (originalRope.EndPoint.position - breakPointPosition).normalized;
|
||||||
|
if (direction.magnitude < 0.01f) direction = Vector3.down;
|
||||||
|
|
||||||
|
breakPointTransform.position -= direction * initialSeparationDistance * 0.5f;
|
||||||
|
secondBreakTransform.position += direction * initialSeparationDistance * 0.5f;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates two new rope GameObjects for the broken segments
|
/// Creates two new rope GameObjects for the broken segments
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void CreateRopeSegments()
|
private void CreateRopeSegments(Vector3 breakPointPosition)
|
||||||
{
|
{
|
||||||
// Create the first half rope (from start to break point)
|
// Create the first half rope (from start to break point)
|
||||||
firstHalfRope = new GameObject("Rope_FirstHalf");
|
firstHalfRope = new GameObject("Rope_FirstHalf");
|
||||||
@@ -177,7 +202,7 @@ public class RopeBreaker : MonoBehaviour
|
|||||||
|
|
||||||
// Configure the second half rope - REVERSED: Rock (End) is now Start, Break point is now End
|
// Configure the second half rope - REVERSED: Rock (End) is now Start, Break point is now End
|
||||||
secondHalfRopeComponent.SetStartPoint(originalRope.EndPoint);
|
secondHalfRopeComponent.SetStartPoint(originalRope.EndPoint);
|
||||||
secondHalfRopeComponent.SetEndPoint(breakPointTransform, false); // Don't recalculate yet
|
secondHalfRopeComponent.SetEndPoint(secondBreakTransform, false); // Don't recalculate yet
|
||||||
|
|
||||||
// Copy properties from original rope
|
// Copy properties from original rope
|
||||||
CopyRopeProperties(originalRope, secondHalfRopeComponent);
|
CopyRopeProperties(originalRope, secondHalfRopeComponent);
|
||||||
@@ -187,6 +212,20 @@ public class RopeBreaker : MonoBehaviour
|
|||||||
|
|
||||||
// Now force recalculation after initialization
|
// Now force recalculation after initialization
|
||||||
secondHalfRopeComponent.RecalculateRope();
|
secondHalfRopeComponent.RecalculateRope();
|
||||||
|
|
||||||
|
// Set explicit rope length constraints on the physics followers
|
||||||
|
// This needs to be done after the rope segments are created so we have the correct rope lengths
|
||||||
|
RopeEndPhysicsFollower playerFollower = breakPointTransform.GetComponent<RopeEndPhysicsFollower>();
|
||||||
|
if (playerFollower != null)
|
||||||
|
{
|
||||||
|
playerFollower.SetMaxDistance(firstHalfRopeComponent.ropeLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
RopeEndPhysicsFollower rockFollower = secondBreakTransform.GetComponent<RopeEndPhysicsFollower>();
|
||||||
|
if (rockFollower != null)
|
||||||
|
{
|
||||||
|
rockFollower.SetMaxDistance(secondHalfRopeComponent.ropeLength);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -252,92 +291,6 @@ public class RopeBreaker : MonoBehaviour
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Animates the rope break with falling and swinging effects
|
|
||||||
/// </summary>
|
|
||||||
private IEnumerator AnimateRopeBreak()
|
|
||||||
{
|
|
||||||
float elapsedTime = 0f;
|
|
||||||
Vector3 originalBreakPosition = breakPointTransform.position;
|
|
||||||
|
|
||||||
// Create a second break point that will move in the opposite direction
|
|
||||||
GameObject secondBreakPoint = new GameObject("RopeBreakPoint_Second");
|
|
||||||
secondBreakTransform = secondBreakPoint.transform;
|
|
||||||
secondBreakTransform.position = originalBreakPosition;
|
|
||||||
secondBreakTransform.SetParent(transform.parent);
|
|
||||||
|
|
||||||
// Add the physics follower component to the second break point
|
|
||||||
RopeEndPhysicsFollower secondFollower = secondBreakPoint.AddComponent<RopeEndPhysicsFollower>();
|
|
||||||
secondFollower.targetTag = "Rock";
|
|
||||||
secondFollower.followSpeed = ropeFollowSpeed;
|
|
||||||
secondFollower.trailing = ropeTrailing;
|
|
||||||
secondFollower.oscillationAmplitude = ropeOscillationAmplitude * 1.5f; // Slightly more oscillation for falling piece
|
|
||||||
secondFollower.oscillationFrequency = ropeOscillationFrequency;
|
|
||||||
secondFollower.disableOscillation = true; // Enable oscillation for rock-attached end
|
|
||||||
|
|
||||||
// Update the second rope to use this new break point as the end point
|
|
||||||
// (since we reversed the rope direction, the break point is now the end)
|
|
||||||
secondHalfRopeComponent.SetEndPoint(secondBreakTransform);
|
|
||||||
secondHalfRopeComponent.RecalculateRope();
|
|
||||||
|
|
||||||
// Get the directions for swinging
|
|
||||||
Vector3 direction1 = (originalRope.StartPoint.position - breakPointTransform.position).normalized;
|
|
||||||
Vector3 direction2 = (originalRope.EndPoint.position - breakPointTransform.position).normalized;
|
|
||||||
|
|
||||||
// Make sure the directions have horizontal components
|
|
||||||
direction1.y = 0;
|
|
||||||
direction2.y = 0;
|
|
||||||
|
|
||||||
// Normalize to prevent zero vectors
|
|
||||||
if (direction1.magnitude < 0.01f) direction1 = Vector3.right;
|
|
||||||
if (direction2.magnitude < 0.01f) direction2 = Vector3.left;
|
|
||||||
|
|
||||||
direction1.Normalize();
|
|
||||||
direction2.Normalize();
|
|
||||||
|
|
||||||
// Initial separation to create a visual gap
|
|
||||||
float separationDistance = originalLineRenderer.startWidth * 2f; // Base the gap on the rope width
|
|
||||||
breakPointTransform.position += -direction2 * separationDistance * 0.5f;
|
|
||||||
secondBreakTransform.position += direction2 * separationDistance * 0.5f;
|
|
||||||
|
|
||||||
// Recalculate the ropes after initial separation
|
|
||||||
firstHalfRopeComponent.RecalculateRope();
|
|
||||||
secondHalfRopeComponent.RecalculateRope();
|
|
||||||
|
|
||||||
while (elapsedTime < 0.75f) // Animate for 1 second
|
|
||||||
{
|
|
||||||
elapsedTime += Time.deltaTime;
|
|
||||||
|
|
||||||
// Calculate swing motion using sine wave (with different phases for each end)
|
|
||||||
float swingFactor1 = Mathf.Sin(elapsedTime * 4f) * swingAmount * Mathf.Exp(-elapsedTime);
|
|
||||||
float swingFactor2 = Mathf.Sin(elapsedTime * 4f + Mathf.PI * 0.5f) * swingAmount * Mathf.Exp(-elapsedTime);
|
|
||||||
|
|
||||||
// First break point (attached to player) - maintain Y position but allow swinging
|
|
||||||
Vector3 pos1 = originalBreakPosition;
|
|
||||||
pos1.y = originalBreakPosition.y; // Keep original Y position - no falling
|
|
||||||
pos1 += direction1 * swingFactor1;
|
|
||||||
pos1 += -direction2 * (separationDistance * 0.5f); // Maintain separation
|
|
||||||
breakPointTransform.position = pos1;
|
|
||||||
|
|
||||||
// Second break point (hanging rope end) - allow small amount of falling
|
|
||||||
Vector3 pos2 = originalBreakPosition;
|
|
||||||
float fallDistance = fallSpeed * elapsedTime;
|
|
||||||
pos2.y = originalBreakPosition.y - fallDistance; // Limited falling
|
|
||||||
pos2 += direction2 * swingFactor2;
|
|
||||||
pos2 += direction2 * (separationDistance * 0.5f); // Maintain separation
|
|
||||||
secondBreakTransform.position = pos2;
|
|
||||||
|
|
||||||
// Recalculate the ropes each frame
|
|
||||||
firstHalfRopeComponent.RecalculateRope();
|
|
||||||
secondHalfRopeComponent.RecalculateRope();
|
|
||||||
|
|
||||||
yield return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// After the initial animation, let the physics followers take over
|
|
||||||
// They will continue to move and animate the rope endpoints
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Restores the original rope and cleans up the broken pieces
|
/// Restores the original rope and cleans up the broken pieces
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -366,11 +319,9 @@ public class RopeBreaker : MonoBehaviour
|
|||||||
Destroy(breakPointTransform.gameObject);
|
Destroy(breakPointTransform.gameObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find and destroy the second break point if it exists
|
if (secondBreakTransform != null)
|
||||||
Transform secondBreakPoint = transform.parent?.Find("RopeBreakPoint_Second");
|
|
||||||
if (secondBreakPoint != null)
|
|
||||||
{
|
{
|
||||||
Destroy(secondBreakPoint.gameObject);
|
Destroy(secondBreakTransform.gameObject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,45 +1,233 @@
|
|||||||
using UnityEngine;
|
using GogoGaga.OptimizedRopesAndCables;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
public class RopeEndPhysicsFollower : MonoBehaviour
|
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;
|
public string targetTag;
|
||||||
[Tooltip("How quickly the endpoint follows the target")] public float followSpeed = 5f;
|
[Tooltip("How quickly the endpoint follows the target")]
|
||||||
[Tooltip("How much trailing (0 = instant, 1 = very slow)")] public float trailing = 0.2f;
|
public float followSpeed = 5f;
|
||||||
[Tooltip("Amplitude of fake gravity/flap")] public float oscillationAmplitude = 0.15f;
|
[Tooltip("How much trailing (0 = instant, 1 = very slow)")]
|
||||||
[Tooltip("Frequency of fake gravity/flap")] public float oscillationFrequency = 2f;
|
public float trailing = 0.2f;
|
||||||
[Tooltip("If true, disable vertical oscillation (useful for player attachment)")]
|
|
||||||
public bool disableOscillation = true;
|
|
||||||
|
|
||||||
|
[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 Transform target;
|
||||||
private Vector3 velocity;
|
private Vector3 velocity;
|
||||||
private Vector3 offset;
|
private Vector2 physicsVelocity;
|
||||||
private float oscillationTime;
|
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()
|
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))
|
if (!string.IsNullOrEmpty(targetTag))
|
||||||
{
|
{
|
||||||
GameObject found = GameObject.FindGameObjectWithTag(targetTag);
|
GameObject found = GameObject.FindGameObjectWithTag(targetTag);
|
||||||
if (found) target = found.transform;
|
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()
|
void Update()
|
||||||
{
|
{
|
||||||
if (!target) return;
|
if (!target) return;
|
||||||
// Smooth follow with trailing
|
|
||||||
Vector3 desired = target.position + offset;
|
// Calculate deltaTime for physics stability
|
||||||
transform.position = Vector3.SmoothDamp(transform.position, desired, ref velocity, trailing, followSpeed);
|
float deltaTime = Time.deltaTime;
|
||||||
|
|
||||||
// Add fake gravity/flap (only if oscillation is enabled)
|
// Get target velocity
|
||||||
if (!disableOscillation)
|
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;
|
// Get the straight-line distance between target and this transform
|
||||||
float yOsc = Mathf.Sin(oscillationTime) * oscillationAmplitude;
|
float currentDistance = Vector2.Distance(
|
||||||
transform.position += new Vector3(0, yOsc, 0);
|
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;
|
targetTag = tag;
|
||||||
GameObject found = GameObject.FindGameObjectWithTag(targetTag);
|
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}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user