线缆的物理表现
This commit is contained in:
@@ -31,6 +31,11 @@ public class CustomCursor : MonoBehaviour
|
||||
// 当禁用自定义光标时,显示默认的鼠标光标
|
||||
Cursor.visible = true;
|
||||
}
|
||||
public void Shake(float intensity, float speed)
|
||||
{
|
||||
float shakeAmount = Mathf.Sin(Time.time * speed) * intensity;
|
||||
transform.rotation = Quaternion.Euler(rotationCorrection + new Vector3(0, 0, shakeAmount));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -36,12 +36,27 @@ public class InteractiveLineSegment : VisualLineSegment
|
||||
{
|
||||
_isCuttable = value;
|
||||
// Change the color of the line segment based on its cuttability
|
||||
GetComponent<LineRenderer>().material.color = value ? Color.white : Color.black;
|
||||
//GetComponent<LineRenderer>().material.color = value ? Color.white : Color.black;
|
||||
}
|
||||
}
|
||||
private bool _isCuttable=false;
|
||||
|
||||
public void OnHover()
|
||||
{
|
||||
if (IsCuttable)
|
||||
{
|
||||
TurnGreen();
|
||||
}
|
||||
else
|
||||
{
|
||||
TurnRed();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnHoverExit()
|
||||
{
|
||||
TurnGrey();
|
||||
}
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
@@ -52,11 +67,26 @@ public class InteractiveLineSegment : VisualLineSegment
|
||||
Initialize();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void TurnGreen()
|
||||
{
|
||||
lineRenderer.material.color = Color.green; // 改变 LineRenderer 的颜色
|
||||
}
|
||||
|
||||
public void TurnGrey()
|
||||
{
|
||||
lineRenderer.material.color = Color.grey; // 改变 LineRenderer 的颜色
|
||||
}
|
||||
public void TurnRed()
|
||||
{
|
||||
lineRenderer.material.color = Color.red; // 改变 LineRenderer 的颜色
|
||||
}
|
||||
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
TurnGrey();
|
||||
if (StartTransform == null || EndTransform == null)
|
||||
{
|
||||
Debug.LogError("StartTransform or EndTransform is not set.");
|
||||
@@ -84,5 +114,7 @@ public class InteractiveLineSegment : VisualLineSegment
|
||||
|
||||
// Set the position of the BoxCollider2D to the start point
|
||||
boxCollider.transform.position = StartPoint;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,12 @@ using System.Collections.Generic;
|
||||
using System.Collections;
|
||||
using AibisDream;
|
||||
using System.Linq;
|
||||
using DG.Tweening;
|
||||
|
||||
public class LineManager : MonoBehaviour
|
||||
{
|
||||
//public GameObject interactiveLineSegmentPrefab;
|
||||
public GameObject visualLineSegmentPrefab;
|
||||
public GameObject physicLineSegmentPrefab;
|
||||
|
||||
public CustomCursor customCursor; // 自定义光标,你可以在 Inspector 中设置
|
||||
|
||||
@@ -41,6 +42,131 @@ public class LineManager : MonoBehaviour
|
||||
{
|
||||
return lineSegmentDictionary.Keys;
|
||||
}
|
||||
private InteractiveLineSegment currentLineSegment; // 当前要被剪断的线段
|
||||
|
||||
private const float LONG_PRESS_THRESHOLD = 3.0f; // 长按阈值,可以根据需要调整
|
||||
private float longPressTime = 0.0f; // 长按时间
|
||||
private float detectionRadius=0.4f;
|
||||
|
||||
public void CutLine(InteractiveLineSegment originalLineSegment, Vector3 splitPoint)
|
||||
{
|
||||
// 如果线段不可剪断,就直接返回,不进行剪断操作
|
||||
if (!originalLineSegment.IsCuttable)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the first new line segment
|
||||
GameObject firstLineSegmentObject = Instantiate(physicLineSegmentPrefab, transform);
|
||||
PhysicLineSegment firstLineSegment = firstLineSegmentObject.GetComponent<PhysicLineSegment>();
|
||||
firstLineSegment.Initialize(originalLineSegment.StartTransform.position, splitPoint);
|
||||
|
||||
// Create the second new line segment
|
||||
GameObject secondLineSegmentObject = Instantiate(physicLineSegmentPrefab, transform);
|
||||
PhysicLineSegment secondLineSegment = secondLineSegmentObject.GetComponent<PhysicLineSegment>();
|
||||
secondLineSegment.Initialize(originalLineSegment.EndTransform.position,splitPoint);
|
||||
|
||||
// Immediately destroy the original line segment
|
||||
Destroy(originalLineSegment.gameObject);
|
||||
}
|
||||
|
||||
|
||||
private void AddPhysicsComponents(GameObject lineSegmentObject)
|
||||
{
|
||||
// Add Rigidbody2D
|
||||
Rigidbody2D rb = lineSegmentObject.AddComponent<Rigidbody2D>();
|
||||
rb.gravityScale = 1; // Enable gravity
|
||||
|
||||
// Add HingeJoint2D
|
||||
HingeJoint2D hinge = lineSegmentObject.AddComponent<HingeJoint2D>();
|
||||
hinge.anchor = Vector2.zero; // Set the anchor at one end of the line segment
|
||||
}
|
||||
public void BindOptionToLineSegment(string lineSegmentName, YarnOption option)
|
||||
{
|
||||
InteractiveLineSegment lineSegment = GetLineSegmentByName(lineSegmentName);
|
||||
if (lineSegment != null)
|
||||
{
|
||||
lineSegment.Option = option;
|
||||
}
|
||||
}
|
||||
private void EnterLineSegment(InteractiveLineSegment lineSegment)
|
||||
{
|
||||
if (currentLineSegment != null)
|
||||
{
|
||||
lineSegment.OnHoverExit();
|
||||
}
|
||||
lineSegment.OnHover();
|
||||
currentLineSegment = lineSegment;
|
||||
}
|
||||
private void LongPressLineSegment(InteractiveLineSegment lineSegment)
|
||||
{
|
||||
longPressTime += Time.deltaTime;
|
||||
Vector3 midpoint = (lineSegment.StartPoint + lineSegment.EndPoint) / 2;
|
||||
|
||||
// Make the scissors shake and the line segment turn red
|
||||
if (longPressTime < LONG_PRESS_THRESHOLD)
|
||||
{
|
||||
customCursor.Shake(longPressTime * 5.0f, 10.0f); // 抖动强度随时间增加
|
||||
//lineSegment.TurnRed(longPressTime / LONG_PRESS_THRESHOLD); // 线段变红的程度随时间增加
|
||||
|
||||
// Calculate the midpoint of the line segment
|
||||
|
||||
// Move the scissors towards the midpoint
|
||||
//customCursor.transform.DOMove(midpoint, 0.1f);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cut the line segment
|
||||
CutLine(lineSegment,midpoint);
|
||||
}
|
||||
}
|
||||
|
||||
private void ExitLineSegment()
|
||||
{
|
||||
if (currentLineSegment != null)
|
||||
{
|
||||
currentLineSegment.TurnGrey();
|
||||
}
|
||||
currentLineSegment = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (isScissorsMode)
|
||||
{
|
||||
// Get the position of the mouse in world coordinates
|
||||
Vector3 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
|
||||
|
||||
// Check if there is a line segment in the scissors' detection range
|
||||
Collider2D hitCollider = Physics2D.OverlapCircle(mousePosition, detectionRadius);
|
||||
InteractiveLineSegment hitLineSegment = hitCollider != null ? hitCollider.GetComponent<InteractiveLineSegment>() : null;
|
||||
if (hitLineSegment != null)
|
||||
{
|
||||
EnterLineSegment(hitLineSegment);
|
||||
|
||||
if (Input.GetMouseButtonDown(0))
|
||||
{
|
||||
longPressTime = 0.0f;
|
||||
}
|
||||
else if (Input.GetMouseButton(0))
|
||||
{
|
||||
LongPressLineSegment(hitLineSegment);
|
||||
}
|
||||
else if (Input.GetMouseButtonUp(0))
|
||||
{
|
||||
longPressTime = 0.0f;
|
||||
//customCursor.ResetRotation(); // 假设你有一个方法来重置剪刀的旋转
|
||||
//currentLineSegment.ResetColor(); // 假设你有一个方法来重置线段的颜色
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ExitLineSegment();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetScissorsCursor()
|
||||
{
|
||||
@@ -126,74 +252,7 @@ public class LineManager : MonoBehaviour
|
||||
IsScissorsMode = isEnabled;
|
||||
}
|
||||
|
||||
public void CutLine(InteractiveLineSegment originalLineSegment, Vector3 splitPoint)
|
||||
{
|
||||
// 如果线段不可剪断,就直接返回,不进行剪断操作
|
||||
if (!originalLineSegment.IsCuttable)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the first new line segment
|
||||
GameObject firstLineSegmentObject = Instantiate(visualLineSegmentPrefab, transform);
|
||||
VisualLineSegment firstLineSegment = firstLineSegmentObject.GetComponent<VisualLineSegment>();
|
||||
firstLineSegment.Initialize(originalLineSegment.StartPoint, splitPoint);
|
||||
|
||||
// Create the second new line segment
|
||||
GameObject secondLineSegmentObject = Instantiate(visualLineSegmentPrefab, transform);
|
||||
VisualLineSegment secondLineSegment = secondLineSegmentObject.GetComponent<VisualLineSegment>();
|
||||
secondLineSegment.Initialize(originalLineSegment.EndPoint, splitPoint);
|
||||
|
||||
// Immediately destroy the original line segment
|
||||
Destroy(originalLineSegment.gameObject);
|
||||
|
||||
// Shrink and disappear the new line segments
|
||||
firstLineSegment.ShrinkAndDisappear();
|
||||
secondLineSegment.ShrinkAndDisappear();
|
||||
}
|
||||
|
||||
public void BindOptionToLineSegment(string lineSegmentName, YarnOption option)
|
||||
{
|
||||
InteractiveLineSegment lineSegment = GetLineSegmentByName(lineSegmentName);
|
||||
if (lineSegment != null)
|
||||
{
|
||||
lineSegment.Option = option;
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (Input.GetMouseButtonDown(0) && isScissorsMode)
|
||||
{
|
||||
// Convert the mouse position to a 2D ray
|
||||
Vector2 ray = Camera.main.ScreenToWorldPoint(Input.mousePosition);
|
||||
|
||||
// Check if the ray hits a line segment
|
||||
RaycastHit2D hit = Physics2D.Raycast(ray, Vector2.zero);
|
||||
if (hit.collider != null)
|
||||
{
|
||||
Debug.Log("Raycast hit: " + hit.transform.name);
|
||||
|
||||
InteractiveLineSegment originalLineSegment = hit.transform.GetComponent<InteractiveLineSegment>();
|
||||
if (originalLineSegment != null)
|
||||
{
|
||||
Debug.Log("Line segment hit: " + originalLineSegment.name);
|
||||
|
||||
CutLine(originalLineSegment,hit.point);
|
||||
|
||||
// Check if Option is not null before selecting it
|
||||
if (originalLineSegment.Option != null)
|
||||
{
|
||||
originalLineSegment.Option.SelectOption();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Debug.Log("Hit object is not a line segment");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
[RequireComponent(typeof(LineRenderer))]
|
||||
public class PhysicLineSegment : MonoBehaviour
|
||||
{
|
||||
public float segmentSpacing = 0.1f;
|
||||
public int k = 30;
|
||||
|
||||
public Vector3 start;
|
||||
public Vector3 end;
|
||||
|
||||
private List<VerletParticle> particles = new List<VerletParticle>();
|
||||
private List<VerletStick> sticks = new List<VerletStick>();
|
||||
private LineRenderer lineRenderer;
|
||||
private int segmentCount;
|
||||
public float gravity = -9.8f;
|
||||
|
||||
public void Initialize(Vector3 start, Vector3 end)
|
||||
{
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
//originalLength = Vector3.Distance(start, end);
|
||||
|
||||
// Calculate the number of segments and the spacing between them
|
||||
segmentCount = Mathf.RoundToInt(Vector3.Distance(start, end) / segmentSpacing);
|
||||
lineRenderer = GetComponent<LineRenderer>();
|
||||
|
||||
// Initialize particles
|
||||
for (int i = 0; i < segmentCount; i++)
|
||||
{
|
||||
// Calculate the position of the segment
|
||||
var particle = new VerletParticle(Vector3.Lerp(start, end, i / (float)segmentCount));
|
||||
|
||||
particles.Add(particle);
|
||||
}
|
||||
|
||||
// Initialize sticks
|
||||
for (int i = 0; i < segmentCount - 1; i++)
|
||||
{
|
||||
sticks.Add(new VerletStick(particles[i], particles[i + 1], segmentSpacing));
|
||||
}
|
||||
|
||||
// Lock the first particle
|
||||
particles[0].isLocked = true;
|
||||
|
||||
lineRenderer.positionCount = segmentCount;
|
||||
}
|
||||
|
||||
|
||||
void FixedUpdate()
|
||||
{
|
||||
UpdateLine();
|
||||
for (int i = 0; i < segmentCount; i++)
|
||||
{
|
||||
lineRenderer.SetPosition(i, particles[i].position);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateLine()
|
||||
{
|
||||
// Apply gravity to particles
|
||||
for (int i = 1; i < particles.Count; i++)
|
||||
{
|
||||
|
||||
particles[i].ApplyForce(Vector3.up * gravity * Time.deltaTime);
|
||||
particles[i].UpdatePosition(Time.deltaTime);
|
||||
}
|
||||
|
||||
// Update sticks
|
||||
for (int ik = 0; ik < k; ik++)
|
||||
{
|
||||
foreach (var stick in sticks)
|
||||
{
|
||||
stick.UpdateStick();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1198bee0150e12d4aa20514b2c8f3362
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,31 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class VerletParticle
|
||||
{
|
||||
public Vector3 position;
|
||||
public Vector3 previousPosition;
|
||||
public bool isLocked;
|
||||
|
||||
public VerletParticle(Vector3 position)
|
||||
{
|
||||
this.position = position;
|
||||
this.previousPosition = position;
|
||||
this.isLocked = false;
|
||||
}
|
||||
|
||||
public void ApplyForce(Vector3 force)
|
||||
{
|
||||
Vector3 temp = position;
|
||||
position += position - previousPosition + force;
|
||||
previousPosition = temp;
|
||||
}
|
||||
|
||||
public void UpdatePosition(float deltaTime)
|
||||
{
|
||||
Vector3 temp = position;
|
||||
position += (position - previousPosition) * deltaTime;
|
||||
previousPosition = temp;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cdb6feb01313af04f8abf13c63227196
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,35 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class VerletStick
|
||||
{
|
||||
private VerletParticle particleA;
|
||||
private VerletParticle particleB;
|
||||
private float length;
|
||||
public float stiffness = 0.8f; // Add this line
|
||||
|
||||
public VerletStick(VerletParticle particleA, VerletParticle particleB, float length)
|
||||
{
|
||||
this.particleA = particleA;
|
||||
this.particleB = particleB;
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
public void UpdateStick()
|
||||
{
|
||||
float currentLength = Vector3.Distance(particleA.position, particleB.position);
|
||||
float difference = length - currentLength;
|
||||
Vector3 direction = (particleB.position - particleA.position).normalized;
|
||||
|
||||
if (!particleA.isLocked)
|
||||
{
|
||||
particleA.position -= direction * difference * stiffness; // Change this line
|
||||
}
|
||||
|
||||
if (!particleB.isLocked)
|
||||
{
|
||||
particleB.position += direction * difference * stiffness; // Change this line
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4f52ebcdf1c225c48ad6413459d49309
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -23,14 +23,14 @@ public class VisualLineSegment : MonoBehaviour
|
||||
lineRenderer.SetPosition(1, EndPoint);
|
||||
}
|
||||
|
||||
public void ShrinkAndDisappear()
|
||||
{
|
||||
// Shrink the line segment from the end to the start
|
||||
DOTween.To(() => EndPoint, x => EndPoint = x, StartPoint, 1f)
|
||||
.OnUpdate(() => {
|
||||
lineRenderer.SetPosition(0, StartPoint);
|
||||
lineRenderer.SetPosition(1, EndPoint);
|
||||
})
|
||||
.OnComplete(() => Destroy(gameObject));
|
||||
}
|
||||
// public void ShrinkAndDisappear()
|
||||
// {
|
||||
// // Shrink the line segment from the end to the start
|
||||
// DOTween.To(() => EndPoint, x => EndPoint = x, StartPoint, 1f)
|
||||
// .OnUpdate(() => {
|
||||
// lineRenderer.SetPosition(0, StartPoint);
|
||||
// lineRenderer.SetPosition(1, EndPoint);
|
||||
// })
|
||||
// .OnComplete(() => Destroy(gameObject));
|
||||
// }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user