第一人称视角做前台,并在C#后台翻转 [英] First person camera doing front- and back-flips in C#

查看:259
本文介绍了第一人称视角做前台,并在C#后台翻转的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想获得一个第一人称相机最大可达角和下角都有,所以它不会做前端和后端翻转。出于某种原因, Mathf.Clamp 不守我的X-旋转固定。
任何想法,我能做些什么这个问题?

I'm trying to get a first person camera to have a maximum up-angle and down-angle, so that it doesn't do front- and back-flips when looking to far up or down. For some reason, the Mathf.Clamp doesn't keep my x-rotation fixed. Any ideas as to what I can do about this issue?

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 ,因为这些。是车辆控制

I'm using AddTorque, since these are vehicular controls.

推荐答案

有三个问题与夹紧线(和一些无关的),第一个是:

There are three problems with the clamping line (and a few unrelated ones), the first is:


  • 旋转返回一个四元数 的:

  • rotation is returning a Quaternion:

随意跳过下有一个 两个 3 ...段落:*这些具有的总是的一直在团结煤矿宠物怨恨。从他们所说的内容,他们它的工作的,但我的第一个宠物忿怒是团结可能有一些最严重的命名约定,你会成功的C#的产品找到。它的一部分从支持3种语言(或2这取决于你考虑支持[嘘会阻止被记录,并提供编辑器菜单])茎,但它的一部分似乎只是他们有他们的手由现有的代码捆绑。但是,他们的新IL修改工具的存在可能会改变,在未来的...

Feel free to skip the next one two three... paragraphs:* These have always been pet-peeves of mine in Unity. From what they've said, they're working on it, but my first pet-peeve is that Unity probably has some of the worst naming conventions you'll find in successful C# product. Part of it stems from supporting 3 languages (or 2 depending on what what you consider support [Boo is going to stop being documented and available in the editor menu]), but part of it just seems to them having their hands tied by existing code. But the presence of their new IL modification tools might change that in the future...

其他宠物怨恨是团结的利用可变结构的。它们会导致什么,但麻烦,因为修改结构只是不工作的人是怎么想到的,因为它们是值类型。但我并不需要进入这一点,因为它已经检查,死亡。 (MSDN也使得很清楚这是一个不好的做法)。

The other pet-peeve is Unity's use of mutable structs. They cause nothing but trouble, because modifying structs just doesn't work how people expect it to, since they're value types. But I don't need to get into this because it's been examined to death. (MSDN also makes it pretty clear it's a bad practice).

话虽这么说,团结是的完全有理由的使用它们。这是当游戏的性能要求覆盖理想的设计只是一个时间。不变结构的性能成本(即需要一个全新结构的成本在每次分配的位置,旋转,或任何其他结构基于价值进行了修改)将刚刚太大。

That being said Unity is totally justified in using them. This is just one time when the performance demands of games "overrides" ideal design. The performance cost of immutable structs (i.e. the cost of requiring a brand new struct to be allocated every time a position, rotation, or any other struct based value was modified) would just be too great.

现在我的迷你咆哮线独白的相关性:一个四元数表示的旋转四元数。游戏经常使用旋转四元,主要是因为他们没有受到万向节锁定。统一内部四元数(编辑显示旋转为欧拉角默认情况下,因为你不能给他们任何有意义的变化没有非常先进的数学运算)。

Now the relevance of my mini-rant monologue: a Quaternion represents a rotation quaternion. Games often use rotation quaternions mainly because they aren't subject to gimbal lock. And Unity quaternions internally (the editor shows rotations as Euler angles by default because you can't make any meaningful changes to them without very advanced math).

但它使用了四元数的组件成员名称和类型 X相同的名称,以Z 因为它为的Vector3 的。 的Vector3 表示,除一个很常见的类的很多其他的事情欧拉角(在它的防御那些的的一个四元数)的那些组件的通用名称。所以,如果你看看周围,你可以看到,已经引起许多麻烦的人,因为你可以很容易地修改四元数想这是一个的Vector3 持有的旋转。如果统一没有使用可变的结构,你不得不指定一个四元数,这会使得出错清除,但正如我所说,这是缚手缚脚。在现实中,你只能使用某些功能,或运营商修改四元数在Unity。

But it uses the same names for Quaternion member names and types for the components x,y and z as it does for Vector3's. Vector3 a very common class that represents, amongst many other things Euler angles (in it's defense those are common names for those components of a Quaternion). So if you look around you can see that has caused lots of trouble for people, since you can easily modify a Quaternion thinking it's a Vector3 holding a rotation. If Unity didn't use mutable structs you'd have to assign a Quaternion, which would make the error clear, but as I said, it's hands are tied. In reality you only modify a Quaternion in Unity using certain functions, or operators.

所以,你可能想要的是 transform.eulerAngles

So what you probably want is transform.eulerAngles.

它允许您设置的旋转度。只要记住,作为文档提到,这只是设置绝对值(就像你正在试图做的)。

It allows you to set the for rotation in degrees. Just remember, as the docs mention, it's only for setting absolute value (like you're trying to do).

所以,现在你有...

So now you'll have...

 Mathf.Clamp(transform.eulerAngles.x, -60, 60);



...但是,这不会因为第二个问题的工作:

...but that won't work because of the second problem:

Mathf.Clamp 是一个纯函数的。

Mathf.Clamp is a pure function.

它不会(也不能)修改您传递给它,而不是它的返回值钳位(团结没有它标明变量 PureAttribute ,因为它使用的.NET版本)

It won't (and can't) modify the variables you pass to it, instead it returns the clamped value (Unity doesn't have it marked with PureAttribute because of the .NET version it uses)

现在,你希望能够利用这样的事情..

Now you'd expect to able to use something like this...

transform.eulerAngles.x = Mathf.Clamp(transform.rotation.x,-60,60);

transform.eulerAngles.x = Mathf.Clamp(transform.rotation.x, -60, 60);

...但除非你在UnityScript是,这是行不通的(并且有很好的理由,UnityScript使这是一个缺陷)。最后一个问题是:

...but unless you're in UnityScript, that won't work (and with good reason, UnityScript allowing that is a flaw). The final problem is:

变换为:eulerAngles 是属性的。

transform and eulerAngles are properties.

所以,你想修改由属性返回一个结构的成员。这意味着试图分配一个值,将在声明的结尾被丢弃memeber的副本。为了可能过于简化,什么这将是在这里做等同于:

So you're trying to modify a member of a struct that was returned by the property. That means trying to assign a value to a copy of the memeber that will be discarded at the end of the statement. To possibly oversimplify it, what that would be doing is equivalent here to:

  GetTransformSomehow().GetRotationSomehow().x = 20;

这使得它清楚为什么这样的分配将无法正常工作(这并不意味着任何东西所以编译器禁止它)。

That makes it clear why such an assignment won't work (it wouldn't mean anything so the compiler prohibits it).

一个的正确的方式来做到这一点如下。我选择它里面的对象初始化,并调用 Mathf.Clamp ,因为它使得它非常清楚你有什么想法是一目了然,你可以利用可变的结构(战栗),并跳过额外的的Vector3 ,但我觉得可能会分心这里(调用 transform.eulerAngles = currentEulerAngles 可能看上去有些诡异):

One of the correct ways to do this follows. I choose an object initializer and the call to Mathf.Clamp inside it because it makes it abundantly clear to your what the idea is at a glance, you could take advantage the mutable structs (shudder) and skip the extra Vector3, but I felt that might be distracting here (calling transform.eulerAngles = currentEulerAngles might have looked strange):

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
};


所以,现在你有一些代码的的夹紧变换,但我怀疑你会发现它可以:

So now you have code that should clamp the transform, but I suspect you'll find it either:


  • 在所有不正常工作

  • 还是很有些奇怪(也许抽搐)

  • Doesn't work at all
  • Or is acting very strangely (maybe twitching)

即使它的工作原理,存在一些问题,从这一行干:

Even if it works, there are some problems that stem from this line:

rigidbody.AddTorque(rotUpDown在mouseSensitivity *,* rotLeftRight在mouseSensitivity,0);

rigidbody.AddTorque(rotUpDown*mouseSensitivity, rotLeftRight*mouseSensitivity, 0);

编辑:我没有注意到的问题车辆控制的提及,因此有可能你可以跳过这下问题。我不再100%肯定是什么的问题是因为两个第一人称视角和车辆控制的提要求。如果变换摄像头,和刚体的车辆,我建议将它们分开了,下面仍然适用。但是,如果同属车,我不知道相机应该如何表现(屏幕截图或图表可以帮助清除了)。

I didn't notice the mention of vehicular controls in the question, so it's possible you can skip this next problem. I'm no longer 100% sure what the question is asking because of the mention of both a first person camera and vehicular controls. If the transform for the camera, and the rigidbody for the vehicle, I'd recommend separating them, and the following will still apply. But if both belong to the vehicle, I'm not sure how the camera is supposed to behave (a screenshot or diagram could help clear that up).

首先,我怀疑你真的想使用刚体的第一人称相机。即使是最逼真的第一人称相机将不会通过物理模拟口述的方式来表现。您所看到的效果,如颠颠惯性一些游戏使用动画和速度偏移模拟。所以,你应该使用 Transform.Rotate 文档),如果你发现自己寻找物理学类使用的技术,如:

First off, I doubt you actually want to use a Rigidbody for a first person camera. Even the most realistic first person camera won't be behaving in a manner dictated by physics simulation. The effects you see some games like bobbing and inertia are simulated using animations and speed offsets. So you should be using Transform.Rotate (docs), and if you find yourself looking for "physics-like" use techniques like:


  • 降低在mouseSensitivity 。这是减少响应,当一个字符是困像最简单的方法。

  • Decreasing mouseSensitivity. That's the simplest way to reduce responsiveness, like when a character is sleepy.

此基础上随着时间衰减被夹住的速度旋转(不一样的为刚体速度,但转弯时它模拟惯性)

Basing the rotation on a clamped "velocity" that decays over time (not the same as Rigidbody velocity, but it simulates inertia when turning)

Lerping相机上的位置角色模型每一帧。跳跃或下降时,这种模拟的势头,如果人物的模型动画,也可以添加额外的真实感。如果没有模型,线性插值相机相对于人物的碰撞位置。

Lerping the camera to a position on the character's model each frame. This simulates momentum when jumping or falling, and if the character's model is animated, it can add extra realism. If there's no model, Lerp the camera to a position relative to the character's collider.

另一个可能出现的问题,使用刚体的是它的行为的非确定性的。不管这关系到你的游戏依赖于它的天性,但总体来说,人们期望相同的控制动作将在每次他们让时间产生相同的游戏中的相机移动。但使用刚体意味着每次移动的控制时,他们掷骰子。有没有机会得到过分习惯于精确的动作,因为他们在结果从机器到机器的改变,甚至有时从第二至第二位。它也可以使网络更加复杂,因为你不能再从输入玩家通过再现一组动作的严格把关。

Another possible problem with using Rigidbody is that it's behavior is non-deterministic. Whether or not this matters to your game depends on it's nature, but generally speaking, people expect the same control movement will produce the same in-game camera movement every time they make it. But using a Rigidbody means that each time they move the controls, they're "rolling the dice". There's no chance to get overly accustomed to precise movements because they're vary in result from machine to machine, and even sometimes from second to second. It can also make networking more complex because you can no longer reproduce a set of movements strictly from the input the player passed.

话虽这么说,这下问题适用于这两种方法:

That being said, this next problem applies to either approach:

根据您目前的代码时,摄像机将移动较慢的速度较慢的设备。你总是希望在3D运动是什么的帧率-独立运动的(二维它有时值得商榷)。此修复程序(或至少,一个固定)很简单,只要乘上时间,因为最后一帧( Time.delta )。如果是大(即游戏运行缓慢)将摄像机移动快,如果它是小(即游戏快速运行)将摄像机移动速度较慢,只记得你所需要的在mouseSensitivity更大的数字,因为 Time.delta 将是小于1(约 0.016 在60fps):

With your current code, the camera will move slower on slower devices. What you always want in 3D movement is framerate-independant movement (on 2D it's sometimes debatable). The fix (or at least, one fix) is simple, just multiply by the time since the last frame (Time.delta). If it's large (i.e. the game is running slow) the camera moves faster, and if it's small (i.e. the game is running fast) the camera moves slower, just remember you'll need bigger numbers for mouseSensitivity, since Time.delta is going to be less than 1 (around 0.016 at 60fps):

float rotLeftRight = Input.GetAxis("Mouse X") * Time.delta;
float rotUpDown = Input.GetAxis("Mouse Y") * Time.delta;

您还需要应用旋转或转矩以不同的顺序比你现在,因为你的当前的代码修改旋转的后夹住

You will also need to be apply rotation or torque in a different order than you have now, because your current code is modifying the rotation after you clamp it.

编辑:接下来的这个问题也可能不适用

This next problem might also not be applicable.

下面我将显示变换方法,因为如果你使用了<$ C代码$ C>刚体实际上应该 FixedUpdate 和更新,并增加了一个并发症少。

Here I'm going to show the Transform method because the code for if you use a Rigidbody should actually be split between FixedUpdate and Update, and it adds a few complications.

    //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
    };

编辑:的我无法读取的问题的最后一部分,我想我不要跳过了就应对 FixedUpdate 的问题(你应该看的团结教程的,他们去了大部分的我这里提到的东西[和可能的解释更好])。

I failed to read the last part of the question, and I guess I don't get to skip out on tackling the FixedUpdate issue (you should look at the Unity tutorials, they go over most of the stuff I mentioned here [and are probably better at explaining it]).

背后的想法的为什么的你应该使用 FixedUpdate 有关,为什么你需要使用 Time.delta (事实上,在 FixedUpdate 您不再的需求的被它乘)但足够宽,并且有据可查的是,我会建议做一个小小的研究。但简单的规则,的文档 FixedUpdate 提到的是[它]应该不是用刚体使用更新的。

The idea behind why you should use FixedUpdate is related to why you need to use Time.delta (in fact, in FixedUpdate you no longer need to multiply by it), but is wide enough, and well documented enough, that I'd recommend doing a little research. But the simple rule that the documentation for FixedUpdate mentions is "[it] should be used instead of Update when dealing with Rigidbody".

FixedUpdate 可以得到多次调用框架,但更重要的是这段代码,会发生这些调用的之前相对=nofollow>。所以你需要移动代码的一部分 FixedUpdate ,但得到的输入代码需要留在更新。这是因为,正如我所说, FixedUpdate 在启动更新,但输入得到处理的前将执行更新 的。这意味着你会莫名其妙的错误,就像某些帧丢失的输入,以及无法使用基于按钮输入结束。

FixedUpdate can get called multiple times a frame, but more importantly for this code, these calls occur before Update. So you'll need to move part of the code to FixedUpdate, but the code that gets input needs to stay in Update. This is because,as I mentioned , FixedUpdate will execute before Update, but input gets processed at the start of Update. That means you'll end up with weird bugs, like missing input on some frames, and the inability to use button based inputs.

我个人分开输入电话从这个脚本,并把它放在另一个对象,此脚本引用。它不仅采取的方法之间共享数据关怀,它可以让你做各种很酷的事情一样回放数据,A.I。控制,并且更重要的是,不必改变该脚本不同的控制。但是,为了简单起见,我只使用一个私有字段:

I'd personally separate the Input calls from this script and put it in another object that this script references. Not only does it take care of sharing the data between the methods, it lets you do all kinds of cool things like replay data, A.I. control, and more importantly, different controls without having to change this script. But for simplicity I'll just use a private field:

    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);
    }

这篇关于第一人称视角做前台,并在C#后台翻转的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆