genericthread.cpp
来自「windows ce开发技巧与实例光盘代码」· C++ 代码 · 共 470 行
CPP
470 行
// GenericThread.cpp: implementation of the CGenericThread class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "GenericThread.h"
#include <math.h>
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CGenericThread::CGenericThread()
{
m_bFirstGfxUpdate = TRUE;
m_bRequestSaveScreen = FALSE;
m_dwSpriteCount = 0;
m_dwSpriteIndex = 0;
m_dwSpriteWidth = 0;
m_dwSpriteHeight = 0;
m_bIsRunning = FALSE;
m_bThreadIsFinished = TRUE;
m_bSuspendThread = FALSE;
m_hGameThread = 0;
m_dwScreenOrientation = GDDISPMODE_LANDSCAPE1;
}
CGenericThread::~CGenericThread()
{
KillThread();
}
void CGenericThread::KillThread()
{
m_bIsRunning = FALSE;
if (m_hGameThread)
{
// Allow Win32 to clean up thread resources asynchronously
::WaitForSingleObject(m_hGameThread, INFINITE);
Sleep(100);
::CloseHandle(m_hGameThread);
m_hGameThread = 0;
}
}
BOOL CGenericThread::StartThread(HWND hParentFrame)
{
// Set display mode
m_display.SetDisplayMode(m_dwScreenOrientation);
// Open display
HRESULT hr = 0;
if (GAMEPARAM_FULLSCREEN)
{
hr = m_display.OpenDisplay(hParentFrame, GDOPENDISPLAY_FULLSCREEN | GDOPENDISPLAY_SIZEX2);
}
else
{
hr = m_display.OpenDisplay(hParentFrame, GDOPENDISPLAY_SCALEX2);
}
if (FAILED(hr))
{
::PostMessage(hParentFrame, WM_CLOSE, 0, 0);
return FALSE;
}
m_bIsRunning = TRUE;
m_bThreadIsFinished = FALSE;
m_bSuspendThread = FALSE;
// Start thread - send a pointer to this class as a parameter
DWORD dwThreadID;
m_hGameThread = ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) CGenericThread::ThreadFunc, (void*)this, CREATE_SUSPENDED, &dwThreadID);
if (m_hGameThread == NULL)
{
DWORD dw = ::GetLastError();
m_bIsRunning = FALSE;
m_bThreadIsFinished = TRUE;
m_bSuspendThread = TRUE;
return FALSE;
}
::SetThreadPriority(m_hGameThread, THREAD_PRIORITY_ABOVE_NORMAL);
::ResumeThread(m_hGameThread);
return TRUE;
}
void CGenericThread::SuspendThread()
{
m_bSuspendThread = TRUE;
Sleep(1000);
}
void CGenericThread::ResumeThread()
{
m_bSuspendThread = FALSE;
}
BOOL CGenericThread::IsRunning()
{
return m_bIsRunning;
}
BOOL CGenericThread::IsFinished()
{
return m_bThreadIsFinished;
}
unsigned __stdcall CGenericThread::ThreadFunc(LPVOID pParam)
{
CGenericThread* pGenericThreadClass = (CGenericThread*) pParam;
while (pGenericThreadClass->IsRunning())
{
pGenericThreadClass->ThreadCallback();
while (pGenericThreadClass->IsRunning() && pGenericThreadClass->m_bSuspendThread)
{
Sleep(100);
}
}
pGenericThreadClass->m_bThreadIsFinished = TRUE;
return 0;
}
void CGenericThread::ThreadCallback()
{
DWORD dwScreenWidth = m_display.GetWidth();
DWORD dwScreenHeight = m_display.GetHeight();
// Check if we should save back surface
if (m_bRequestSaveScreen)
{
m_backbuffer.SaveSurface(TEXT("backbuffer.bmp"));
m_bRequestSaveScreen = FALSE;
}
if (m_bFirstGfxUpdate)
{
// Always sleep when starting thread or changing display mode! Why?
// (1) On Windows 2000/XP, high priority threads initialize asynchronously
// (2) On Windows CE, the input buffer for keyboard/pen might be buffered
Sleep(100);
m_dwSpriteCount = 0;
m_display.SetDisplayMode(m_dwScreenOrientation);
// Get back buffer
m_display.GetBackBuffer(&m_backbuffer);
// BEGIN - IF DISPLAYMODE HAS CHANGED ______________________________
// If display mode has changed, update dwScreenWidth and dwScreenHeight
dwScreenWidth = m_display.GetWidth();
dwScreenHeight = m_display.GetHeight();
m_coordlist.RemoveAll();
m_velocitylist.RemoveAll();
m_logoindex.RemoveAll();
m_backgroundOffset.x = 0;
m_backgroundOffset.y = 0;
m_backgroundDirection.x = 0;
m_backgroundDirection.y = 0;
// END - IF DISPLAYMODE HAS CHANGED ________________________________
CPoint koord;
CPoint dir;
WORD nLogoIndex;
// Create intro buffer
m_introbuffer.CreateSurface(0, dwScreenWidth, dwScreenHeight);
m_introbuffer.FillRect(NULL, RGB(60, 60, 150), 0, NULL);
long dwXOffset, dwYOffset;
CString welcome = TEXT("Creating background buffer, please wait...");
dwXOffset = (dwScreenWidth >> 1);
dwYOffset = (dwScreenHeight >> 1);
m_introbuffer.DrawText(dwXOffset, dwYOffset, welcome, RGB(255, 255, 255), GDDRAWTEXT_BORDER | GDDRAWTEXT_CENTER, NULL, NULL);
m_introbuffer.DrawRect(NULL, RGB(255, 255, 255), 0, NULL);
dwYOffset += 10;
// Move in...
for (int n=0; n<=(int)(dwScreenHeight); n+=4)
{
m_backbuffer.BltFast(0, 0, &m_introbuffer, CRect(0, m_introbuffer.GetHeight()-n, m_introbuffer.GetWidth(), m_introbuffer.GetHeight()), 0, NULL);
if (m_display.Flip() == GDERR_BACKBUFFERLOST)
m_display.GetBackBuffer(&m_backbuffer);
}
// Create bitmap surfaces
HINSTANCE hInst = AfxGetInstanceHandle();
const COLORREF TRANSPARENTCOLOR = RGB(255, 0, 255);
m_spritelist.CreateSurface(hInst, IDB_SPRITELIST, TEXT("PNG"));
m_plant1.CreateSurface(hInst, IDB_PLANT1, TEXT("PNG"));
m_plant2.CreateSurface(hInst, IDB_PLANT2, TEXT("PNG"));
m_spritelist.SetColorKey(TRANSPARENTCOLOR);
m_plant1.SetColorKey(TRANSPARENTCOLOR);
m_plant2.SetColorKey(TRANSPARENTCOLOR);
// Get Sprite size
m_dwSpriteWidth = m_spritelist.GetWidth();
m_dwSpriteHeight = m_spritelist.GetHeight() / SPRITE_ANIMATION_FRAMES;
// Create sprite coordinate list
float nDir;
for (int i=0; i<(SPRITECOUNT_MAX+1); i++)
{
koord.x = (DWORD) (2+ rand() * (dwScreenWidth - m_dwSpriteWidth - 2) / RAND_MAX);
koord.y = (DWORD) (2+ rand() * (dwScreenHeight - m_dwSpriteHeight - 2) / RAND_MAX);
nLogoIndex = (WORD) (rand() * (SPRITE_ANIMATION_FRAMES - 1) / RAND_MAX);
nDir = 2 * (float)rand() / (float)RAND_MAX;
dir.x = (nDir > 0.5) ? 2 : -2;
nDir = 2 * (float)rand() / (float)RAND_MAX;
dir.y = (nDir > 0.5) ? 2 : -2;
m_coordlist.AddTail(koord);
m_logoindex.AddTail(nLogoIndex);
m_velocitylist.AddTail(dir);
}
// Set background velocity
m_backgroundDirection.x = 1;
m_backgroundDirection.y = 1;
// Allocate buffer for background area
m_background.CreateSurface(0, BACKGROUND_WIDTH, BACKGROUND_HEIGHT);
m_background.FillRect(NULL, RGB(83, 129, 80), 0, NULL);
// Check to see if we can use direct display access to update the progress bar
DWORD dwDispCaps;
BOOL bDirectDisplay = FALSE;
if (m_display.GetDisplayCaps(&dwDispCaps) == GD_OK)
{
bDirectDisplay = ((dwDispCaps & GDDISPCAPS_DIRECT) == GDDISPCAPS_DIRECT);
}
// Plants
DWORD plantX, plantY;
FLOAT nStepOffset = ((FLOAT)(dwScreenWidth-1)/(FLOAT)PLANTCOUNT);
for (n=0; n<PLANTCOUNT; n++)
{
// Place random plants on the background, alter between plant1 and 2
// Plant1
plantX = (DWORD) (2 + (m_background.GetWidth() - m_plant1.GetWidth() - 2) * rand() / RAND_MAX);
plantY = (DWORD) (2 + (m_background.GetHeight() - (m_plant1.GetHeight()>>1) - 2) * rand() / RAND_MAX);
m_background.AlphaBlt(CRect(plantX, plantY, plantX+m_plant1.GetWidth(), plantY+(m_plant1.GetHeight()>>1)), &m_plant1, NULL, &m_plant1, CRect(0, (m_plant1.GetHeight()>>1), m_plant1.GetWidth(), m_plant1.GetHeight()), 0, NULL);
// Plant2
plantX = (DWORD) (2 + (m_background.GetWidth() - m_plant2.GetWidth() - 2) * rand() / RAND_MAX);
plantY = (DWORD) (2 + (m_background.GetHeight() - (m_plant2.GetHeight()>>1) - 2) * rand() / RAND_MAX);
m_background.AlphaBlt(CRect(plantX, plantY, plantX+m_plant2.GetWidth(), plantY+(m_plant2.GetHeight()>>1)), &m_plant2, NULL, &m_plant2, CRect(0, (m_plant2.GetHeight()>>1), m_plant2.GetWidth(), m_plant2.GetHeight()), 0, NULL);
m_introbuffer.FillRect(CRect((DWORD)nStepOffset+1, dwYOffset, (DWORD)(nStepOffset*n), dwYOffset+2), RGB(255, 255, 255), 0, NULL);
if (bDirectDisplay)
{
// No need to "flip" since we can write directly to the screen area
m_display.FillRect(CRect((DWORD)nStepOffset+1, dwYOffset, (DWORD)(nStepOffset*n), dwYOffset+2), RGB(255, 255, 255), 0, NULL);
}
else
{
m_backbuffer.FillRect(CRect((DWORD)nStepOffset+1, dwYOffset, (DWORD)(nStepOffset*n), dwYOffset+2), RGB(255, 255, 255), 0, NULL);
if (m_display.Flip() == GDERR_BACKBUFFERLOST)
m_display.GetBackBuffer(&m_backbuffer);
}
}
// Move out
for (n=0; n<=(int)(dwScreenHeight); n+=4)
{
m_backbuffer.BltFast(0, n, &m_introbuffer, CRect(0, 0, dwScreenWidth, dwScreenHeight-n), 0, NULL);
m_backbuffer.FillRect(CRect(0, 0, dwScreenWidth, n), RGB(0, 0, 0), 0, NULL);
if (m_display.Flip() == GDERR_BACKBUFFERLOST)
m_display.GetBackBuffer(&m_backbuffer);
}
// Start timer
m_timer.StartTimer(GAMEPARAM_TARGETFPS);
m_bFirstGfxUpdate = FALSE;
}
CPoint koord;
CPoint dir;
long nLogoIndex;
for (int i=0; i<(int)m_dwSpriteCount; i++)
{
koord = m_coordlist.GetAt(m_coordlist.FindIndex(i));
dir = m_velocitylist.GetAt(m_velocitylist.FindIndex(i));
nLogoIndex = m_logoindex.GetAt(m_logoindex.FindIndex(i));
// Update koordinate
koord.x += dir.x;
koord.y += dir.y;
// Update direction X
if (koord.x < 2)
dir.x = 2;
else if (koord.x > (long)(dwScreenWidth - m_dwSpriteWidth - 2))
dir.x = -2;
// Update direction Y
if (koord.y < 2)
dir.y = 2;
else if (koord.y > (long)(dwScreenHeight - m_dwSpriteHeight - 2))
dir.y = -2;
// Update logo index
if ((dir.x * dir.y) < 0)
nLogoIndex++;
else
nLogoIndex--;
if (nLogoIndex < 0)
nLogoIndex = (SPRITE_ANIMATION_FRAMES-1);
if (nLogoIndex > (SPRITE_ANIMATION_FRAMES-1))
nLogoIndex = 0;
m_coordlist.SetAt(m_coordlist.FindIndex(i), koord);
m_velocitylist.SetAt(m_velocitylist.FindIndex(i), dir);
m_logoindex.SetAt(m_logoindex.FindIndex(i), nLogoIndex);
}
m_backgroundOffset.x += m_backgroundDirection.x;
m_backgroundOffset.y += m_backgroundDirection.y;
// Update direction X
if (m_backgroundOffset.x < 1)
m_backgroundDirection.x = 1;
else if (m_backgroundOffset.x > (long)(m_background.GetWidth() - dwScreenWidth - 1))
m_backgroundDirection.x = -1;
// Update direction Y
if (m_backgroundOffset.y < 1)
m_backgroundDirection.y = 1;
else if (m_backgroundOffset.y > (long)(m_background.GetHeight() - dwScreenHeight - 1))
m_backgroundDirection.y = -1;
// Clean up background after last pass
m_backbuffer.BltFast(0, 0, &m_background, CRect(m_backgroundOffset.x, m_backgroundOffset.y, m_backgroundOffset.x+dwScreenWidth, m_backgroundOffset.y+dwScreenHeight), 0, NULL);
// Draw sprites
for (i=0; i<(int)m_dwSpriteCount; i++)
{
koord = m_coordlist.GetAt(m_coordlist.FindIndex(i));
nLogoIndex = m_logoindex.GetAt(m_logoindex.FindIndex(i));
m_backbuffer.BltFast(koord.x, koord.y, &m_spritelist, CRect(0, nLogoIndex * m_dwSpriteHeight, m_dwSpriteWidth, nLogoIndex * m_dwSpriteHeight+m_dwSpriteHeight), GDBLTFAST_KEYSRC, NULL);
}
CString str;
str.Format(TEXT("Spritecount: [%d]"), m_dwSpriteCount);
DrawFrameInfo(dwScreenHeight - 60, str, RGB(50, 130, 60));
FLOAT nActualFrameTime = 0;
m_timer.GetActualFrameTime(&nActualFrameTime);
str.Format(TEXT("Current frame: [%.3f ms]"), nActualFrameTime);
DrawFrameInfo(dwScreenHeight - 40, str, RGB(170, 90, 60));
FLOAT nFrameRate = 0;
m_timer.GetActualFrameRate(&nFrameRate);
str.Format(TEXT("Current framerate: [%.3f fps]"), nFrameRate);
DrawFrameInfo(dwScreenHeight - 20, str, RGB(90, 60, 170));
// Draw logo text
CString str1, str2, str3;
str1 = TEXT("[1] = Quit");
str2 = TEXT("-www.gapidraw.com-");
str3 = TEXT("[2] = Rotate");
GDFILLRECTFX fillrectfx;
fillrectfx.dwOpacity = 128;
m_backbuffer.FillRect(CRect(0, 0, dwScreenWidth, 10), RGB(0, 0, 0), GDFILLRECT_OPACITY, &fillrectfx);
m_backbuffer.DrawText(2, 2, str1, RGB(255, 255, 255), GDDRAWTEXT_BORDER | GDDRAWTEXT_LEFT, 0, NULL);
m_backbuffer.DrawText(dwScreenWidth/2, 2, str2, RGB(255, 255, 255), GDDRAWTEXT_BORDER | GDDRAWTEXT_CENTER, 0, NULL);
m_backbuffer.DrawText(dwScreenWidth-3, 2, str3, RGB(255, 255, 255), GDDRAWTEXT_BORDER | GDDRAWTEXT_RIGHT, 0, NULL);
m_backbuffer.DrawLine(0, 10, dwScreenWidth-1, 10, RGB(255, 255, 255), 0, NULL);
// FLIP BUFFERS AND WAIT FOR NEXT FRAME _________________
if (m_display.Flip() == GDERR_BACKBUFFERLOST)
{
m_display.GetBackBuffer(&m_backbuffer);
}
BOOL bAddSprite = TRUE;
BOOL bOldDevice = FALSE;
if (m_timer.WaitForNextFrame() == GDERR_FRAMETIMEOVERFLOW)
{
// The computer cannot keep up with the frame rate.
// Decrease the number of sprites, sleep 1 ms and try again...
if (m_dwSpriteCount <= SPRITECOUNT_MIN)
bOldDevice = TRUE;
else
bAddSprite = FALSE;
Sleep(1);
}
// ______________________________________________________
// Always decrease if we are exceeding frame target time,
// unless we use an older device which cannot even display SPRITECOUNT_MIN
if ((bAddSprite == FALSE) || (++m_dwSpriteIndex > 5))
{
m_dwSpriteIndex = 0;
if (bOldDevice)
{
m_dwSpriteCount += (m_dwSpriteCount < SPRITECOUNT_MIN) ? 1 : 0;
}
else
{
if (bAddSprite)
m_dwSpriteCount += (m_dwSpriteCount < SPRITECOUNT_MAX) ? 1 : 0;
else
m_dwSpriteCount -= (m_dwSpriteCount > 0) ? 1 : 0;
}
}
}
void CGenericThread::SaveScreenshot()
{
m_bRequestSaveScreen = TRUE;
}
void CGenericThread::ChangeScreenOrientation()
{
if (!m_bFirstGfxUpdate)
{
switch (m_dwScreenOrientation)
{
case GDDISPMODE_NORMAL:
m_dwScreenOrientation = GDDISPMODE_LANDSCAPE1;
break;
case GDDISPMODE_LANDSCAPE1:
m_dwScreenOrientation = GDDISPMODE_LANDSCAPE2;
break;
case GDDISPMODE_LANDSCAPE2:
m_dwScreenOrientation = GDDISPMODE_NORMAL;
break;
}
m_bFirstGfxUpdate = TRUE;
}
}
__forceinline
void CGenericThread::DrawFrameInfo(DWORD dwY, const TCHAR* pText, COLORREF color)
{
DWORD strX = 20;
DWORD strWidth;
GDFILLRECTFX fillrectfx;
fillrectfx.dwOpacity = 128;
m_backbuffer.DrawText(0, 0, pText, RGB(0, 0, 0), GDDRAWTEXT_CALCWIDTH, 0, &strWidth);
m_backbuffer.DrawLine(0, dwY-2, strX+strWidth+3, dwY-2, RGB(0, 0, 0), 0, NULL);
m_backbuffer.DrawLine(strX+strWidth+3, dwY-2, strX+strWidth+3, dwY+8, RGB(0, 0, 0), 0, NULL);
m_backbuffer.DrawLine(0, dwY+8, strX+strWidth+3, dwY+8, RGB(0, 0, 0), 0, NULL);
m_backbuffer.FillRect(CRect(0, dwY-1, strX+strWidth+3, dwY+8), color, GDFILLRECT_OPACITY, &fillrectfx);
m_backbuffer.DrawText(strX, dwY, pText, RGB(255, 255, 255), GDDRAWTEXT_BORDER, 0, 0);
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?