using UnityEngine; using System.Collections; using GogoGaga.OptimizedRopesAndCables; /// /// Component that allows breaking a rope in half. /// Attach this to the same GameObject that has a Rope and LineRenderer component. /// 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; // 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(); originalLineRenderer = GetComponent(); if (originalRope == null || originalLineRenderer == null) { Debug.LogError("RopeBreaker requires both Rope and LineRenderer components on the same GameObject"); enabled = false; } } /// /// Breaks the rope at the specified position. /// /// Optional override for break position (0-1) /// True if rope was broken successfully, false otherwise 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; } /// /// Creates a transform at the break point to use as an anchor /// private void CreateBreakPointTransform(Vector3 breakPointPosition) { // Store references to the original rope endpoints Transform originalStartPoint = originalRope.StartPoint; Transform originalEndPoint = originalRope.EndPoint; // Create a new GameObject for the break point (attached to Player) 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(); // Set specific transform to follow instead of using tag follower.SetTargetTransform(originalStartPoint); follower.canFall = false; // Player rope end doesn't fall follower.followSpeed = ropeFollowSpeed; follower.trailing = ropeTrailing; // Create second break point (for the rock-attached end) 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(); // Set specific transform to follow instead of using tag secondFollower.SetTargetTransform(originalEndPoint); secondFollower.canFall = true; // Rock end can fall secondFollower.followSpeed = ropeFollowSpeed; secondFollower.trailing = ropeTrailing; secondFollower.gravityStrength = ropeGravityStrength; secondFollower.verticalHangStrength = ropeVerticalHangStrength; secondFollower.damping = ropeDamping; secondFollower.initialFallImpulse = initialFallImpulse; // Create initial separation Vector3 direction = (originalEndPoint.position - breakPointPosition).normalized; if (direction.magnitude < 0.01f) direction = Vector3.down; breakPointTransform.position -= direction * initialSeparationDistance * 0.5f; secondBreakTransform.position += direction * initialSeparationDistance * 0.5f; } /// /// Creates two new rope GameObjects for the broken segments /// 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(); // Get the LineRenderer that was automatically added LineRenderer firstLineRenderer = firstHalfRope.GetComponent(); if (firstLineRenderer == null) { // Only add if somehow not created (shouldn't happen, but safety check) firstLineRenderer = firstHalfRope.AddComponent(); } 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(); // Get the LineRenderer that was automatically added LineRenderer secondLineRenderer = secondHalfRope.GetComponent(); if (secondLineRenderer == null) { // Only add if somehow not created (shouldn't happen, but safety check) secondLineRenderer = secondHalfRope.AddComponent(); } 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(); if (playerFollower != null) { playerFollower.SetMaxDistance(firstHalfRopeComponent.ropeLength); } RopeEndPhysicsFollower rockFollower = secondBreakTransform.GetComponent(); if (rockFollower != null) { rockFollower.SetMaxDistance(secondHalfRopeComponent.ropeLength); } } /// /// Copies properties from one LineRenderer to another /// 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; } /// /// Copies properties from one Rope to another /// 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(); } /// /// Plays visual and audio effects at the break point /// 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); } } /// /// Restores the original rope and cleans up the broken pieces /// 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); } } }