动画抖动

今天终于解决了动画抖动的问题
 首先描述下动画抖动的表现:就是将人物拉近到基本充满屏幕的时候,运动比较快的肢端明显运动不平滑。
 一开始怀疑动画代码写得有问题,仔细检查代码后没有发现问题。再次怀疑浮点运算误差造成的,因为骨骼动画中,骨骼的运算,是靠从跟骨骼,一级一级级联运算过来的,在肢端级联次数是最多的。更改编译选项,从原来的最快的浮点运算改为最精确的浮点运算。问题没有解决。然后通过在同一帧里,逐次增加10ms绘制50次,表现出来的连续轨迹间距均匀。遂否定了骨骼动画代码的问题。
 然后为了找出问题,收集了每帧之间的时间间隔,并用立方图表现出来,发现在2D环境和3D环境下差别较大。2D环境较为平稳,3D环境下波动较大。于是,考虑是不是垂直同步造成的,在网上搜索,居然有一大堆关于如何调节显卡参数解决游戏中画面抖动问题的。于是,思路被转到考虑显卡的参数或BUG上去了,折腾两天,无果。想到自己机器上装有WoW,遂进入WoW,选择自己NB的战士号,在人物选择界面观察了半天,发现WoW里,动画是平滑的。否定了是显卡的BUG和创建设备的参数的问题。
 同时,谢Boss也在查这个问题,他告诉了我一个让我觉得萝卜都靠不住的消息:timeGetTime()的误差在15ms左右。测试代码如下: 
 DWORD dwLastTime = timeGetTime();
 for(;;)
 {
  Sleep(0);
  DWORD dwTime = timeGetTime();
  if(dwTime - dwLastTime > 1)
   TRACE("timeGetTime = %u/n",dwTime - dwLastTime);
  dwLastTime = dwTime;
 }

 居然输出一大堆15,16出来。当时的心情......要知道,之前因为使用QueryPerformanceCounter在AMD的双核CPU上也出错——现在我在Windows上居然找不到一个可以信任的取时间函数了!
 直觉告诉我不应该是这样的,平时我们听音乐看电影,并没有出现由于时间错误导致的问题,而timeGetTime就是多媒体使用的函数。再者,我不希望Microsoft倒掉——我只会在Microsoft的Windows下写程序混饭吃。AMD倒掉了,我还有Intel;ATI倒掉了,我还有NVidia——Microsoft倒掉了,我可没有Linux。
 MSDN永远是Windows程序员最好的老师,大约30秒后,我证明了萝卜还是可靠的:
 timeBeginPeriod(1);
 DWORD dwLastTime = timeGetTime();
 for(;;)
 {
  Sleep(0);
  DWORD dwTime = timeGetTime();
  if(dwTime - dwLastTime > 1)
   TRACE("timeGetTime = %u/n",dwTime - dwLastTime);
  dwLastTime = dwTime;
 }
 timeEndPeriod(1);

 看来,问题就在之前没有使用timeBeginPeriod(1);而导致timeGetTime()的误差过大,使得帧之间的时间间隔误差过大,导致动画抖动的。带着这个思路,把谢Boss的消息循环的代码拿过来看了看,发现些细节上的问题,同时,利用timeSetEvent写了一个靠得住的休眠函数:
 static void XSleep(DWORD dwDelay,HANDLE hEvent)
 {
  MMRESULT hTimer = timeSetEvent(dwDelay,1,(LPTIMECALLBACK)hEvent,0,TIME_ONESHOT | TIME_CALLBACK_EVENT_SET);
  MsgWaitForMultipleObjectsEx(1,&hEvent,INFINITE,QS_ALLINPUT,0); //当有Windows消息时,还能继续处理Windows消息。故选择了这个函数。
  timeKillEvent(hTimer);
 }

 整个消息循环代码如下:

 MSG msg;
 DWORD dwLastTime;
 HANDLE hSleepEvent = CreateEvent(NULL,FALSE,FALSE,NULL);

 timeBeginPeriod(1);

 dwLastTime = timeGetTime();
 while(isActive())
 {
  //需要一直处理Windows消息到无消息处理为止
  for(;PeekMessage(&msg,NULL,0,0,PM_REMOVE);)
  {
   if(msg.message == WM_QUIT)
   {
    CloseHandle(hSleepEvent);
    timeEndPeriod(1);
    return ;
   }
   if(!TranslateAccelerator(msg.hwnd,hAccelTable,&msg))
   {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
   }
  }

  DWORD FrameDelay = max(1,1000/max(1,GetMaxFPS()));
  DWORD dwTime = timeGetTime();
  if(dwLastTime + FrameDelay > dwTime)
  {
   XSleep(dwLastTime + FrameDelay - dwTime,hSleepEvent);
  }
  else
  {
   update();
   dwLastTime += ((dwTime - dwLastTime) / FrameDelay) * FrameDelay; //当实际帧数严重低于预期帧数时,这段代码可以完成跳帧功能;当实际帧数大于等于预期帧数时,这段代码仍然可以使帧之间的时间间隔固定。之前谢Boss没有处理好的主要就是这个。
  }
 }

 CloseHandle(hSleepEvent);
 timeEndPeriod(1);

 这样修改以后,帧之间的时间间隔误差基本在正负1ms之间了。动画抖动问题迎刃而解。

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值