我试图让第一人称相机具有最大的上倾角和下倾角,以便在向远处或向下看时它不会进行前后翻转。出于某种原因,Mathf.Clamp
并未保持我的x旋转固定。
关于我可以对这个问题做些什么的任何想法?
float rotLeftRight = Input.GetAxis("Mouse X");
float rotUpDown = Input.GetAxis("Mouse Y");
Mathf.Clamp(transform.rotation.x, -60, 60);
rigidbody.AddTorque(rotUpDown*mouseSensitivity, rotLeftRight*mouseSensitivity, 0);
我使用AddTorque
,因为这些是车辆控件。
答案 0 :(得分:6)
夹紧线有三个问题(和一些不相关的问题),第一个是:
rotation
正在返回Quaternion
:
随意跳过下一个一个 两个三个...段落:*这些总是在Unity中一直是我的宠儿。从他们所说的they're working on it开始,但我的第一个小问题是Unity可能有一些你在成功的C#产品中找到的最糟糕的命名约定。它的一部分源于支持3种语言(或者2取决于您认为支持的内容[Boo将不再记录并在编辑器菜单中提供]),但部分原因似乎是它们被现有代码捆绑在一起。但他们新的IL修改工具的存在可能在未来改变它......
另一个小小的烦恼是Unity使用可变结构。它们只会带来麻烦,因为修改结构并不会影响人们对它的期望,因为它们是值类型。但是我不需要进入这个因为它是examined to death。 (MSDN也使它成为pretty clear it's a bad practice)。
据说Unity在使用它们时完全合理。这只是一次游戏的性能要求超过"理想的设计。不可变结构的性能成本(即每次修改位置,旋转或任何其他基于结构的值时要求分配全新结构的成本)都太大了。
现在我的迷你咆哮独白的相关性:Quaternion
代表rotation quaternion。游戏通常使用轮换四元数,主要是因为它们不受gimbal lock的约束。内部的Unity四元数(默认情况下,编辑器将旋转显示为欧拉角,因为如果没有非常高级的数学运算,您无法对它们进行任何有意义的更改)。
但它对Quaternion
,x
和y
组件的z
成员名称和类型使用与Vector3
&#相同的名称39; S。 Vector3
一个非常常见的类,代表许多其他内容 Euler角度(在其中 Quaternion
的那些组件的名称。因此,如果你环顾四周,你可以看到这给人们带来了很多麻烦,因为你可以轻松修改Quaternion
认为它是Vector3
持有一个轮换。如果Unity没有使用可变结构,你必须分配一个Quaternion
,这会使错误清楚,但正如我所说,它的双手并列。实际上,您只能使用某些函数或运算符修改Unity中的Quaternion
。
所以你可能想要的是transform.eulerAngles
。
它允许您以度为单位设置旋转。请记住,正如文档所提到的,它只是设置绝对值(就像你正在尝试做的那样)。
所以现在你已经......
了 Mathf.Clamp(transform.eulerAngles.x, -60, 60);
...但由于第二个问题,它不会起作用:
Mathf.Clamp
是纯函数。
它不会(并且不能)修改您传递给它的变量,而是返回钳位值(Unity不会将其标记为PureAttribute
,因为它使用的.NET版本)
现在你希望能够使用这样的东西......
transform.eulerAngles.x = Mathf.Clamp(transform.rotation.x,-60,60);
...但除非您使用UnityScript,否则不会起作用(并且有充分的理由,UnityScript允许这是一个缺陷)。最后一个问题是:
transform
和eulerAngles
是属性。
因此,您尝试修改属性返回的结构的成员。这意味着尝试将值分配给将在语句结束时丢弃的memeber副本。为了可能过度简化它,那将要做的就是:
GetTransformSomehow().GetRotationSomehow().x = 20;
这清楚地说明了为什么这样的任务不起作用(它不会意味着什么,所以编译器禁止它)。
其中一个正确的方法如下。我选择了一个对象初始值设定项以及对它内部Mathf.Clamp
的调用,因为它使得你的想法一目了然,你可以充分利用可变结构(颤抖)并跳过额外的{{1}但是我觉得这可能会分散注意力(调用Vector3
可能看起来很奇怪):
transform.eulerAngles = currentEulerAngles
所以现在你有应该钳制转换的代码,但我怀疑你会发现它:
或者表现得非常奇怪(可能会抽搐)
即使它有效,也存在一些源于此问题的问题:
rigidbody.AddTorque(rotUpDown mouseSensitivity,rotLeftRight mouseSensitivity,0);
修改:我没有注意到问题中提到的车辆控件,所以您可以跳过下一个问题。由于提到了第一人称相机和车辆控制装置,我不再100%确定问题所在。如果摄像机的变换和车辆的刚体,我建议将它们分开,以下内容仍然适用。但如果两者都属于车辆,我不确定相机应该如何表现(截图或图表可以帮助清除它)。
首先,我怀疑你真的想用var currentEulerAngles = transform.eulerAngles; //Store the value we have
transform.eulerAngles = new Vector3();
{
x = Mathf.Clamp(currentRotation.x, -60, 60), //Set x to the value that got returned
y = currentEulerAngles.y, //Set y to the same value
z = currentEulerAngles.z //Set z to the same value
};
作为第一人称相机。即使是最真实的第一人称相机也不会以物理模拟的方式表现出来。您可以使用动画和速度偏移来模拟您看到的诸如摆动和惯性等游戏的效果。所以你应该使用Rigidbody
(docs),如果你发现自己正在寻找类似物理的"使用如下技术:
减少Transform.Rotate
。这是降低响应能力的最简单方法,例如当角色昏昏欲睡时。
根据夹紧的速度进行旋转"随着时间的推移而衰减(与mouseSensitivity
速度不同,但在转弯时会模拟惯性)
每帧拍摄相机到角色模特的位置。这可以模拟跳跃或摔倒时的动量,如果角色的模型是动画的,它可以增加额外的真实感。如果没有型号,请将相机拍到相对于角色对撞机的位置。
使用Rigidbody
的另一个可能问题是它的行为非确定性。这对你的游戏是否重要取决于它的本质,但一般来说,人们期望相同的控制运动每次制作时都会产生相同的游戏内相机运动。但是使用Rigidbody
意味着每次他们移动控件时,他们都会掷骰子"。没有机会过度习惯于精确的动作,因为它们因机器而异,甚至有时从第二到第二。它还可以使网络变得更加复杂,因为你无法再根据玩家传递的输入重现一组动作。
话虽如此,下一个问题适用于任何一种方法:
使用当前代码,相机在较慢的设备上移动速度会变慢。你在3D运动中总是想要的是与帧率无关的运动(在2D上它有时是值得商榷的)。修复(或至少一次修复)很简单,只需乘以自上一帧(Rigidbody
)以来的时间。如果它很大(即游戏运行缓慢),相机移动得更快,如果它很小(即游戏运行速度很快),相机移动得更慢,只记得你需要更大mouseSensitivity的数字,因为Time.delta
将小于1(60fps左右Time.delta
):
0.016
您还需要以与现在不同的顺序应用旋转或扭矩,因为您当前的代码正在修改后的 。
修改:下一个问题可能也不适用。
我将在此处显示float rotLeftRight = Input.GetAxis("Mouse X") * Time.delta;
float rotUpDown = Input.GetAxis("Mouse Y") * Time.delta;
方法,因为如果您使用Transform
的代码实际上应该在Rigidbody
和FixedUpdate
之间分配,并且它增加了一些复杂性。
Update
编辑:我没有看到问题的最后部分,我想我不会跳过解决 //Now framerate doesn't affect the controls
float rotLeftRight = Input.GetAxis("Mouse X") * Time.delta;
float rotUpDown = Input.GetAxis("Mouse Y") * Time.delta;
//Rotate the transform. Usually the multiplication of Time.delta would be inside this call, but for simplicity I multiplied it above
transform.Rotate(rotUpDown*mouseSensitivity, rotLeftRight*mouseSensitivity, 0);
var currentEulerAngles = transform.eulerAngles; //Store the value we have after rotations
transform.eulerAngles = new Vector3
{
x = Mathf.Clamp(currentRotation.x, -60, 60), //Set x to the value that got returned
y = currentEulerAngles.y, //Set y to the same value
z = currentEulerAngles.z //Set z to the same value
};
问题(你应该看一下) Unity tutorials,它们覆盖了我在这里提到的大部分内容[并且可能更好地解释它]。)
为什么你应该使用FixedUpdate
背后的想法与你需要使用FixedUpdate
的原因有关(实际上,在Time.delta
你不再是需要乘以它),但足够广泛,并且有足够的文档记录,我建议做一些研究。但是the documentation for FixedUpdate
提到的简单规则是#34; [it]应该在处理FixedUpdate
"时使用而不是更新。
Rigidbody
可以多次调用一次,但更重要的是,对于此代码,这些调用会发生before Update。因此,您需要将部分代码移至FixedUpdate
,但获取输入的代码需要保留在FixedUpdate
中。这是因为,正如我所提到的,Update
将在FixedUpdate
之前执行,但输入会在Update
的开头处理。这意味着你最终会遇到奇怪的错误,例如缺少某些帧上的输入,以及无法使用基于按钮的输入。
我亲自将Input调用与此脚本分开并将其放入此脚本引用的另一个对象中。它不仅负责在方法之间共享数据,还可以让你做各种很酷的事情,比如重放数据,A.I。控制,更重要的是,不必更改此脚本的不同控件。但为了简单起见,我只使用私人领域:
Update