统一拍打子对象的旋转
Clapming rotation of child objects in unity
我有一个游戏对象,其中包含另一个应该能够朝向目标旋转的游戏对象(想象一个坦克炮塔)。所以我创建了以下脚本:
public class Rotator : MonoBehaviour {
public GameObject _enemy;
void Update () {
var actualTarget = _enemy.transform.position;
var targetDir = actualTarget - transform.position;
var step = 2 * Time.deltaTime;
var target = Quaternion.LookRotation(targetDir.normalized, Vector3.up);
var actual = target * Quaternion.Inverse(transform.parent.rotation);
var targetRotation = Quaternion.Slerp(transform.localRotation, actual, step);
targetRotation.eulerAngles = ClampRotation(targetRotation.eulerAngles);
transform.localRotation = targetRotation;
}
private static Vector3 ClampRotation(Vector3 eulerAngles) {
var x = Mathf.Clamp(eulerAngles.x > 180 ? eulerAngles.x - 360 : eulerAngles.x, -180, 180);
var y = Mathf.Clamp(eulerAngles.y > 180 ? eulerAngles.y - 360 : eulerAngles.y, -45, 45);
return new Vector3(x, y, 0);
}
}
对象设置:
名为"parent"的对象在Y轴上旋转了90度,其他的都没有旋转。
在 y 轴上的夹紧效果很好 - 旋转保持在 -45 到 45 度之间。然而,旋转在 x 轴上不起作用(有或没有夹紧)。
所以这里的目标是当我向左或向右移动立方体时,红色的立方体围绕 Y 轴在 [-45,45] 度之间旋转,当我向上或向下移动它时,红色的立方体在 [-180,180] 度之间旋转围绕 X 轴。
我使用 Transform
类的 LookAt
方法取得了一些成功,但由于某种原因,如果我尝试手动修改 localRotation
的 eulerAngles
它突然失去了旋转的可能性在 X 轴上向后,即使我只是对 Y 值做一些事情...
经过长时间的反复试验和疯狂浏览互联网,我设法找到了适合我需要的答案。 Clamp
方法的第一个 if 语句的评论词 - 如果我希望对象也被夹在它的倒置位置(如果目标在它后面),这很有用:
void Update() {
transform.LookAt(_target.transform);
var rotation = transform.localRotation;
var eulers = ClampRotation(rotation.eulerAngles);
transform.localEulerAngles = eulers;
}
private static Vector3 ClampRotation(Vector3 eulerAngles) {
var x = Clamp(eulerAngles.x, -60, 60);
var y = Clamp(eulerAngles.y, -45, 45);
return new Vector3(x, y, 0);
}
private static float Clamp(float angle, float min, float max) {
if ((angle <= 180 && angle >= 180 - Mathf.Abs(min)) || (angle >= 180 && angle <= 180 + max)) {
return Mathf.Clamp(angle, 180 - Mathf.Abs(min), 180 + max);
}
if (angle > 180f) {
angle -= 360f;
}
angle = Mathf.Clamp(angle, min, max);
if (angle < 0f) {
angle += 360f;
}
return angle;
}
编辑:
事实证明,有时候创建自己的细粒度解决方案会更好,您可以更轻松地修改,所以对于任何感兴趣的人,您也可以使用以下代码来做我想做的事情:
void Update() {
var actualTarget = _enemy.transform.position;
var targetDir = actualTarget - transform.position;
var target = Quaternion.LookRotation(targetDir.normalized, transform.up);
var actual = Quaternion.Inverse(transform.parent.rotation) * target;
actual.eulerAngles = ClampRotation(actual.eulerAngles);
var targetRotation = Quaternion.Slerp(transform.localRotation, actual, 8 * Time.deltaTime);
transform.localRotation = targetRotation;
}
private static Vector3 ClampRotation(Vector3 newRotation) {
var x = Clamp(newRotation.x, -179, 179);
var y = Clamp(newRotation.y, -45, 45);
return new Vector3(x, y, 0);
}
private static float Clamp(float angle, float min, float max) {
if ((angle <= 180 && angle >= 180 - Mathf.Abs(min)) || (angle >= 180 && angle <= 180 + max)) {
return Mathf.Clamp(angle, 180 - Mathf.Abs(min), 180 + max);
}
if (angle > 180f) {
angle -= 360f;
}
angle = Mathf.Clamp(angle, min, max);
if (angle < 0f) {
angle += 360f;
}
if (Mathf.Abs(angle) == 360) {
angle = 0;
}
return angle;
}
我有一个游戏对象,其中包含另一个应该能够朝向目标旋转的游戏对象(想象一个坦克炮塔)。所以我创建了以下脚本:
public class Rotator : MonoBehaviour {
public GameObject _enemy;
void Update () {
var actualTarget = _enemy.transform.position;
var targetDir = actualTarget - transform.position;
var step = 2 * Time.deltaTime;
var target = Quaternion.LookRotation(targetDir.normalized, Vector3.up);
var actual = target * Quaternion.Inverse(transform.parent.rotation);
var targetRotation = Quaternion.Slerp(transform.localRotation, actual, step);
targetRotation.eulerAngles = ClampRotation(targetRotation.eulerAngles);
transform.localRotation = targetRotation;
}
private static Vector3 ClampRotation(Vector3 eulerAngles) {
var x = Mathf.Clamp(eulerAngles.x > 180 ? eulerAngles.x - 360 : eulerAngles.x, -180, 180);
var y = Mathf.Clamp(eulerAngles.y > 180 ? eulerAngles.y - 360 : eulerAngles.y, -45, 45);
return new Vector3(x, y, 0);
}
}
对象设置:
名为"parent"的对象在Y轴上旋转了90度,其他的都没有旋转。
在 y 轴上的夹紧效果很好 - 旋转保持在 -45 到 45 度之间。然而,旋转在 x 轴上不起作用(有或没有夹紧)。
所以这里的目标是当我向左或向右移动立方体时,红色的立方体围绕 Y 轴在 [-45,45] 度之间旋转,当我向上或向下移动它时,红色的立方体在 [-180,180] 度之间旋转围绕 X 轴。
我使用 Transform
类的 LookAt
方法取得了一些成功,但由于某种原因,如果我尝试手动修改 localRotation
的 eulerAngles
它突然失去了旋转的可能性在 X 轴上向后,即使我只是对 Y 值做一些事情...
经过长时间的反复试验和疯狂浏览互联网,我设法找到了适合我需要的答案。 Clamp
方法的第一个 if 语句的评论词 - 如果我希望对象也被夹在它的倒置位置(如果目标在它后面),这很有用:
void Update() {
transform.LookAt(_target.transform);
var rotation = transform.localRotation;
var eulers = ClampRotation(rotation.eulerAngles);
transform.localEulerAngles = eulers;
}
private static Vector3 ClampRotation(Vector3 eulerAngles) {
var x = Clamp(eulerAngles.x, -60, 60);
var y = Clamp(eulerAngles.y, -45, 45);
return new Vector3(x, y, 0);
}
private static float Clamp(float angle, float min, float max) {
if ((angle <= 180 && angle >= 180 - Mathf.Abs(min)) || (angle >= 180 && angle <= 180 + max)) {
return Mathf.Clamp(angle, 180 - Mathf.Abs(min), 180 + max);
}
if (angle > 180f) {
angle -= 360f;
}
angle = Mathf.Clamp(angle, min, max);
if (angle < 0f) {
angle += 360f;
}
return angle;
}
编辑:
事实证明,有时候创建自己的细粒度解决方案会更好,您可以更轻松地修改,所以对于任何感兴趣的人,您也可以使用以下代码来做我想做的事情:
void Update() {
var actualTarget = _enemy.transform.position;
var targetDir = actualTarget - transform.position;
var target = Quaternion.LookRotation(targetDir.normalized, transform.up);
var actual = Quaternion.Inverse(transform.parent.rotation) * target;
actual.eulerAngles = ClampRotation(actual.eulerAngles);
var targetRotation = Quaternion.Slerp(transform.localRotation, actual, 8 * Time.deltaTime);
transform.localRotation = targetRotation;
}
private static Vector3 ClampRotation(Vector3 newRotation) {
var x = Clamp(newRotation.x, -179, 179);
var y = Clamp(newRotation.y, -45, 45);
return new Vector3(x, y, 0);
}
private static float Clamp(float angle, float min, float max) {
if ((angle <= 180 && angle >= 180 - Mathf.Abs(min)) || (angle >= 180 && angle <= 180 + max)) {
return Mathf.Clamp(angle, 180 - Mathf.Abs(min), 180 + max);
}
if (angle > 180f) {
angle -= 360f;
}
angle = Mathf.Clamp(angle, min, max);
if (angle < 0f) {
angle += 360f;
}
if (Mathf.Abs(angle) == 360) {
angle = 0;
}
return angle;
}