1. 程式人生 > >《MFC遊戲開發》筆記五 定時器和簡單動畫

《MFC遊戲開發》筆記五 定時器和簡單動畫

本系列文章由七十一霧央編寫,轉載請註明出處。


      上一節筆記中,我們講解了鍵盤響應和滑鼠響應,實現了對於玩家的操作,程式做出正確的響應。但是大家在玩遊戲的過程中,應該會注意到,在大家沒有操作的時候,程式的畫面仍然不是靜止的,比如NPC會四處走動,怪物仍然會跑過來攻擊玩家等,也就是說,畫面仍然在隨著時間不斷的更新。這一點在程式中對應的就是無時無刻不在更新著遊戲資訊和繪製畫面,以便及時的反映出遊戲的狀態。

      在WIN 32程式中,一般大家會把繪製程式放到訊息迴圈之中,但是在MFC中由於對WIN 32高度的封裝和訊息印射機制,我們很難找到訊息迴圈的位置,所以我們就需要採用別的辦法。大家現在已經知道,我們繪圖都是在OnPaint裡進行的,那麼我們不斷的執行OnPaint函式不就行了嗎?實現的方法就是今天要講解的定時器了。

       定時器(Timer)物件可以每隔一段時間發出一個時間訊息,程式一旦接收到此訊息之後,便可以決定接下來要做哪些事情。霧央先說一下定時器大概會有5毫秒左右的誤差,精度不夠,在實際遊戲開發中,很少使用到,但是對於我們初學者來說,這個對於遊戲性幾乎沒有任何影響,還很方便大家的開發,所以我們仍然使用了定時器。

      下面來介紹如何建立與刪除定時器。

      1.建立定時器

      Windows API 的SetTimer()函式可為視窗建立一個定時器,並每隔一段時間就發出WM_TIMER訊息,此函式的定義是

  1. UINT_PTRSetTimer(  
  2. UINT_PTRnIDEvent, //定時器代號
  3. UINTuElapse, //時間間隔
  4. TIMERPROClpTimerFunc //處理函式
  5. );  

      SetTimer()函式的第1個引數是定時器的代號,這個代號在同一個視窗中必須是唯一的,且值不為0,第2個引數則是定時器發出WM_TIMER訊息的時間間隔:第3個參則用於設定由系統呼叫處理WM_TIMER訊息的相應函式,如果不用響應函式處理WM_TIMER訊息,則此引數應設為NULL。

      一句話概括,就是SetTimer函式會建立一個ID為第一個引數的定時器,它每隔第二個引數的時間就會執行一次第三個引數指向的函式。

      如果不需要自己定義處理函式,第三個引數設定為NULL,我們可以使用預設的訊息處理函式。

      下面是設定一個每隔100毫秒發出WM_TIMER訊息的定時器的程式程式碼。

  1. SetTimer(1,100,NULL);  

      2.刪除定時器

      定時器建立後,就會一直自動地按照定義設定的時間間隔發出WM_TIMER訊息,如果要停用某個定時器,必須使用下面的這個函式:

  1. BOOL   KillTimer(int 定時器代號);  

      在MFC中,大家要使用定時器,需要先通過類嚮導新增“WM_TIMER”訊息,新增的具體過程如果有不會的同學請閱讀上一節筆記:滑鼠響應和鍵盤響應。

       在新增完定時器訊息後,CChildView.cpp中會出現

  1. void CChildView::OnTimer(UINT_PTR nIDEvent)  

       這個函式,這就是定時器訊息處理函數了,它的引數nIDEvent就是表示執行OnTimer函式的定時器的ID了。

       霧央要強調一下關於建立定時器的位置,大家基本可以在任何地方建立,比如在OnPaint中等,但是千萬不要在PreCreateWindow函式中建立定時器,否則大家就會發現程式一執行就會彈出來一個出錯框了。

       如果大家希望在視窗一建立的時候就建立定時器,比如驅動我們視窗繪製的定時器等,那麼我們可以新增“WM_CREATE”訊息,在這裡面進行建立。

       在示例程式中我們要實現的是按下T鍵人物自動向右移動,按下I鍵定值移動。大家如果自己執行一下程式,就會感覺這有幾分動畫的影子了。事實上,如果讓人物移動的時候,變化一下圖片,比如幾張跑動的圖片不斷的切換,那麼就是一個真正意義上的動畫了。

       另外,霧央有一個感到非常抱歉的事情要和大家說明一下,在之前的程式碼中,霧央漏掉了一句很重要的程式碼,在OnPaint函式中釋放DC即ReleaseDC之前要加上ValidateRect(&m_client);這個函式的作用是使繪圖區變得有效。在windows中,如果我們的視窗被遮擋了什麼的,視窗那部分就變得無效,就會產生WM_PAINT訊息,當繪製完畢後,必須要使視窗變得有效,否則系統將周而復始的產生WM_PAINT訊息,使得CPU佔用率非常高,而且還會出現很多莫名其妙的問題,比如使用MessageBox會導致程式失去響應等。

下面貼程式碼

標頭檔案

  1. // ChildView.h : CChildView 類的介面
  2. //
  3. #pragma once
  4. // CChildView 視窗
  5. class CChildView : public CWnd  
  6. {  
  7. // 構造
  8. public:  
  9.     CChildView();  
  10. // 特性
  11. public:  
  12.     CRect m_client;    //儲存客戶區大小
  13.     CRect m_heroPos;    //儲存英雄的位置
  14.     CImage m_hero;   //英雄
  15.     CImage m_bg;      //背景圖片
  16. // 操作
  17. public:  
  18. // 重寫
  19.     protected:  
  20.     virtualBOOL PreCreateWindow(CREATESTRUCT& cs);  
  21. // 實現
  22. public:  
  23.     virtual ~CChildView();  
  24.     // 生成的訊息對映函式
  25. protected:  
  26.     afx_msg void OnPaint();  
  27.     DECLARE_MESSAGE_MAP()  
  28. public:  
  29.     afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);  
  30.     afx_msg void OnLButtonDown(UINT nFlags, CPoint point);  
  31.     afx_msg void OnTimer(UINT_PTR nIDEvent);  
  32.     afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);  
  33. };  

CPP檔案
  1. // ChildView.cpp : CChildView 類的實現
  2. //
  3. #include "stdafx.h"
  4. #include "GameMFC.h"
  5. #include "ChildView.h"
  6. #ifdef _DEBUG
  7. #define new DEBUG_NEW
  8. #endif
  9. //定時器的名稱用巨集比較清楚
  10. #define TIMER_PAINT 1
  11. #define TIMER_HEROMOVE 2
  12. // CChildView
  13. CChildView::CChildView()  
  14. {  
  15. }  
  16. CChildView::~CChildView()  
  17. {  
  18. }  
  19. BEGIN_MESSAGE_MAP(CChildView, CWnd)  
  20.     ON_WM_PAINT()  
  21.     ON_WM_KEYDOWN()  
  22.     ON_WM_LBUTTONDOWN()  
  23.     ON_WM_TIMER()  
  24.     ON_WM_CREATE()  
  25. END_MESSAGE_MAP()  
  26. //將png貼圖透明
  27. void TransparentPNG(CImage *png)  
  28. {  
  29.     for(int i = 0; i <png->GetWidth(); i++)  
  30.     {  
  31.         for(int j = 0; j <png->GetHeight(); j++)  
  32.         {  
  33.             unsigned char* pucColor = reinterpret_cast<unsigned char *>(png->GetPixelAddress(i , j));  
  34.             pucColor[0] = pucColor[0] * pucColor[3] / 255;  
  35.             pucColor[1] = pucColor[1] * pucColor[3] / 255;  
  36.             pucColor[2] = pucColor[2] * pucColor[3] / 255;  
  37.         }  
  38.     }  
  39. }  
  40. // CChildView 訊息處理程式
  41. BOOL CChildView::PreCreateWindow(CREATESTRUCT& cs)   
  42. {  
  43.     if (!CWnd::PreCreateWindow(cs))  
  44.         return FALSE;  
  45.     cs.dwExStyle |= WS_EX_CLIENTEDGE;  
  46.     cs.style &= ~WS_BORDER;  
  47.     cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS,   
  48.         ::LoadCursor(NULL, IDC_ARROW), reinterpret_cast<HBRUSH>(COLOR_WINDOW+1), NULL);  
  49.     //載入背景
  50.     m_bg.Load("bg.png");  
  51.     //載入英雄圖片
  52.     m_hero.Load("hero.png");  
  53.     TransparentPNG(&m_hero);  
  54.     //設定英雄初始位置
  55.     m_heroPos.left=100;    //人物左邊貼在100的位置
  56.     m_heroPos.right=100+60; //人物的右邊等於左邊加上人物的寬度
  57.     m_heroPos.top=400;  
  58.     m_heroPos.bottom=400+60;  
  59.     return TRUE;  
  60. }  
  61. void CChildView::OnPaint()   
  62. {  
  63.     //獲取視窗DC指標
  64.     CDC *cDC=this->GetDC();  
  65.     //獲取視窗大小
  66.     GetClientRect(&m_client);  
  67.     //貼背景
  68.     m_bg.Draw(*cDC,m_client);  
  69.     //貼英雄
  70.     m_hero.Draw(*cDC,m_heroPos);  
  71.     //在繪製完圖後,使視窗區有效
  72.     ValidateRect(&m_client);  
  73.     //釋放DC
  74.     ReleaseDC(cDC);  
  75. }  
  76. //按鍵響應函式
  77. void CChildView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)  
  78. {  
  79.     //nChar表示按下的鍵值
  80.     switch(nChar)  
  81.     {  
  82.     case'd':         //遊戲中按下的鍵當然應該不區分大小寫了
  83.     case'D':  
  84.         m_heroPos.left+=10;    //向右移動10個畫素的單位
  85.         m_heroPos.right+=10;   //左邊和右邊都要移動哦
  86.         break;  
  87.     case'a':  
  88.     case'A':  
  89.         m_heroPos.left-=10;  
  90.         m_heroPos.right-=10;  
  91.         break;  
  92.     case'w':  
  93.     case'W':  
  94.         m_heroPos.top-=10;  
  95.         m_heroPos.bottom-=10;  
  96.         break;  
  97.     case's':  
  98.     case'S':  
  99.         m_heroPos.top+=10;  
  100.         m_heroPos.bottom+=10;  
  101.         break;  
  102.     case't':  
  103.     case

    相關推薦

    MFC遊戲開發筆記 定時簡單動畫

    本系列文章由七十一霧央編寫,轉載請註明出處。       上一節筆記中,我們講解了鍵盤響應和滑鼠響應,實現了對於玩家的操作,程式做出正確的響應。但是大家在玩遊戲的過程中,應該會注意到,在大家沒有操作的時候,程式的畫面仍然不是靜止的,比如NPC會四處走動,怪物仍然會跑過來攻擊玩家等,也就是說,畫面

    egret遊戲開發筆記(新增特效釋放特效)

    新增特效: this.effPart=EffectMgr.addEff("eff/962", this.group_1, -this.img_0.width / 2+11, this.img_0.hei

    (筆記)Linux內核學習(八)之定時時間管理

    全局變量 define 結構 load 統計 object 一個 完成 溢出 一 內核中的時間觀念 內核在硬件的幫助下計算和管理時間。硬件為內核提供一個系統定時器用以計算流逝的時間。系 統定時器以某種頻率自行觸發,產生時鐘中斷,進入內核時鐘中斷處理程序中進行

    遊戲開發定時迴圈播放動畫

    //Windows視窗標頭檔案 #include <windows.h> //swprintf_s函式包含的標頭檔案 #include <tchar.h> //PlaySound函式包含的庫檔案 #pragma comment(lib,"winmm.lib") /

    co_routine.cpp/.h/inner.h(第四部分:定時事件迴圈)—— libco原始碼分析、學習筆記

    由於本原始碼蠻長的,所以按照功能劃分模組來分析,分為若干部分,詳見二級目錄↑ 定時器和事件迴圈 libco管理定時事件便是使用時間輪這種資料結構 定時器前驅知識本篇只稍微提一下,具體知識請參考《Linux高效能伺服器程式設計 遊雙 著》第11章,或其他方式學

    linux核心分析筆記----定時時間管理

    在這一次裡,主要講講和時間相關的東西,這個我們都比較熟悉,我就直接如主題。       首先要明白兩個概念:系統定時器和動態定時器。週期性產生的事件都是有系統定時器驅動的,這裡的系統定時器是一種可程式設計硬體晶片,它能以固定頻率產生中斷。該中斷就是定時器中斷

    讀書筆記(十一)-定時時間管理

    系統中有很多與時間相關的程式(比如定期執行的任務,某一時間執行的任務,推遲一段時間執行的任務),因此,時間的管理對於linux來說非常重要. 主要內容: 系統時間 定時器 定時器相關概念 定時器執行流程 實現程式延遲的方法 定時器和延遲的例子 1.系

    【轉】【UNITY3D 遊戲開發】Google-protobuf與FlatBuffers資料的序列化反序列化

    ★protobuf有啥缺陷?前幾天剛剛在“光環效應 ”的帖子裡強調了“要同時評估優點和缺點”。所以俺最後再來批判一下這玩意兒的缺點。◇應用 不夠廣由於protobuf剛公佈沒多久,相比XML而言,protobuf還屬於初出茅廬。因此,在知名度、應用廣度等方面都遠不如XML。由於這個原因,假如你設計的系統需要提

    遊戲開發筆記)——服務端系統分層

            本來很想按順序寫下來,到了第五篇是打算先寫架構的,無奈這東西暫時沒辦法弄的比較通透,拖了很久也還是覺得寫起來有困難。有一個客觀因素是這陣子有點忙,很多東西要做,也沒辦法留出許多時間用來學習。         還是先寫已經有點概念的東西吧.... 關於架構:

    【UNITY3D 遊戲開發】Google-protobuf與FlatBuffers資料的序列化反序列化

     關於Protobuf 通過本文的轉載和分享的相關連結,足夠了解使用了,所以這裡不贅述了。但是這裡Himi順便提一下“FlatBuffers” ,它是 Protocol Buffers升級版,其主要區別在於FlatBuffers在訪問資料前不需要解析/拆包這一步。

    我的遊戲開發筆記):Animator的運用二

    好了好了,上篇說到如何用程式碼來控制Animator裡的條件引數,話不多說,直接開始。 Animator m_Animator;m_Animator = GetComponent<Animator>(); m_Animator.SetBool("OnGround

    Cocos2d-x遊戲開發之lua編輯 Sublime 搭建,集成cocos2dLuaApi自有類

    系統 下一個 下載 tail mil 命令 term 小寫 一次 版權聲明:本文為博主原創文章。未經博主同意不得轉載。 https://blog.csdn.net/wisdom605768

    Python學習筆記——叠代生成器

    返回對象 node manual 通過 line 計數 bject repr 對象 1、手動遍歷叠代器   使用next函數,並捕獲StopIteration異常。 def manual_iter(): with open(‘./test.py‘) as f:

    java學習筆記定時

    blog div this rgs date row demo sdf 時間 定時器 1 package pack01_timer; 2 3 import java.io.File; 4 import java.text.ParseException; 5 i

    python學習筆記():裝飾、生成器、內置函數、json

    知識 我們 數列 ext 返回 utf choice 斐波拉契數列 不同 一、裝飾器 裝飾器,這個器就是函數的意思,連起來,就是裝飾函數,裝飾器本身也是一個函數,它的作用是用來給其他函數添加新功能,比如說,我以前寫了很多代碼,系統已經上線了,但是性能比較不好,現在想把程序裏

    微信小遊戲開發筆記

    由於 新產品 名稱 creat gpo 思考 1.8 rep 開發者 2017年12月28日,微信更新的 6.6.1 版本開放了微信小遊戲,小遊戲作為一款新產品迅速走向開發者視野,為趕上最新IT潮流,決定通過一款簡單的微信小遊戲來學習小遊戲的基本開發。 由於從未接觸過遊戲開

    JSP學習筆記之applicationpage物件

    這篇部落格將9大物件的剩下的物件部分全部講掉。 E、Application內建物件 Application物件直接包裝了servlet的ServletContext類的物件,是javax.servlet.ServletContext 類的例項。這個物件在JSP頁面的整個生命週

    IOS開發中NSTimer定時的使用

    第一步:呼叫函式 [self getCircleBackGround:self.blcView.btChartView.mCircleView getCurrentLb:self.blcView.btChartView.mCurrent ]; 第二步:方法的實現 -(voi

    讀Muduo原始碼筆記---8(定時)

    muduo的定時器由三個類實現,TimerId、Timer、TimerQueue。 1、採用timer_create函式得到定時器物件       timerfd_create把時間變成了一個檔案描述符,該“檔案”在定時器超時的那一刻變得可讀,這樣就能很方便的融

    Edit on GitHub Node.js 事件迴圈,定時 process.nextTick()

    Node.js 事件迴圈,定時器和 process.nextTick() 什麼是事件輪詢 事件迴圈是 Node.js 處理非阻塞 I/O 操作的機制——儘管 JavaScript 是單執行緒處理的——當有可能的時候,它們會把操作轉移到系統核心中去。 既然目前大多數核心都是多執行