-
Notifications
You must be signed in to change notification settings - Fork 0
/
GameEngine.cpp
466 lines (407 loc) · 13.8 KB
/
GameEngine.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
//-----------------------------------------------------------------
// Game Engine Object
// C++ Source - GameEngine.cpp
//-----------------------------------------------------------------
//-----------------------------------------------------------------
// Include Files
//-----------------------------------------------------------------
#include "GameEngine.h"
//-----------------------------------------------------------------
// Static Variable Initialization
//-----------------------------------------------------------------
GameEngine *GameEngine::m_pGameEngine = NULL;
//-----------------------------------------------------------------
// Windows Functions
//-----------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
MSG msg;
static int iTickTrigger = 0;
int iTickCount;
if (GameInitialize(hInstance))
{
// Initialize the game engine
if (!GameEngine::GetEngine()->Initialize(iCmdShow))
return FALSE;
// Enter the main message loop
while (TRUE)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
// Process the message
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
// Make sure the game engine isn't sleeping
if (!GameEngine::GetEngine()->GetSleep())
{
// Check the tick count to see if a game cycle has elapsed
iTickCount = GetTickCount();
if (iTickCount > iTickTrigger)
{
iTickTrigger = iTickCount +
GameEngine::GetEngine()->GetFrameDelay();
HandleKeys();
GameEngine::GetEngine()->CheckJoystick();
GameCycle();
}
}
}
}
return (int)msg.wParam;
}
// End the game
GameEnd();
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam)
{
// Route all Windows messages to the game engine
return GameEngine::GetEngine()->HandleEvent(hWindow, msg, wParam, lParam);
}
//-----------------------------------------------------------------
// Game Engine Helper Methods
//-----------------------------------------------------------------
BOOL GameEngine::CheckSpriteCollision(Sprite* pTestSprite)
{
// See if the sprite has collided with any other sprites
vector<Sprite*>::iterator siSprite;
for (siSprite = m_vSprites.begin(); siSprite != m_vSprites.end(); siSprite++)
{
// Make sure not to check for collision with itself
if (pTestSprite == (*siSprite))
continue;
// Test the collision
if (pTestSprite->TestCollision(*siSprite))
// Collision detected
return SpriteCollision((*siSprite), pTestSprite);
}
// No collision
return FALSE;
}
//-----------------------------------------------------------------
// GameEngine Constructor(s)/Destructor
//-----------------------------------------------------------------
GameEngine::GameEngine(HINSTANCE hInstance, LPTSTR szWindowClass,
LPTSTR szTitle, WORD wIcon, WORD wSmallIcon, int iWidth, int iHeight)
{
// Set the member variables for the game engine
m_pGameEngine = this;
m_hInstance = hInstance;
m_hWindow = NULL;
if (lstrlen(szWindowClass) > 0)
lstrcpy(m_szWindowClass, szWindowClass);
if (lstrlen(szTitle) > 0)
lstrcpy(m_szTitle, szTitle);
m_wIcon = wIcon;
m_wSmallIcon = wSmallIcon;
m_iWidth = iWidth;
m_iHeight = iHeight;
m_iFrameDelay = 50; // 50 FPS default
m_bSleep = TRUE;
m_uiJoystickID = 0;
m_vSprites.reserve(100);
m_uiMIDIPlayerID = 0;
}
GameEngine::~GameEngine()
{
}
//-----------------------------------------------------------------
// Game Engine General Methods
//-----------------------------------------------------------------
BOOL GameEngine::Initialize(int iCmdShow)
{
WNDCLASSEX wndclass;
// Create the window class for the main window
wndclass.cbSize = sizeof(wndclass);
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = m_hInstance;
wndclass.hIcon = LoadIcon(m_hInstance,
MAKEINTRESOURCE(GetIcon()));
wndclass.hIconSm = LoadIcon(m_hInstance,
MAKEINTRESOURCE(GetSmallIcon()));
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = m_szWindowClass;
// Register the window class
if (!RegisterClassEx(&wndclass))
return FALSE;
// Calculate the window size and position based upon the game size
int iWindowWidth = m_iWidth + GetSystemMetrics(SM_CXFIXEDFRAME) * 2,
iWindowHeight = m_iHeight + GetSystemMetrics(SM_CYFIXEDFRAME) * 2 +
GetSystemMetrics(SM_CYCAPTION);
if (wndclass.lpszMenuName != NULL)
iWindowHeight += GetSystemMetrics(SM_CYMENU);
int iXWindowPos = (GetSystemMetrics(SM_CXSCREEN) - iWindowWidth) / 2,
iYWindowPos = (GetSystemMetrics(SM_CYSCREEN) - iWindowHeight) / 2;
// Create the window
m_hWindow = CreateWindow(m_szWindowClass, m_szTitle, WS_POPUPWINDOW |
WS_CAPTION | WS_MINIMIZEBOX, iXWindowPos, iYWindowPos, iWindowWidth,
iWindowHeight, NULL, NULL, m_hInstance, NULL);
if (!m_hWindow)
return FALSE;
// Show and update the window
ShowWindow(m_hWindow, iCmdShow);
UpdateWindow(m_hWindow);
return TRUE;
}
LRESULT GameEngine::HandleEvent(HWND hWindow, UINT msg, WPARAM wParam, LPARAM lParam)
{
// Route Windows messages to game engine member functions
switch (msg)
{
case WM_CREATE:
// Set the game window and start the game
SetWindow(hWindow);
GameStart(hWindow);
return 0;
case WM_SETFOCUS:
// Activate the game and update the Sleep status
GameActivate(hWindow);
SetSleep(FALSE);
return 0;
case WM_KILLFOCUS:
// Deactivate the game and update the Sleep status
GameDeactivate(hWindow);
SetSleep(TRUE);
return 0;
case WM_PAINT:
HDC hDC;
PAINTSTRUCT ps;
hDC = BeginPaint(hWindow, &ps);
// Paint the game
GamePaint(hDC);
EndPaint(hWindow, &ps);
return 0;
case WM_LBUTTONDOWN:
// Handle left mouse button press
MouseButtonDown(LOWORD(lParam), HIWORD(lParam), TRUE);
return 0;
case WM_LBUTTONUP:
// Handle left mouse button release
MouseButtonUp(LOWORD(lParam), HIWORD(lParam), TRUE);
return 0;
case WM_RBUTTONDOWN:
// Handle right mouse button press
MouseButtonDown(LOWORD(lParam), HIWORD(lParam), FALSE);
return 0;
case WM_RBUTTONUP:
// Handle right mouse button release
MouseButtonUp(LOWORD(lParam), HIWORD(lParam), FALSE);
return 0;
case WM_MOUSEMOVE:
// Handle mouse movement
MouseMove(LOWORD(lParam), HIWORD(lParam));
return 0;
case WM_DESTROY:
// End the game and exit the application
GameEnd();
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWindow, msg, wParam, lParam);
}
void GameEngine::ErrorQuit(LPTSTR szErrorMsg)
{
MessageBox(GetWindow(), szErrorMsg, TEXT("Critical Error"), MB_OK | MB_ICONERROR);
PostQuitMessage(0);
}
BOOL GameEngine::InitJoystick()
{
// Make sure joystick driver is present
UINT uiNumJoysticks;
if ((uiNumJoysticks = joyGetNumDevs()) == 0)
return FALSE;
// Make sure the joystick is attached
JOYINFO jiInfo;
if (joyGetPos(JOYSTICKID1, &jiInfo) != JOYERR_UNPLUGGED)
m_uiJoystickID = JOYSTICKID1;
else
return FALSE;
// Calculate the trip values
JOYCAPS jcCaps;
joyGetDevCaps(m_uiJoystickID, &jcCaps, sizeof(JOYCAPS));
DWORD dwXCenter = ((DWORD)jcCaps.wXmin + jcCaps.wXmax) / 2;
DWORD dwYCenter = ((DWORD)jcCaps.wYmin + jcCaps.wYmax) / 2;
m_rcJoystickTrip.left = (jcCaps.wXmin + (WORD)dwXCenter) / 2;
m_rcJoystickTrip.right = (jcCaps.wXmax + (WORD)dwXCenter) / 2;
m_rcJoystickTrip.top = (jcCaps.wYmin + (WORD)dwYCenter) / 2;
m_rcJoystickTrip.bottom = (jcCaps.wYmax + (WORD)dwYCenter) / 2;
return TRUE;
}
void GameEngine::CaptureJoystick()
{
// Capture the joystick
if (m_uiJoystickID == JOYSTICKID1)
joySetCapture(m_hWindow, m_uiJoystickID, (UINT)NULL, TRUE);
}
void GameEngine::ReleaseJoystick()
{
// Release the joystick
if (m_uiJoystickID == JOYSTICKID1)
joyReleaseCapture(m_uiJoystickID);
}
void GameEngine::CheckJoystick()
{
if (m_uiJoystickID == JOYSTICKID1)
{
JOYINFO jiInfo;
JOYSTATE jsJoystickState = 0;
if (joyGetPos(m_uiJoystickID, &jiInfo) == JOYERR_NOERROR)
{
// Check horizontal movement
if (jiInfo.wXpos < (WORD)m_rcJoystickTrip.left)
jsJoystickState |= JOY_LEFT;
else if (jiInfo.wXpos > (WORD)m_rcJoystickTrip.right)
jsJoystickState |= JOY_RIGHT;
// Check vertical movement
if (jiInfo.wYpos < (WORD)m_rcJoystickTrip.top)
jsJoystickState |= JOY_UP;
else if (jiInfo.wYpos > (WORD)m_rcJoystickTrip.bottom)
jsJoystickState |= JOY_DOWN;
// Check buttons
if(jiInfo.wButtons & JOY_BUTTON1)
jsJoystickState |= JOY_FIRE1;
if(jiInfo.wButtons & JOY_BUTTON2)
jsJoystickState |= JOY_FIRE2;
}
// Allow the game to handle the joystick
HandleJoystick(jsJoystickState);
}
}
void GameEngine::AddSprite(Sprite* pSprite)
{
// Add a sprite to the sprite vector
if (pSprite != NULL)
{
// See if there are sprites already in the sprite vector
if (m_vSprites.size() > 0)
{
// Find a spot in the sprite vector to insert the sprite by its z-order
vector<Sprite*>::iterator siSprite;
for (siSprite = m_vSprites.begin(); siSprite != m_vSprites.end(); siSprite++)
if (pSprite->GetZOrder() < (*siSprite)->GetZOrder())
{
// Insert the sprite into the sprite vector
m_vSprites.insert(siSprite, pSprite);
return;
}
}
// The sprite's z-order is highest, so add it to the end of the vector
m_vSprites.push_back(pSprite);
}
}
void GameEngine::DrawSprites(HDC hDC)
{
// Draw the sprites in the sprite vector
vector<Sprite*>::iterator siSprite;
for (siSprite = m_vSprites.begin(); siSprite != m_vSprites.end(); siSprite++)
(*siSprite)->Draw(hDC);
}
void GameEngine::UpdateSprites()
{
// Check to see if the sprite vector needs to grow
if (m_vSprites.size() >= (m_vSprites.capacity() / 2))
m_vSprites.reserve(m_vSprites.capacity() * 2);
// Update the sprites in the sprite vector
RECT rcOldSpritePos;
SPRITEACTION saSpriteAction;
vector<Sprite*>::iterator siSprite;
for (siSprite = m_vSprites.begin(); siSprite != m_vSprites.end(); siSprite++)
{
// Save the old sprite position in case we need to restore it
rcOldSpritePos = (*siSprite)->GetPosition();
// Update the sprite
saSpriteAction = (*siSprite)->Update();
// Handle the SA_KILL sprite action
if (saSpriteAction & SA_KILL)
{
delete (*siSprite);
m_vSprites.erase(siSprite);
siSprite--;
continue;
}
// See if the sprite collided with any others
if (CheckSpriteCollision(*siSprite))
// Restore the old sprite position
(*siSprite)->SetPosition(rcOldSpritePos);
}
}
void GameEngine::CleanupSprites()
{
// Delete and remove the sprites in the sprite vector
vector<Sprite*>::iterator siSprite;
for (siSprite = m_vSprites.begin(); siSprite != m_vSprites.end(); siSprite++)
{
delete (*siSprite);
m_vSprites.erase(siSprite);
siSprite--;
}
}
Sprite* GameEngine::IsPointInSprite(int x, int y)
{
// See if the point is in a sprite in the sprite vector
vector<Sprite*>::reverse_iterator siSprite;
for (siSprite = m_vSprites.rbegin(); siSprite != m_vSprites.rend(); siSprite++)
if (!(*siSprite)->IsHidden() && (*siSprite)->IsPointInside(x, y))
return (*siSprite);
// The point is not in a sprite
return NULL;
}
void GameEngine::PlayMIDISong(LPTSTR szMIDIFileName, BOOL bRestart)
{
// See if the MIDI player needs to be opened
if (m_uiMIDIPlayerID == 0)
{
// Open the MIDI player by specifying the device and filename
MCI_OPEN_PARMS mciOpenParms;
mciOpenParms.lpstrDeviceType = "sequencer";
mciOpenParms.lpstrElementName = szMIDIFileName;
if (mciSendCommand((MCIDEVICEID)NULL, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_ELEMENT,
(DWORD_PTR)&mciOpenParms) == 0)
// Get the ID for the MIDI player
m_uiMIDIPlayerID = mciOpenParms.wDeviceID;
else
// There was a problem, so just return
return;
}
// Restart the MIDI song, if necessary
if (bRestart)
{
MCI_SEEK_PARMS mciSeekParms;
if (mciSendCommand(m_uiMIDIPlayerID, MCI_SEEK, MCI_SEEK_TO_START,
(DWORD_PTR)&mciSeekParms) != 0)
// There was a problem, so close the MIDI player
CloseMIDIPlayer();
}
// Play the MIDI song
MCI_PLAY_PARMS mciPlayParms;
if (mciSendCommand(m_uiMIDIPlayerID, MCI_PLAY, 0,
(DWORD_PTR)&mciPlayParms) != 0)
// There was a problem, so close the MIDI player
CloseMIDIPlayer();
}
void GameEngine::PauseMIDISong()
{
// Pause the currently playing song, if possible
if (m_uiMIDIPlayerID != 0)
mciSendCommand(m_uiMIDIPlayerID, MCI_PAUSE, 0, (DWORD_PTR)NULL);
}
void GameEngine::CloseMIDIPlayer()
{
// Close the MIDI player, if possible
if (m_uiMIDIPlayerID != 0)
{
mciSendCommand(m_uiMIDIPlayerID, MCI_CLOSE, 0, (DWORD_PTR)NULL);
m_uiMIDIPlayerID = 0;
}
}