Input.GetButtonDown没有正确响应

来源:互联网 发布:范冰冰章子怡 知乎 编辑:程序博客网 时间:2024/04/29 18:01

缘起

之前在游戏主城中做了个很简单的功能,点击某些建筑会有高亮效果。最近突然发现不好使了,偶尔会响应一次,但效果也缺失了一半。

最开始以为是建筑的包围盒出问题了,一想也不对,如果是这个原因,应该一直不出效果而不是偶尔正常。写了点日志,发现Input.GetButtonDown居然没有正常响应,瞬间就不淡定了。

镜花水月

最近也没有更新Unity版本,问题必然还在自身。纠结了很久,查看调用堆栈的时候,发现不知何时开始所有的逻辑代码全丢到一个FixedUpdate中更新了。凭直觉认定效果异常肯定跟这个改动有关。

依稀记得FixedUpdate描述是隔固定时间调用一次,查了下官方文档:

MonoBehaviour.FixedUpdate() 
Description 
This function is called every fixed framerate frame, if the MonoBehaviour is enabled. 
FixedUpdate should be used instead of Update when dealing with Rigidbody. For example when adding a force to a rigidbody, you have to apply the force every fixed frame inside FixedUpdate instead of every frame inside Update.

FixedUpdate以固定帧率每帧调用。看了这段描述,其实我挺疑惑的。如果FixedUpdate里一帧逻辑过于复杂,耗时超过了setting中的值会出现什么情况,FixedUpdate的调用原理到底是什么?

去年初用MFC做特效编辑器的时候遇见过一个BUG,早先的开发人员将主逻辑代码丢到SetTimer的回调中,0.01S触发一次回调。看起来没什么问题,100帧跑特效很流畅。不过某天遇见一个需要加载大量资源的特效时,堆栈满天乱飞,查得我异常蛋疼。最后才确定SetTimer不管你当前代码在哪个地方,到了触发时间,一定会强行中止,开始执行回调函数。

很流氓对不对?我知道Unity中的FixedUpdate不会这样,但仍然做了个测试。不管逻辑多么耗时,FixedUpdate都会完整执行所有的代码,然后开始下一次调用。那么就不存在Mfc中的问题,一次逻辑还没有完整调用就被打断从头开始。

对于FixedUpdate的猜测只是一场梦吗?

缘续

既然此路不通,只能转个方向前进,回到Input.GetButtonDown。官方文档这么写:

Input.GetButtonDown 
public static function GetButtonDown(buttonName: string): bool; 
Parameters 
Description 
Returns true during the frame the user pressed down the virtual button identified by buttonName. 
You need to call this function from the Update function, since the state gets reset each frame. It will not return true until the user has released the key and pressed it again.

瞬间又刷新了认知,我一直以为GetButtonDown按下之后会一直为true,谁知道下一帧就会重置呢。。。经验主义害死人!这也说明了多看官方文档的重要性。

等等,下一帧就会重置,而且还是在Update中!我似乎大概明白了什么,原因还是跟FixedUpdate有关!因为Update跟FixedUpdate的刷新频率不一样,FixedUpdate还没有取到GetButtonDown的值,就被Update先给重置了。网上找到一个妹纸的博客,里面有图看起来比较详尽,我就不重复写这块了。

链接地址:4. 让Input能够正确工作

文章里面这一小节是这样的:

一些人总觉得自己按照文档里去读取Input,但总是没有响应似的,苦苦找不到原因。其实,大部分这类原因是因为这里面涉及到了物理运算。官网上写到,物理运算要放到FixedUpdate里面进行处理,那么如果需要用Input控制物理运动,就把Input也放到FixedUpdate里面不就可以了吗?这是错的!这是因为,Update是真正和我们机器相关,帧率受不同机器性能影响的函数,而FixedUpdate则是一个Unity自行封装定义的函数,它的默认值一般是0.02m,也就是50fps,我们可以通过Edit -> Project Setting -> Physics来改变调用间隔时间。但要注意,这个值只是一个请求,你的机子可能不能达到这个速度。   因此,Update和FixedUpdate其实是完全独立的两个函数。当Update执行了一次时,FixedUpdate可能已经执行了两次、三次甚至一次都没有执行过。下面的例子就说明了为什么把Input放到FixedUpdate是错误的。

也就是说,下面的写法是错误的:

using UnityEngine;public class Test:MonoBehaviour{    void FixedUpdate(){        if(Input.GetKeyDown(KeyCode.Space)){           //Action        }    }}

正确的写法是:

using UnityEngine;public class Test:MonoBehaviour{    bool action = false;    void Update(){         if(Input.GetKeyDown(KeyCode.Space)){             action = true;         }    }    void FixedUpdate(){       if(action){           //Action           action = false;        }    }}

上面,我们用一个boolean值来控制开关。由于Update每一帧都会调用,因此可以保证所有的输入都可以被响应。

缘灭

虽然是个简单的BUG,不过也让我明白了刚转unity不久,自己还欠缺很多东西。需要沉下心来做个安静写代码,安心多读官方文档,多与人交流学习的程序猿。

附: 
candycat博客地址:http://blog.csdn.net/candycat1992

0 0
原创粉丝点击