diff --git a/Assets/Game1/Scenes/Game1_Level1.unity b/Assets/Game1/Scenes/Game1_Level1.unity index 9c1c6bb..737bb57 100644 --- a/Assets/Game1/Scenes/Game1_Level1.unity +++ b/Assets/Game1/Scenes/Game1_Level1.unity @@ -283,6 +283,7 @@ MonoBehaviour: radius: 1.8 splitAngle: 0 isAlwaysForward: 1 + maxRotationAngle: 170 qiu: {fileID: 1083892317890881295, guid: 83c0dc6728f2e3a4ab110648310fc920, type: 3} rotationalSpeed: 5 --- !u!1 &49123918 @@ -3116,6 +3117,7 @@ MonoBehaviour: radius: 1.8 splitAngle: 0 isAlwaysForward: 1 + maxRotationAngle: 60 qiu: {fileID: 1083892317890881295, guid: 83c0dc6728f2e3a4ab110648310fc920, type: 3} rotationalSpeed: 2.8 --- !u!1 &1581003589 diff --git a/Assets/Game1/Scripts/RingManager.cs b/Assets/Game1/Scripts/RingManager.cs index a846826..78e71a8 100644 --- a/Assets/Game1/Scripts/RingManager.cs +++ b/Assets/Game1/Scripts/RingManager.cs @@ -12,95 +12,55 @@ public class RingManager : MonoBehaviour public float splitAngle = 0f; [Tooltip("是否一直朝前")] public bool isAlwaysForward = true; + [Tooltip("最大左右旋转角度绝对值")] + public float maxRotationAngle = 90; // 限制角度 public Transform qiu; - public float rotationalSpeed = 5f; // 改为角速度(弧度/秒),更直观 - // 旋转力大小(可在Inspector面板调整) - //public float rotationalForce = 5f; + public float rotationalSpeed = 5f; private List qiuList = new List(); - private float beforePositionX = 0; private float targetRotationalSpeed = 0; private bool isToLeftRotation = false; private bool isCanRotation = false; + + // 【新增】用于记录当前整体偏离初始位置的角度 + private float currentRingAngle = 0f; + void Awake() { + // ... (Awake中的初始化代码保持不变) ... var qiuCollider = qiu.GetComponentInChildren(); + // 注意:如果 qiuCollider 为空这里会报错,建议加个判空 float ringLen = 2 * Mathf.PI * radius; - float qiuRadius = qiuCollider.bounds.extents.magnitude; + // 简单防错 + float qiuRadius = qiuCollider != null ? qiuCollider.bounds.extents.magnitude : 0.5f; float len = Mathf.FloorToInt(ringLen / (qiuRadius * qiu.localScale.x) / 2); for (int i = 0; i < initCount; i++) { float angle = Mathf.PI * 2 / initCount * i + splitAngle; - //if (initCount == 2) - //{ - // if (i == 1) - // { - // angle = Mathf.PI + splitAngle; - // } - //} - //else - //{ - // angle = 360 - (i / len) * Mathf.PI * 2 + splitAngle; // 角度计算逻辑 - //} Vector3 pos = new Vector3(Mathf.Cos(angle), 0, Mathf.Sin(angle)) * radius; - pos.x += transform.position.x; - pos.y += transform.position.y; - pos.z += transform.position.z; + pos += transform.position; var qiuInst = Instantiate(qiu, pos, Quaternion.identity); qiuInst.SetParent(transform, true); - // 确保小球有刚体组件并配置参数 Rigidbody rb = qiuInst.GetComponent(); - if (rb == null) - rb = qiuInst.AddComponent(); - - // 配置刚体属性(避免重力影响,冻结不必要的运动) + if (rb == null) rb = qiuInst.AddComponent(); rb.useGravity = false; rb.constraints = RigidbodyConstraints.FreezePositionY | RigidbodyConstraints.FreezeRotation; qiuList.Add(qiuInst); } - //targetRotationalSpeed = rotationalSpeed; beforePositionX = transform.position.x; - // 初始施加旋转力 - //ApplyRotationalForce(); } - // 给所有小球施加旋转力的方法 - //void ApplyRotationalForce() - //{ - // foreach (var ball in qiuList) - // { - // Rigidbody rb = ball.GetComponent(); - // if (rb == null) continue; - - // // 1. 计算小球相对于中心的径向向量(从中心指向小球) - // Vector3 radialDirection = (ball.position - transform.position).normalized; - - // // 2. 计算切线方向(垂直于径向,绕Y轴旋转的方向) - // // Vector3.Cross(radialDirection, Vector3.up) = 逆时针旋转方向 - // // Vector3.Cross(Vector3.up, radialDirection) = 顺时针旋转方向 - // Vector3 tangentialDirection = Vector3.Cross(radialDirection, -Vector3.up).normalized; - - // // 3. 施加切线方向的力(使小球绕中心旋转) - // //rb.AddForce(tangentialDirection * rotationalForce); - - // // 可选:如果想保持恒定速度,可直接设置角速度而非持续加力 - // float angularSpeed = rotationalForce / radius; - // rb.velocity = tangentialDirection * angularSpeed * radius; - // } - //} - - // 可选:在FixedUpdate中持续施加力(会让小球加速旋转) void FixedUpdate() { + // 1. 运动状态检测 isCanRotation = true; - if (Mathf.Abs(beforePositionX - transform.position.x) < 0.03f) { isCanRotation = false; @@ -109,16 +69,14 @@ public class RingManager : MonoBehaviour if (isCanRotation) { if (transform.position.x < beforePositionX) - { isToLeftRotation = true; - } else - { isToLeftRotation = false; - } } beforePositionX = transform.position.x; + + // 2. 计算目标速度(平滑插值) if (isCanRotation) { targetRotationalSpeed = Mathf.Lerp(targetRotationalSpeed, rotationalSpeed, Time.deltaTime * 5); @@ -127,20 +85,54 @@ public class RingManager : MonoBehaviour { targetRotationalSpeed = Mathf.Lerp(targetRotationalSpeed, 0, Time.deltaTime * 5); } - // 或逐个旋转小球(如果需要独立控制) - foreach (var ball in qiuList) + + // 如果速度非常小,就不进行旋转计算了,节省性能 + if (targetRotationalSpeed < 0.01f) return; + + // 3. 计算本帧【原本想要】旋转的角度量 + float rotationStep = targetRotationalSpeed * Time.deltaTime * Mathf.Rad2Deg; + + // 确定方向符号 (左移往负方向转,右移往正方向转,根据你的需求调整正负) + // 假设:向左移动(isToLeftRotation=true)对应负角度(-),向右对应正角度(+) + float direction = isToLeftRotation ? -1f : 1f; + float finalStep = rotationStep * direction; + + // 4. 【核心逻辑】限制角度 + // 预测下一帧的角度 + float nextAngle = currentRingAngle + finalStep; + + // 如果预测角度超出了范围,需要进行截断 + if (nextAngle > maxRotationAngle) { - if (isToLeftRotation) + // 如果超出了最大值,本帧只转到最大值剩下的那一丢丢 + finalStep = maxRotationAngle - currentRingAngle; + currentRingAngle = maxRotationAngle; + } + else if (nextAngle < -maxRotationAngle) + { + // 如果超出了最小值 + finalStep = -maxRotationAngle - currentRingAngle; + currentRingAngle = -maxRotationAngle; + } + else + { + // 没超出,直接累加 + currentRingAngle = nextAngle; + } + + // 5. 应用旋转 + // 只有当 finalStep 不接近0时才执行,避免浮点数抖动 + if (Mathf.Abs(finalStep) > 0.0001f) + { + foreach (var ball in qiuList) { - ball.RotateAround(transform.position, Vector3.up, -targetRotationalSpeed * Time.deltaTime * Mathf.Rad2Deg); - } - else - { - ball.RotateAround(transform.position, Vector3.up, targetRotationalSpeed * Time.deltaTime * Mathf.Rad2Deg); - } - if (isAlwaysForward) - { - ball.eulerAngles = Vector3.zero; + // 使用计算好的 finalStep 进行旋转 + ball.RotateAround(transform.position, Vector3.up, finalStep); + + if (isAlwaysForward) + { + ball.eulerAngles = Vector3.zero; + } } } } @@ -149,7 +141,7 @@ public class RingManager : MonoBehaviour { foreach (var ball in qiuList) { - Destroy(ball.gameObject); + if (ball != null) Destroy(ball.gameObject); } } } \ No newline at end of file