using UnityEngine; using UnityEngine.UI; using DG.Tweening; using UnityEditor; public class DisassemblyAssembly : MonoBehaviour { public Camera cameraO; public Transform[] modelParts; // 模型的各个部分 // 用于射线检测的层 public LayerMask clickableLayers; public Vector3[] explodedPositions; // 记录爆炸后的目标位置 private Vector3[] originalPositions; // 记录模型各个部分的原始位置 private bool isAssembled = true; // 模型是否已组装 public GameObject moveObj; public float snapThreshold = 0.5f; // 吸附阈值 public float explodeDistance = 5; public Slider progressBar; // 进度条 public Material tipMaterial; // 高亮材质 private GameObject[] backupParts; // 备份对象 public Transform centerTran; private Vector3 _offset; private Vector3 _targetScreenPoint; private Vector3 moveObjPrePosition; private bool isDraging = false; void Start() { // 记录模型各个部分的原始位置 originalPositions = new Vector3[modelParts.Length]; explodedPositions = new Vector3[modelParts.Length]; backupParts = new GameObject[modelParts.Length]; for (int i = 0; i < modelParts.Length; i++) { originalPositions[i] = modelParts[i].position; explodedPositions[i] = modelParts[i].position + (modelParts[i].position - centerTran.position).normalized * explodeDistance; // 创建备份对象 backupParts[i] = Instantiate(modelParts[i].gameObject, modelParts[i].position, modelParts[i].rotation, centerTran); backupParts[i].GetComponent().material = tipMaterial; Collider c = backupParts[i].GetComponent(); Destroy(c); backupParts[i].SetActive(false); // 初始时隐藏 } // 监听滑动条的值变化 progressBar.onValueChanged.AddListener(OnSliderValueChanged); } void Update() { if (Input.GetKeyDown(KeyCode.I)) { if (isAssembled) { ExplodeModel(); } else { AssembleModel(); } isAssembled = !isAssembled; } if (Input.GetMouseButtonDown(0)) { Ray ray = cameraO.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(ray, out hit, Mathf.Infinity, clickableLayers)) { moveObj = hit.transform.gameObject; if (moveObj) { isDraging = true; _targetScreenPoint = cameraO.WorldToScreenPoint(moveObj.transform.position); moveObjPrePosition = moveObj.transform.position; _offset = moveObj.transform.position - cameraO.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, _targetScreenPoint.z)); int de = System.Array.FindIndex(modelParts, t => t == moveObj.transform); backupParts[de].SetActive(true); } } } if (isDraging) { var curScreenSpace = new Vector3(Input.mousePosition.x, Input.mousePosition.y, _targetScreenPoint.z); Vector3 curWorldPoint = cameraO.ScreenToWorldPoint(curScreenSpace); curWorldPoint.x = Mathf.Clamp(curWorldPoint.x, xMinValue, xMaxValue); curWorldPoint.y = Mathf.Clamp(curWorldPoint.y, yMinValue, yMaxValue); curWorldPoint.z = Mathf.Clamp(curWorldPoint.z, zMinValue, zMaxValue); moveObj.transform.position = curWorldPoint + _offset; } if (isDraging && Input.GetMouseButtonUp(0)) { isDraging = false; if (moveObj) { /* if (moveObjPrePosition != moveObj.transform.position) Singleton.Instance.Splited = true;*/ int de = System.Array.FindIndex(modelParts, t => t == moveObj.transform); backupParts[de].SetActive(false); SnapToClosestPosition(moveObj.transform); } } } void ExplodeModel() { float duration = 1f; // 动画持续时间 for (int i = 0; i < modelParts.Length; i++) { modelParts[i].DOMove(explodedPositions[i], duration); // 移动每个部分到爆炸位置 } } void AssembleModel() { float duration = 1f; // 动画持续时间 for (int i = 0; i < modelParts.Length; i++) { modelParts[i].DOMove(originalPositions[i], duration); // 将每个部分移回原位 } } void SnapToClosestPosition(Transform part) { Vector3 closestPosition = Vector3.zero; float closestDistance = float.MaxValue; //Vector3[] targetPositions = isAssembled ? explodedPositions : originalPositions; Vector3[] targetPositions = originalPositions; int de = System.Array.FindIndex(modelParts, t => t == part); float distance = Vector3.Distance(part.position, targetPositions[de]); if (distance < closestDistance) { closestDistance = distance; closestPosition = targetPositions[de]; } if (closestDistance <= snapThreshold) { part.DOMove(closestPosition, 0.5f); // 吸附到最近的位置 } } void OnSliderValueChanged(float value) { for (int i = 0; i < modelParts.Length; i++) { Vector3 targetPosition = originalPositions[i] + (explodedPositions[i] - originalPositions[i]) * value; modelParts[i].DOMove(targetPosition, 0.5f); } } //是否限制活动范围 [SerializeField] private bool isRangeClamped = true; //限制范围 当isRangeClamped为true时起作用 [SerializeField] private float xMinValue = -100f; //x最小值 [SerializeField] private float xMaxValue = 100f; //x最大值 [SerializeField] private float zMinValue = -100f; //z最小值 [SerializeField] private float zMaxValue = 100f; //z最大值 [SerializeField] private float yMinValue = 0f; //y最小值 [SerializeField] private float yMaxValue = 100; //y最大值 #if UNITY_EDITOR private void OnDrawGizmosSelected() { //如果限制活动范围 将区域范围绘制出来 if (isRangeClamped) { Handles.color = Color.cyan; Vector3[] points = new Vector3[8] { new Vector3(xMinValue, yMinValue, zMinValue), new Vector3(xMaxValue, yMinValue, zMinValue), new Vector3(xMaxValue, yMinValue, zMaxValue), new Vector3(xMinValue, yMinValue, zMaxValue), new Vector3(xMinValue, yMaxValue, zMinValue), new Vector3(xMaxValue, yMaxValue, zMinValue), new Vector3(xMaxValue, yMaxValue, zMaxValue), new Vector3(xMinValue, yMaxValue, zMaxValue) }; for (int i = 0; i < 4; i++) { int start = i % 4; int end = (i + 1) % 4; Handles.DrawLine(points[start], points[end]); Handles.DrawLine(points[start + 4], points[end + 4]); Handles.DrawLine(points[start], points[i + 4]); } } } #endif }