0 c# camera rotation unity-game-engine
我试图让第一人称相机具有最大的上倾角和下倾角,这样当向上或向下看时,它不会进行前后翻转.出于某种原因,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);
Run Code Online (Sandbox Code Playgroud)
我正在使用AddTorque,因为这些是车辆控制.
夹紧线有三个问题(和一些不相关的问题),第一个问题是:
rotation回来了Quaternion:
随意跳过下一个 一 二三......段落:*这些在Unity中一直是我的宠儿.从他们所说的,他们正在努力,但我的第一个小问题是Unity可能有一些你在成功的C#产品中找到的最差的命名约定.它的一部分源于支持3种语言(或者2取决于您认为支持的内容[Boo将不再记录并在编辑器菜单中提供]),但部分原因似乎是它们被现有代码捆绑在一起.但他们新的IL修改工具的存在可能在未来改变它...
另一个小小的烦恼是Unity使用可变结构.它们只会造成麻烦,因为修改结构只是不符合人们的期望,因为它们是价值类型.但是我不需要进入这个因为它已被检查过死亡.(MSDN也很清楚这是一个不好的做法).
据说Unity 在使用它们时是完全合理的.这只是游戏性能要求"覆盖"理想设计的一次.不可变结构的性能成本(即每次修改位置,旋转或任何其他基于结构的值时要求分配全新结构的成本)都太大了.
现在我的相关性 迷你咆哮独白:a Quaternion代表一个旋转四元数.游戏通常使用旋转四元数,主要是因为它们不受万向节锁定的影响.内部的Unity四元数(默认情况下,编辑器将旋转显示为欧拉角,因为如果没有非常高级的数学运算,您无法对它们进行任何有意义的更改).
但它使用相同的名称Quaternion成员名称和类型的组件x,y并且z因为它为Vector3的.Vector3一个非常常见的类,代表欧拉角度中的许多其他东西 (在它的防御中,这些是 a的那些组件的通用名称).因此,如果你环顾四周,你可以看到这给人们带来了很多麻烦,因为你可以很容易地修改思维,这是一个轮换.如果Unity没有使用可变结构,你必须分配一个,这将使错误清楚,但正如我所说,它是双手并列.实际上,您只能使用某些功能或运算符在Unity中修改.QuaternionQuaternionVector3QuaternionQuaternion
所以你可能想要的是transform.eulerAngles.
它允许您以度为单位设置旋转.请记住,正如文档所提到的,它只是设置绝对值(就像你想要做的那样).
所以现在你会......
Mathf.Clamp(transform.eulerAngles.x, -60, 60);
Run Code Online (Sandbox Code Playgroud)
......但由于第二个问题,这不起作用:
Mathf.Clamp是一个纯粹的功能.
它不会(也不能)修改传递给它的变量,而是返回限制值(PureAttribute由于它使用的.NET版本,Unity没有标记它)
现在你希望能够使用这样的东西......
transform.eulerAngles.x = Mathf.Clamp(transform.rotation.x,-60,60);
...但除非您使用UnityScript,否则无法使用(并且有充分的理由,UnityScript允许这是一个缺陷).最后一个问题是:
transform并且eulerAngles是属性.
所以你试图修改属性返回的结构的成员.这意味着尝试将值分配给将在语句结束时丢弃的memeber副本.为了可能过度简化它,那将要做的就是:
GetTransformSomehow().GetRotationSomehow().x = 20;
Run Code Online (Sandbox Code Playgroud)
这清楚地说明了为什么这样的赋值不起作用(这并不意味着编译器禁止它).
一个正确的方式来做到这一点如下.我选择了一个对象初始化程序和Mathf.Clamp对它的调用,因为它使你的想法一目了然,你可以充分利用可变结构(颤抖)并跳过额外的Vector3,但我觉得这可能会分散注意力(打电话transform.eulerAngles = currentEulerAngles可能看起来很奇怪):
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
};
Run Code Online (Sandbox Code Playgroud)所以,现在你有一些代码应夹紧变换,但我怀疑你会发现它可以:
或者表现得非常奇怪(也许抽搐)
即使它有效,也存在一些源于此问题的问题:
rigidbody.AddTorque(rotUpDown mouseSensitivity,rotLeftRight mouseSensitivity,0);
编辑:我没有注意到问题中提到的车辆控件,所以你可以跳过下一个问题.由于提到第一人称相机和车辆控制,我不再100%确定问题是什么.如果相机的变换和车辆的刚体,我建议将它们分开,以下内容仍然适用.但如果两者都属于车辆,我不确定相机应该如何表现(截图或图表可以帮助清除它).
首先,我怀疑你真的想用Rigidbody一个第一人称相机.即使是最逼真的第一人称相机也不会以物理模拟的方式表现.您可以使用动画和速度偏移来模拟您看到的诸如摆动和惯性等游戏的效果.所以你应该使用Transform.Rotate(docs),如果你发现自己正在寻找"像物理一样"的使用技巧,比如:
减少mouseSensitivity.这是降低响应能力的最简单方法,比如角色昏昏欲睡时.
基于夹紧的"速度"进行旋转,该速度随时间衰减(与Rigidbody速度不同,但在转动时模拟惯性)
每帧都将相机拉到角色模型上的某个位置.这可以模拟跳跃或下降时的动量,如果角色的模型是动画的,它可以增加额外的真实感.如果没有模型,请将相机拍摄到相对于角色对撞机的位置.
使用的另一个可能的问题Rigidbody是它的行为是非确定性的.这对你的游戏是否重要取决于它的性质,但一般来说,人们期望相同的控制运动每次制作时都会产生相同的游戏内相机运动.但是使用一种Rigidbody手段,每次他们移动控件时,他们都在"掷骰子".没有机会过度习惯于精确的动作,因为它们在机器之间的结果各不相同,甚至有时从第二到第二.它还可以使网络更加复杂,因为您无法再从播放器传递的输入中严格重现一组动作.
话虽这么说,下一个问题适用于任何一种方法:
使用当前代码,相机将在较慢的设备上移动较慢.你一直想要的3D运动是帧率独立运动(在2D上它有时是值得商榷的).修复(或至少一次修复)很简单,只需乘以自上一帧(Time.delta)以来的时间.如果它很大(即游戏运行缓慢),相机移动得更快,如果它很小(即游戏运行速度很快),相机移动Time.delta得更慢,只记得你需要更大的数字来获得mouseSensitivity,因为它会更少超过1(大约0.01660fps):
float rotLeftRight = Input.GetAxis("Mouse X") * Time.delta;
float rotUpDown = Input.GetAxis("Mouse Y") * Time.delta;
Run Code Online (Sandbox Code Playgroud)
您还需要以与现在不同的顺序应用旋转或扭矩,因为当前代码在夹紧后修改旋转.
编辑:下一个问题可能也不适用.
在这里,我将展示该Transform方法,因为如果使用a的代码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
};
Run Code Online (Sandbox Code Playgroud)
编辑:我没有阅读问题的最后部分,我想我不会跳过解决FixedUpdate问题(你应该看一下Unity教程,它们会讨论我在这里提到的大部分内容[并且是可能更好地解释它]).
你应该使用的原因背后的想法FixedUpdate与你需要使用的原因有关Time.delta(实际上,FixedUpdate你不再需要使用它),但是足够宽,并且有足够的文档记录,我建议你做一些研究.但是提到的文档FixedUpdate的简单规则是"[it]应该在处理时使用而不是Update Rigidbody".
FixedUpdate可以多次调用一次帧,但更重要的是,对于此代码,这些调用在Update之前发生.因此,您需要将部分代码移动到FixedUpdate,但获取输入的代码需要保留Update.这是因为,正如我所提到的,FixedUpdate将在之前执行Update,但输入在开始时Update被处理.这意味着你最终会遇到奇怪的错误,例如缺少某些帧上的输入,以及无法使用基于按钮的输入.
我个人将Input调用与此脚本分开并将其放入此脚本引用的另一个对象中.它不仅可以处理方法之间的数据共享,还可以让您执行各种很酷的操作,例如重放数据,AI控制,更重要的是,可以执行不同的控件,而无需更改此脚本.但为了简单起见,我将使用私有字段:
private float _verticalInput = 0f;
private _horizontalInput = 0f;
void Update()
{
_horizontalInput = Input.GetAxis("Mouse X");
_verticalInput = Input.GetAxis("Mouse Y");
var currentEulerAngles = transform.eulerAngles; //Store the value we have after rotations
//Update is called after Fixed Update, so we can do this
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
};
}
void FixedUpdate()
{
//We don't need Time.delta anymore since we're moving in FixedUpdate. (You can still use it to keep your units consistent)
rigidbody.AddTorque(_verticalInput*mouseSensitivity, _horizontalInput*mouseSensitivity, 0);
}
Run Code Online (Sandbox Code Playgroud)