参考:https://github.com/dci05049/Verlet-Rope-Unity
使用插件:Dotween
绳子代码修改:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RopeBridge : MonoBehaviour
{
public Transform StartPoint;
public Transform EndPoint;
private LineRenderer lineRenderer;
public List<RopeSegment> ropeSegments = new List<RopeSegment>();
public float ropeSegLen = 0.25f;
public int segmentLength = 35;
public float startWidth = 0.1f;
public float endWidth = 0.1f;
// Use this for initialization
void Start()
{
this.lineRenderer = this.GetComponent<LineRenderer>();
Vector3 ropeStartPoint = StartPoint.position;
for (int i = 0; i < segmentLength; i++)
{
this.ropeSegments.Add(new RopeSegment(ropeStartPoint));
ropeStartPoint.y -= ropeSegLen;
}
}
// Update is called once per frame
void Update()
{
this.DrawRope();
}
private void FixedUpdate()
{
this.Simulate();
}
private void Simulate()
{
// SIMULATION
Vector2 forceGravity = new Vector2(0f, -1f);
for (int i = 1; i < this.segmentLength; i++)
{
RopeSegment firstSegment = this.ropeSegments[i];
Vector2 velocity = firstSegment.posNow - firstSegment.posOld;
firstSegment.posOld = firstSegment.posNow;
firstSegment.posNow += velocity;
firstSegment.posNow += forceGravity * Time.fixedDeltaTime;
this.ropeSegments[i] = firstSegment;
}
//CONSTRAINTS
for (int i = 0; i < 50; i++)
{
this.ApplyConstraint();
}
}
private void ApplyConstraint()
{
//Constrant to First Point
RopeSegment firstSegment = this.ropeSegments[0];
firstSegment.posNow = this.StartPoint.position;
this.ropeSegments[0] = firstSegment;
//Constrant to Second Point
RopeSegment endSegment = this.ropeSegments[this.ropeSegments.Count - 1];
endSegment.posNow = this.EndPoint.position;
this.ropeSegments[this.ropeSegments.Count - 1] = endSegment;
for (int i = 0; i < this.segmentLength - 1; i++)
{
RopeSegment firstSeg = this.ropeSegments[i];
RopeSegment secondSeg = this.ropeSegments[i + 1];
float dist = (firstSeg.posNow - secondSeg.posNow).magnitude;
float error = Mathf.Abs(dist - this.ropeSegLen);
Vector2 changeDir = Vector2.zero;
if (dist > ropeSegLen)
{
changeDir = (firstSeg.posNow - secondSeg.posNow).normalized;
}
else if (dist < ropeSegLen)
{
changeDir = (secondSeg.posNow - firstSeg.posNow).normalized;
}
Vector2 changeAmount = changeDir * error;
if (i != 0)
{
firstSeg.posNow -= changeAmount * 0.5f;
this.ropeSegments[i] = firstSeg;
secondSeg.posNow += changeAmount * 0.5f;
this.ropeSegments[i + 1] = secondSeg;
}
else
{
secondSeg.posNow += changeAmount;
this.ropeSegments[i + 1] = secondSeg;
}
}
}
private void DrawRope()
{
lineRenderer.startWidth = this.startWidth;
lineRenderer.endWidth = this.endWidth;
Vector3[] ropePositions = new Vector3[this.segmentLength];
for (int i = 0; i < this.segmentLength; i++)
{
ropePositions[i] = this.ropeSegments[i].posNow;
}
lineRenderer.positionCount = ropePositions.Length;
lineRenderer.SetPositions(ropePositions);
}
public struct RopeSegment
{
public Vector2 posNow;
public Vector2 posOld;
public RopeSegment(Vector2 pos)
{
this.posNow = pos;
this.posOld = pos;
}
}
void OnDrawGizmos()
{
DrawBGLine();
}
private void DrawBGLine()
{
#if UNITY_EDITOR
Gizmos.color = Color.green;
Gizmos.DrawLine(StartPoint.position, EndPoint.position);
#endif
}
}
动画代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;
using System;
using UnityEngine.Events;
public class RopeAnim:MonoBehaviour
{
Transform parent;
RopeBridge ropeBridge;
Vector3 target;
Tween tween;
public float percent = 0.5f;
public UnityEvent OnAnimEnd;
public float duration=10;
// Start is called before the first frame update
void Start()
{
ropeBridge = GetComponent<RopeBridge>();
target = ropeBridge.EndPoint.position;
ropeBridge.EndPoint.position = ropeBridge.StartPoint.position;
parent = transform.parent;
startWidth = ropeBridge.startWidth;
endwidth = ropeBridge.endWidth;
InitAnim();
}
float startWidth = 0;
float endwidth = 0;
public void InitAnim()
{
ropeBridge.EndPoint.position = ropeBridge.StartPoint.position;
ropeBridge.startWidth = 0f;
ropeBridge.endWidth = 0f;
if (parent == null){
for (int i = 0; i < transform.childCount; i++) {
RopeAnim childAnim = transform.GetChild(i).GetComponent<RopeAnim>();
if (childAnim != null) {
childAnim.InitAnim();
}
}
}
}
// Update is called once per frame
void Update()
{
}
public void Play()
{
//ropeBridge.lineWidth = 0.1f;
ropeBridge.startWidth = startWidth;
ropeBridge.endWidth = endwidth;
ropeBridge.ropeSegLen = 0.01f;
tween =ropeBridge.EndPoint.DOMove(target, duration).OnComplete(OnComplete).OnUpdate(OnUpdate);
}
private void OnUpdate()
{
if (parent != null)
{
if (parent.GetComponent<RopeBridge>())
{
RopeBridge parentBridge = parent.GetComponent<RopeBridge>();
int NodePoint = (int)(parentBridge.segmentLength * percent);
if (NodePoint > parentBridge.segmentLength - 1)
{
NodePoint = parentBridge.segmentLength - 1;
}
if (NodePoint < 0)
{
NodePoint = 0;
}
ropeBridge.StartPoint.position = parentBridge.ropeSegments[NodePoint].posNow;
}
}
}
private void OnComplete()
{
if (OnAnimEnd != null)
{
OnAnimEnd.Invoke();
}
if (parent != null) {
ropeBridge.ropeSegLen = 0.05f;
ropeBridge.EndPoint.DOShakePosition(1, 0.2f, 5, 10).SetLoops(-1).OnUpdate(OnUpdate);
}
}
public void Stop()
{
tween.Pause();
}
}