gpt4 book ai didi

c# - 如何使用 LookRotation 根据手的位置旋转对象?

转载 作者:行者123 更新时间:2023-12-05 05:43:43 38 4
gpt4 key购买 nike

我正在为 VR 游戏创建一个交互系统,但我正在努力处理双手交互。我正在使用 Quaternion.LookRotation()方法根据手的位置和旋转生成抓取对象的旋转。前向部分非常简单:

Vector3 fwd = (primaryHand.position - secondaryHand.position).normalized;

“向上”部分是我遇到的困难。最初我尝试使用双手的平均向上方向:

Vector3 avgUp = Quaternion.Slerp(primaryHand.rotation, secondaryHand.rotation, 0.5f) * Vector3.up;

这种方法存在一个问题:手的向上向量可能与 fwd 向量对齐,这会导致对象在越过它时翻转过来。这是问题的简单说明: Small direction changes can cause the object to flip over.浅绿色箭头表示手的向上方向,而深绿色是用作 LookRotation() 方法参数的计算方向。

明显的解决方案似乎是选择一个不同的固定向量而不是“向上”,一个不会很容易与 fwd 向量对齐的向量。在示例中,它可以是与手指对齐的向量。但请记住,手的初始旋转没有限制,因此无论您选择哪个向量,手总是会发生旋转,以便向量对齐。即使您动态地选择最佳矢量(一个垂直于 fwd 的矢量),它与 fwd 的对齐仍然最多为 90 度。

为了解决这个问题,我尝试将方向限制为不会导致问题的值,但这导致了另一个问题(我很难确定哪些值是正确的,哪些应该被丢弃)。我觉得我在这里做错了什么,有没有更好的解决方案?

最佳答案

您可以计算手的旋转增量并将其应用于对象的“基础向上”(如果我们只考虑手的位置变化,则为新的向上……这当然与物体的轴)。然后确定导致每只手向上旋转的角度变化。平均这些角度,然后使用 Quaternion.AngleAxis 将这些角度与手轴应用到之前的“基础向上”。然后你就有了 Quaternion.LookRotation 的前进和向上)。

下面是如何使用它的示例,包括 VR 手部噪音模拟。要查看测试,请统一创建一个新场景并将其附加到相机,它将在播放开始时构建场景。有一个抓握/释放 gui 将出现在播放 View 中。您可以在场景 View 中调整手的旋转

example of use

    Vector3 leftHandPosCurrent;
Vector3 rightHandPosCurrent;

Vector3 worldAxisPrev;

Quaternion leftHandRotPrev;
Quaternion leftHandRotCurrent;

Quaternion rightHandRotPrev;
Quaternion rightHandRotCurrent;

bool isGripping;
bool firstFrameGripping;

Rigidbody grippedRB;

Transform leftHand;
Transform rightHand;

Quaternion targetRot;

/*
* On subsequent frames of gripping, calculate deltas in positions and
* rotations, average out the hand's effects, then apply them to the gripped
* object
*/
void HandleGrippedRot()
{
Vector3 worldAxisCurrent = rightHandPosCurrent - leftHandPosCurrent;

if (!firstFrameGripping)
{
Vector3 prevUp = targetRot * Vector3.up;
// we haven't moved the transform based on the hands yet, so
// find the new up would be ignoring hand rotations
Vector3 newUp = Quaternion.FromToRotation(worldAxisPrev,
worldAxisCurrent) * prevUp;

float leftHandAngle = GetDegRot(newUp, leftHandRotPrev,
leftHandRotCurrent, worldAxisCurrent);
float rightHandAngle = GetDegRot(newUp, rightHandRotPrev,
rightHandRotCurrent, worldAxisCurrent);
float avgAngle = (leftHandAngle + rightHandAngle) * 0.5f;

newUp = Quaternion.AngleAxis(avgAngle, worldAxisCurrent) * prevUp;

targetRot = Quaternion.LookRotation(worldAxisCurrent,
newUp);
}
else
{
firstFrameGripping = false;
}

leftHandRotPrev = leftHandRotCurrent;
rightHandRotPrev = rightHandRotCurrent;

worldAxisPrev = worldAxisCurrent;
}

/*
* Given the "up" of the object without taking hand rotations into account
* and the axis, determine how a hand's delta rotation affects that up
* around the axis and return the angle of that rotation
*/
float GetDegRot(Vector3 baseUp, Quaternion prevHandRot, Quaternion curHandRot,
Vector3 axis)
{
Vector3 adjUp = (curHandRot * Quaternion.Inverse(prevHandRot)) * baseUp;
adjUp = Vector3.ProjectOnPlane(adjUp, axis);

return Vector3.SignedAngle(baseUp, adjUp, axis);
}

void Update()
{
AddVRNoise(leftHand);
AddVRNoise(rightHand);

leftHandPosCurrent = leftHand.position;
rightHandPosCurrent = rightHand.position;

leftHandRotCurrent = leftHand.rotation;
rightHandRotCurrent = rightHand.rotation;


if (isGripping)
{
HandleGrippedRot();
}
}

void StartGrip()
{
if (isGripping) return;
isGripping = true;
firstFrameGripping = true;
// grippedTransform is set accordingly at some point
}

void EndGrip()
{
if (!isGripping) return;
isGripping = false;
}

/*
* example of using targetRot to move rb
*/
private void FixedUpdate()
{
if (!isGripping) return;

Quaternion delta = targetRot
* Quaternion.Inverse(grippedRB.transform.rotation);

delta.ToAngleAxis(out float angle, out Vector3 axis);

// convert to shortest angle form
if (angle > 180f)
{
axis = -axis; angle = 360f - angle;
}

grippedRB.angularVelocity = angle * 0.25f * axis;
}

/*
* just for testing purposes
*/
void Start()
{
leftHand = CreateHand(true);
leftHand.position = Vector3.left;

rightHand = CreateHand(false);
rightHand.position = Vector3.right;

CreateArrow();
}

/*
* just for testing purposes
*/
void AddVRNoise(Transform hand)
{
Quaternion noise = Random.rotation;
noise.ToAngleAxis(out float angle, out Vector3 axis);
angle = 100f * Time.deltaTime;
noise = Quaternion.AngleAxis(angle, axis);

Quaternion noisyRot = hand.rotation * noise;
hand.rotation = noisyRot;
}

/*
* just for testing purposes
*/
void OnGUI()
{
if (GUI.Button(new Rect(0, 0, 100, 50), "Grip"))
{
StartGrip();
}

if (GUI.Button(new Rect(100, 0, 100, 50), "Release"))
{
EndGrip();
}
}


/*
* just for testing purposes
*/
Transform CreateHand(bool isLeft)
{
string handName = isLeft ? "Left" : "Right";
GameObject hand = new GameObject($"{handName}hand");
GameObject palm = GameObject.CreatePrimitive(PrimitiveType.Cube);
palm.transform.localScale = new Vector3(0.5f, 0.2f, 1f);
palm.transform.SetParent(hand.transform);
GameObject thumb = GameObject.CreatePrimitive(PrimitiveType.Cube);
thumb.transform.localScale = new Vector3(0.2f, 0.1f, 0.1f);
thumb.transform.SetParent(hand.transform);
thumb.transform.localPosition = new Vector3(isLeft ? 0.32f : -0.32f,
0f, -.31f);

return hand.transform;
}

/*
* just for testing purposes
*/
void CreateArrow()
{
GameObject arrow = new GameObject();
GameObject body = GameObject.CreatePrimitive(PrimitiveType.Cube);
body.transform.localScale = new Vector3(1f, 1f, 5f);
body.transform.SetParent(arrow.transform);
GameObject head = GameObject.CreatePrimitive(PrimitiveType.Cube);
head.transform.SetParent(arrow.transform);
head.transform.localEulerAngles = Vector3.up * 45f;
head.transform.localPosition = new Vector3(0f, 0f, 2.5f);

grippedRB = arrow.AddComponent<Rigidbody>();
grippedRB.useGravity = false;

arrow.transform.position = 2f * Vector3.up;
}

关于c# - 如何使用 LookRotation 根据手的位置旋转对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71764354/

38 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com