五千年(敝帚自珍)

主题:初到贵宝地,灌一点游戏开发的东东 -- foundera

共:💬23 🌺4
全看分页树展 · 主题 跟帖
家园 游戏鼠标操作的思考

游戏鼠标操作的思考 出 处:GameRes.com

  常规的游戏都是在进行屏幕更新的时候再进行鼠标绘制,一旦FPS降低,鼠标的控制将非常的困难,这点相信大家都遇到过,这次想和大家讨论的就是如何让鼠标操作更贴切,更顺畅。问题如何解决?相信我们每天在使用Windows,Windows在磁盘操作,或者CPU繁忙的时候都能保持鼠标的操作顺畅,我们的游戏完全可以学习Windows的做法来让我们的游戏鼠标操作更为方便。

  我们先来说说Windows实现鼠标的做法,按照我的观察,Windows的鼠标应该是通过多线程来实现,而且鼠标线程的优先级非常之高,并且鼠标在屏幕上是使用局部更新,知道了原理,我们就可以开始动手实现我们的游戏鼠标了。首先,我们必须创建出我们可爱的鼠标线程,让我们的鼠标拥有较高的优先级,这样才能最大限度让鼠标灵活,参考如下代码:

  // 我们的鼠标线程

  DWORD WINAPI ThreadProc( LPVOID lpParameter )

  {

    ...  // 实现鼠标更新的部分代码

  }

  // 创建我们鼠标线程,详细请参考多线程编程文章

  thread = CreateThread(NULL, 0, ThreadProc, 0, 0, &g_dwMouseThread);

  // 提高线程的优先级

  SetThreadPriority(thread, THREAD_PRIORITY_TIME_CRITICAL);

  这样,我们的线程就创建完毕了,接下来我们来看看如何进行屏幕的局部更新,我们的游戏(2D)一般都是运行在DirectDraw的环境下,要进行屏幕的局部更新我们需要对主表面进行直接操作,可能你会想到,万一我们在进行鼠标更新的同时,游戏主线程也在对主表面进行如页面翻转等操作,一但这样,这将产生无法预料的结果,为了避免这种情况的产生,我们还必须进行双线程的同步处理,说了那么多废话,来看看我们如何实现:

  // 用来标记鼠标进程是否进行

  bool g_bMouseThreadRun = true;

  // 用来处理同步,此类属于MFC线程处理部分,具体请参考MSDN

  CCriticalSection critsection;

  // 鼠标处理实现线程部分

  DWORD WINAPI ThreadProc( LPVOID lpParameter )

  {

    // 进行鼠标线程的内部循环处理

    while(g_bMouseThreadRun)

    {

      // 为了能进一步节省鼠标线程所消耗的系统资源

      // 我们使用了DirectInput的鼠标事件响应操作

      // 具体实现参看DirectInput的鼠标处理部分

      DWORD dwResult = WaitForSingleObject(g_hMouseEvent, INFINITE);

      if(dwResult == WAIT_OBJECT_0) // 有鼠标事件发生

      {

        // 重新获得鼠标的位置信息

        // 此处非常重要,用来作为与屏幕刷新的同步处理

        // 这里将线程锁定,使主线程无法进行屏幕刷新操作

        critsection.Lock();

        if(检测鼠标位置是否有更新)

        {

          // 恢复原鼠标位置的图象 bakbuffer <- save back image

          // 保存鼠标将绘制部分的背景图象 save back image -> bakbuffer

          // 在新位置绘制鼠标 draw mouse image

        }

        // 释放线程

        critsection.Unlock();

      }

    }

  }

  完成了我们的鼠标线程,接下来我们来设计我们的屏幕刷新部分,此处要注意两个问题,一个是与鼠标进程一样的同步问题,还有就是实现对局部更新时的背景图象保存缓冲的更新,一但屏幕刷新,背景难免会产生变化,那此时我们在鼠标线程中所保存的局部图象数据(bakbuffer)将是无效错误过时的数据,所以,我们必须对局部图象保存数据进行与背表面(BackSurface)进行匹配处理,并且将鼠标图象绘制到屏幕上,参看如下:

  // 屏幕刷新函数

  HRESULT Present()

  {

    // 同上,为了满足同步需要

    // 如果有其它线程调用了Lock(),那此处将处于等待状态

    // 直到其它线程Unlock(),此函数才将返回。

    critsection.Lock();

    // 对鼠标线程所保存的局部图象数据(bakbuffer)

    // 进行与背景的匹配操作 save BackSurface image -> bakbuffer

    // 绘制鼠标到屏幕上,防止鼠标被背景覆盖

    ... // 屏幕刷新部分

    // 恢复线程锁定

    critsection.Unlock();

  }

  关于线程的释放,这里我们只需要简单的将g_bMouseThreadRun这个全局变量设置为false,这样线程就能自动退出,不过注意,为了防止鼠标线程还未退出,主线程已经将部分关键数据释放造成错误,最好能让主线程停止一会,以便鼠标线程的正确退出。

  最后,我们来谈谈这种方法的利弊,优点很明显,可以让鼠标操作更为顺畅,缺点,使编程复杂化,而且多少会影响些主线程的性能。说了那么多,有兴趣的朋友可以去下载我的HoHo游戏引擎,里面有全部原代码,还有附带的实现例子。

全看分页树展 · 主题 跟帖


有趣有益,互惠互利;开阔视野,博采众长。
虚拟的网络,真实的人。天南地北客,相逢皆朋友

Copyright © cchere 西西河