让玩家自由旋转一个GameObject
Let the players freely rotate a GameObject
我正在尝试找到一种方法,让玩家可以使用鼠标自由拖动和旋转对象 (click to view scene)。
如您所见,移动对象的脚本工作得很好,但当您想要旋转它时,问题就来了。事实上,它在您第一次尝试旋转对象时起作用。如果您松开鼠标并重试,该对象会在再次被鼠标旋转之前回到其初始旋转。我不想要那个。我希望它从原来的位置继续旋转。到目前为止,这是我的代码:
private void OnMouseDown()
{
worldPositionA = Camera.main.ScreenToWorldPoint(Input.mousePosition);
}
void Update()
{
worldPositionB = Camera.main.ScreenToWorldPoint(Input.mousePosition);
}
private void OnMouseDrag()
{
Vector3 relativePositionA = worldPositionA - parent.gameObject.transform.position;
Vector3 relativePositionB = worldPositionB - parent.gameObject.transform.position;
float angleBetween = Vector2.SignedAngle(relativePositionB, relativePositionA);
angleBetween += parent.transform.rotation.z;
parent.transform.rotation = Quaternion.AngleAxis(angleBetween, Vector3.back);
}
worldPositionA是鼠标开始拖动时的世界位置,worldPositionB是更新后的鼠标位置。
relativePosition(A 和 B)相同,但相对于对象的中心。
我知道它们是正确的,因为我有一个指向它们的光线投射,并且 angleBetween 也是正确的,因为我正在控制台中打印它。
怎么了?有什么我没有考虑到的吗?
不确定是不是这个原因,但是角度计算取决于旋转,每帧也会改变。我建议将初始旋转保存在 OnMouseDown
中,这样角度计算仅取决于初始条件(即旋转和鼠标位置)和当前鼠标位置,而不取决于任何中间结果。这也可能有助于避免数值问题。
我不是特别熟悉 unity,但是有没有理由将鼠标位置保存在 worldPositionB 中而不是直接在 OnMouseDrag 中计算它?
您的问题很可能与线路有关
angleBetween += parent.transform.rotation.z;
rotation
是一个 Quaternion
。您应该永远不要 直接设置和读取各个组件,除非您非常清楚自己在做什么。 Quaternion
不是 3 个而是 4 个分量 x
、y
、z
和 w
,它们在 -1
和 1
之间移动,这很可能不是您期望的值。
因为一开始 worldPositionA = worldPositionB
第一个角度也总是在 -1
和 1
之间所以看起来对象被重置为原始旋转。
稍后您不会注意到这个错误,因为它最多会偏离 +/-1°。
您可以尝试使用 eulerAngles
,它实际上 returns 围绕 Z
轴的预期旋转:
angleBetween += parent.transform.rotation.eulerAngles.z;
或者您可以使用 *
operator
将角度旋转添加到现有旋转
var angleBetween = Vector2.SignedAngle(relativePositionB, relativePositionA);
parent.transform.rotation *= Quaternion.AngleAxis(angleBetween, Vector3.back);
顺便说一句,我会简单地移动
的部分
worldPositionB = Camera.main.ScreenToWorldPoint(Input.mousePosition);
进入 OnMouseDrag()
因为只有那里需要它。
并且通常您应该存储 Camera.main
引用,因为此调用非常昂贵:
// If possible even already reference this via the Inspector!
[SerializeField] private Camera _mainCamera;
// As fallback get it ONCE on runtime
private void Awake()
{
if(!_mainCamera) _mainCamera = Camera.main;
}
private void OnMouseDown()
{
worldPositionA = _mainCamera.ScreenToWorldPoint(Input.mousePosition);
}
private void OnMouseDrag()
{
worldPositionB = _mainCamera.ScreenToWorldPoint(Input.mousePosition);
var relativePositionA = worldPositionA - parent.transform.position;
var relativePositionB = worldPositionB - parent.transform.position;
我仍然不知道我以前的脚本有什么问题,但我找到了另一种方法来完成我想要的。
我没有直接使用 Transform,而是添加了一个 Rigidbody 并修改了 Rotation 值。
这是脚本,以防有人需要它:
[SerializeField] private Camera _mainCamera; //Assigned from Inspector
[SerializeField] private GameObject parent = null; //Assigned from Inspector
private Rigidbody2D parentRb2d;
float startRotation;
float angleBetween;
private void Awake()
{
parentRb2d = parent.GetComponent<Rigidbody2D>();
}
private void Update()
{
worldPositionB = _mainCamera.ScreenToWorldPoint(Input.mousePosition);
}
private void OnMouseDown()
{
worldPositionA = _mainCamera.ScreenToWorldPoint(Input.mousePosition);
startRotation = parentRb2d.rotation;
}
private void OnMouseDrag()
{
Vector3 relativePositionA = worldPositionA - parent.gameObject.transform.position;
Vector3 relativePositionB = worldPositionB - parent.gameObject.transform.position;
angleBetween = Vector2.SignedAngle(relativePositionB, relativePositionA);
parentRb2d.MoveRotation(startRotation - angleBetween);
}
如果您的对象在相反的方向,请尝试将最后一行中的“-”运算符切换为“+”。
我正在尝试找到一种方法,让玩家可以使用鼠标自由拖动和旋转对象 (click to view scene)。 如您所见,移动对象的脚本工作得很好,但当您想要旋转它时,问题就来了。事实上,它在您第一次尝试旋转对象时起作用。如果您松开鼠标并重试,该对象会在再次被鼠标旋转之前回到其初始旋转。我不想要那个。我希望它从原来的位置继续旋转。到目前为止,这是我的代码:
private void OnMouseDown()
{
worldPositionA = Camera.main.ScreenToWorldPoint(Input.mousePosition);
}
void Update()
{
worldPositionB = Camera.main.ScreenToWorldPoint(Input.mousePosition);
}
private void OnMouseDrag()
{
Vector3 relativePositionA = worldPositionA - parent.gameObject.transform.position;
Vector3 relativePositionB = worldPositionB - parent.gameObject.transform.position;
float angleBetween = Vector2.SignedAngle(relativePositionB, relativePositionA);
angleBetween += parent.transform.rotation.z;
parent.transform.rotation = Quaternion.AngleAxis(angleBetween, Vector3.back);
}
worldPositionA是鼠标开始拖动时的世界位置,worldPositionB是更新后的鼠标位置。 relativePosition(A 和 B)相同,但相对于对象的中心。 我知道它们是正确的,因为我有一个指向它们的光线投射,并且 angleBetween 也是正确的,因为我正在控制台中打印它。 怎么了?有什么我没有考虑到的吗?
不确定是不是这个原因,但是角度计算取决于旋转,每帧也会改变。我建议将初始旋转保存在 OnMouseDown
中,这样角度计算仅取决于初始条件(即旋转和鼠标位置)和当前鼠标位置,而不取决于任何中间结果。这也可能有助于避免数值问题。
我不是特别熟悉 unity,但是有没有理由将鼠标位置保存在 worldPositionB 中而不是直接在 OnMouseDrag 中计算它?
您的问题很可能与线路有关
angleBetween += parent.transform.rotation.z;
rotation
是一个 Quaternion
。您应该永远不要 直接设置和读取各个组件,除非您非常清楚自己在做什么。 Quaternion
不是 3 个而是 4 个分量 x
、y
、z
和 w
,它们在 -1
和 1
之间移动,这很可能不是您期望的值。
因为一开始 worldPositionA = worldPositionB
第一个角度也总是在 -1
和 1
之间所以看起来对象被重置为原始旋转。
稍后您不会注意到这个错误,因为它最多会偏离 +/-1°。
您可以尝试使用 eulerAngles
,它实际上 returns 围绕 Z
轴的预期旋转:
angleBetween += parent.transform.rotation.eulerAngles.z;
或者您可以使用 *
operator
var angleBetween = Vector2.SignedAngle(relativePositionB, relativePositionA);
parent.transform.rotation *= Quaternion.AngleAxis(angleBetween, Vector3.back);
顺便说一句,我会简单地移动
的部分worldPositionB = Camera.main.ScreenToWorldPoint(Input.mousePosition);
进入 OnMouseDrag()
因为只有那里需要它。
并且通常您应该存储 Camera.main
引用,因为此调用非常昂贵:
// If possible even already reference this via the Inspector!
[SerializeField] private Camera _mainCamera;
// As fallback get it ONCE on runtime
private void Awake()
{
if(!_mainCamera) _mainCamera = Camera.main;
}
private void OnMouseDown()
{
worldPositionA = _mainCamera.ScreenToWorldPoint(Input.mousePosition);
}
private void OnMouseDrag()
{
worldPositionB = _mainCamera.ScreenToWorldPoint(Input.mousePosition);
var relativePositionA = worldPositionA - parent.transform.position;
var relativePositionB = worldPositionB - parent.transform.position;
我仍然不知道我以前的脚本有什么问题,但我找到了另一种方法来完成我想要的。 我没有直接使用 Transform,而是添加了一个 Rigidbody 并修改了 Rotation 值。 这是脚本,以防有人需要它:
[SerializeField] private Camera _mainCamera; //Assigned from Inspector
[SerializeField] private GameObject parent = null; //Assigned from Inspector
private Rigidbody2D parentRb2d;
float startRotation;
float angleBetween;
private void Awake()
{
parentRb2d = parent.GetComponent<Rigidbody2D>();
}
private void Update()
{
worldPositionB = _mainCamera.ScreenToWorldPoint(Input.mousePosition);
}
private void OnMouseDown()
{
worldPositionA = _mainCamera.ScreenToWorldPoint(Input.mousePosition);
startRotation = parentRb2d.rotation;
}
private void OnMouseDrag()
{
Vector3 relativePositionA = worldPositionA - parent.gameObject.transform.position;
Vector3 relativePositionB = worldPositionB - parent.gameObject.transform.position;
angleBetween = Vector2.SignedAngle(relativePositionB, relativePositionA);
parentRb2d.MoveRotation(startRotation - angleBetween);
}
如果您的对象在相反的方向,请尝试将最后一行中的“-”运算符切换为“+”。