328 lines
13 KiB
C#
328 lines
13 KiB
C#
using UnityEngine;
|
|
using System.Collections;
|
|
using GogoGaga.OptimizedRopesAndCables;
|
|
|
|
/// <summary>
|
|
/// Component that allows breaking a rope in half.
|
|
/// Attach this to the same GameObject that has a Rope and LineRenderer component.
|
|
/// </summary>
|
|
public class RopeBreaker : MonoBehaviour
|
|
{
|
|
[Header("Break Settings")]
|
|
[Tooltip("Position along rope where break occurs (0-1)")]
|
|
[Range(0f, 1f)]
|
|
[SerializeField] private float breakPosition = 0.5f;
|
|
|
|
[Tooltip("Effect to spawn at break point (optional)")]
|
|
[SerializeField] private GameObject breakEffect;
|
|
|
|
[Tooltip("Sound to play when rope breaks (optional)")]
|
|
[SerializeField] private AudioClip breakSound;
|
|
|
|
[Header("Physics Settings")]
|
|
[Tooltip("Follow speed for the rope physics simulation")]
|
|
[SerializeField] private float ropeFollowSpeed = 5f;
|
|
|
|
[Tooltip("Trailing amount for the rope physics simulation")]
|
|
[SerializeField] private float ropeTrailing = 0.2f;
|
|
|
|
[Tooltip("Gravity strength applied to hanging rope end")]
|
|
[SerializeField] private float ropeGravityStrength = 9.8f;
|
|
|
|
[Tooltip("How strongly the rope tries to hang vertically")]
|
|
[SerializeField] private float ropeVerticalHangStrength = 2f;
|
|
|
|
[Tooltip("Damping for physics movement (higher = less bouncy)")]
|
|
[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 Rope originalRope;
|
|
private LineRenderer originalLineRenderer;
|
|
private GameObject firstHalfRope;
|
|
private GameObject secondHalfRope;
|
|
private Rope firstHalfRopeComponent;
|
|
private Rope secondHalfRopeComponent;
|
|
private Transform breakPointTransform;
|
|
private Transform secondBreakTransform;
|
|
|
|
private void Awake()
|
|
{
|
|
// Get references to the required components
|
|
originalRope = GetComponent<Rope>();
|
|
originalLineRenderer = GetComponent<LineRenderer>();
|
|
|
|
if (originalRope == null || originalLineRenderer == null)
|
|
{
|
|
Debug.LogError("RopeBreaker requires both Rope and LineRenderer components on the same GameObject");
|
|
enabled = false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Breaks the rope at the specified position.
|
|
/// </summary>
|
|
/// <param name="breakPositionOverride">Optional override for break position (0-1)</param>
|
|
/// <returns>True if rope was broken successfully, false otherwise</returns>
|
|
public bool BreakRope(float? breakPositionOverride = null)
|
|
{
|
|
if (originalRope == null || !originalRope.StartPoint || !originalRope.EndPoint)
|
|
{
|
|
Debug.LogError("Cannot break rope: Missing rope component or endpoints");
|
|
return false;
|
|
}
|
|
|
|
// Use override position if provided
|
|
float breakPos = breakPositionOverride ?? breakPosition;
|
|
breakPos = Mathf.Clamp01(breakPos);
|
|
|
|
// Get the world position at the break point
|
|
Vector3 breakPointPosition = originalRope.GetPointAt(breakPos);
|
|
|
|
// Create a transform at the break point to use as an anchor
|
|
CreateBreakPointTransform(breakPointPosition);
|
|
|
|
// Create two new rope GameObjects
|
|
CreateRopeSegments(breakPointPosition);
|
|
|
|
// Hide the original rope
|
|
originalLineRenderer.enabled = false;
|
|
|
|
// Play effects
|
|
PlayBreakEffects(breakPointPosition);
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a transform at the break point to use as an anchor
|
|
/// </summary>
|
|
private void CreateBreakPointTransform(Vector3 breakPointPosition)
|
|
{
|
|
// Create a new GameObject for the break point
|
|
GameObject breakPointObj = new GameObject("RopeBreakPoint");
|
|
breakPointTransform = breakPointObj.transform;
|
|
breakPointTransform.position = breakPointPosition;
|
|
breakPointTransform.SetParent(transform.parent); // Parent to the same parent as the rope
|
|
|
|
// Add the physics follower component to the break point
|
|
RopeEndPhysicsFollower follower = breakPointObj.AddComponent<RopeEndPhysicsFollower>();
|
|
follower.targetTag = "Player";
|
|
follower.followSpeed = ropeFollowSpeed;
|
|
follower.trailing = ropeTrailing;
|
|
follower.useGravity = false; // Player rope end doesn't use gravity
|
|
|
|
// 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>
|
|
/// Creates two new rope GameObjects for the broken segments
|
|
/// </summary>
|
|
private void CreateRopeSegments(Vector3 breakPointPosition)
|
|
{
|
|
// Create the first half rope (from start to break point)
|
|
firstHalfRope = new GameObject("Rope_FirstHalf");
|
|
firstHalfRope.transform.position = transform.position;
|
|
firstHalfRope.transform.rotation = transform.rotation;
|
|
firstHalfRope.transform.SetParent(transform.parent);
|
|
|
|
// Add Rope component which automatically adds LineRenderer due to RequireComponent
|
|
firstHalfRopeComponent = firstHalfRope.AddComponent<Rope>();
|
|
|
|
// Get the LineRenderer that was automatically added
|
|
LineRenderer firstLineRenderer = firstHalfRope.GetComponent<LineRenderer>();
|
|
if (firstLineRenderer == null)
|
|
{
|
|
// Only add if somehow not created (shouldn't happen, but safety check)
|
|
firstLineRenderer = firstHalfRope.AddComponent<LineRenderer>();
|
|
}
|
|
CopyLineRendererProperties(originalLineRenderer, firstLineRenderer);
|
|
|
|
// Create the second half rope (from break point to end)
|
|
secondHalfRope = new GameObject("Rope_SecondHalf");
|
|
secondHalfRope.transform.position = transform.position;
|
|
secondHalfRope.transform.rotation = transform.rotation;
|
|
secondHalfRope.transform.SetParent(transform.parent);
|
|
|
|
// Add Rope component which automatically adds LineRenderer due to RequireComponent
|
|
secondHalfRopeComponent = secondHalfRope.AddComponent<Rope>();
|
|
|
|
// Get the LineRenderer that was automatically added
|
|
LineRenderer secondLineRenderer = secondHalfRope.GetComponent<LineRenderer>();
|
|
if (secondLineRenderer == null)
|
|
{
|
|
// Only add if somehow not created (shouldn't happen, but safety check)
|
|
secondLineRenderer = secondHalfRope.AddComponent<LineRenderer>();
|
|
}
|
|
CopyLineRendererProperties(originalLineRenderer, secondLineRenderer);
|
|
|
|
// Configure the first half rope
|
|
firstHalfRopeComponent.SetStartPoint(originalRope.StartPoint);
|
|
firstHalfRopeComponent.SetEndPoint(breakPointTransform, false); // Don't recalculate yet
|
|
|
|
// Copy properties from original rope
|
|
CopyRopeProperties(originalRope, firstHalfRopeComponent);
|
|
|
|
// Explicitly initialize the rope
|
|
firstHalfRopeComponent.Initialize();
|
|
|
|
// Now force recalculation after initialization
|
|
firstHalfRopeComponent.RecalculateRope();
|
|
|
|
// Configure the second half rope - REVERSED: Rock (End) is now Start, Break point is now End
|
|
secondHalfRopeComponent.SetStartPoint(originalRope.EndPoint);
|
|
secondHalfRopeComponent.SetEndPoint(secondBreakTransform, false); // Don't recalculate yet
|
|
|
|
// Copy properties from original rope
|
|
CopyRopeProperties(originalRope, secondHalfRopeComponent);
|
|
|
|
// Explicitly initialize the rope
|
|
secondHalfRopeComponent.Initialize();
|
|
|
|
// Now force recalculation after initialization
|
|
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>
|
|
/// Copies properties from one LineRenderer to another
|
|
/// </summary>
|
|
private void CopyLineRendererProperties(LineRenderer source, LineRenderer destination)
|
|
{
|
|
// Copy material
|
|
destination.material = source.material;
|
|
|
|
// Copy colors
|
|
destination.startColor = source.startColor;
|
|
destination.endColor = source.endColor;
|
|
|
|
// Copy width
|
|
destination.startWidth = source.startWidth;
|
|
destination.endWidth = source.endWidth;
|
|
|
|
// Copy other properties
|
|
destination.numCornerVertices = source.numCornerVertices;
|
|
destination.numCapVertices = source.numCapVertices;
|
|
destination.alignment = source.alignment;
|
|
destination.textureMode = source.textureMode;
|
|
destination.generateLightingData = source.generateLightingData;
|
|
destination.useWorldSpace = source.useWorldSpace;
|
|
destination.loop = source.loop;
|
|
destination.sortingLayerID = source.sortingLayerID;
|
|
destination.sortingOrder = source.sortingOrder;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Copies properties from one Rope to another
|
|
/// </summary>
|
|
private void CopyRopeProperties(Rope source, Rope destination)
|
|
{
|
|
destination.linePoints = source.linePoints;
|
|
destination.stiffness = source.stiffness;
|
|
destination.damping = source.damping;
|
|
destination.ropeLength = source.ropeLength / 2f; // Halve the rope length for each segment
|
|
destination.ropeWidth = source.ropeWidth;
|
|
destination.midPointWeight = source.midPointWeight;
|
|
destination.midPointPosition = source.midPointPosition;
|
|
|
|
// Recalculate the rope to update its appearance
|
|
destination.RecalculateRope();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Plays visual and audio effects at the break point
|
|
/// </summary>
|
|
private void PlayBreakEffects(Vector3 breakPointPosition)
|
|
{
|
|
// Spawn break effect if assigned
|
|
if (breakEffect != null)
|
|
{
|
|
Instantiate(breakEffect, breakPointPosition, Quaternion.identity);
|
|
}
|
|
|
|
// Play break sound if assigned
|
|
if (breakSound != null)
|
|
{
|
|
AudioSource.PlayClipAtPoint(breakSound, breakPointPosition);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Restores the original rope and cleans up the broken pieces
|
|
/// </summary>
|
|
public void RestoreRope()
|
|
{
|
|
// Re-enable the original rope
|
|
if (originalLineRenderer != null)
|
|
{
|
|
originalLineRenderer.enabled = true;
|
|
}
|
|
|
|
// Clean up the broken rope pieces
|
|
if (firstHalfRope != null)
|
|
{
|
|
Destroy(firstHalfRope);
|
|
}
|
|
|
|
if (secondHalfRope != null)
|
|
{
|
|
Destroy(secondHalfRope);
|
|
}
|
|
|
|
// Clean up both break points
|
|
if (breakPointTransform != null)
|
|
{
|
|
Destroy(breakPointTransform.gameObject);
|
|
}
|
|
|
|
if (secondBreakTransform != null)
|
|
{
|
|
Destroy(secondBreakTransform.gameObject);
|
|
}
|
|
}
|
|
}
|