forked from ebobtron/WinBreakoutCPlusPlus
/
breakout.cpp
370 lines (297 loc) · 11.9 KB
/
breakout.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
/**
*
* WinBreakoutC++
*
* breakout.cpp
*
* 2015 a collaboration of CS50x students
*
*/
#include "breakout.h"
/** declare some global object variables **/
theBALL ballInfo;
RECT mwinRect; // main window rectangle
RECT ballRect; // ball rectangle
RECT ballRectInvaild; // ball rectangle for future and past positions
// needed to redraw only where needed
RECT textRect; // text rectangle
RECT paddleRect; // paddle rectangle
RECT paddleRectInvaild; // rectangle need to for paddle redraw
POINTS mpoint_x; // POINTS structure to relay mouse data to paddle
/** need to keep track of paddle length beyond it's creation **/
/** maybe a paddle info structure like ball **/
int paddlelength = 0;
/** make the main window **/
HWND createMainWin(HINSTANCE hThisInstance, int sizeX, int sizeY){
/** declare a class name. Why? Because. **/
TCHAR szClassName[ ] = _T("Student Project");
/** declare the main window handle **/
HWND hwnd = NULL;
/** declare data structure object variable for the window class **/
WNDCLASSEX wincl;
/** initialize the window structure **/
wincl.hInstance = hThisInstance;
wincl.lpszClassName = szClassName;
wincl.lpfnWndProc = WindowProcedure; // This function is called by windows
wincl.style = CS_DBLCLKS; // Catch double-clicks
wincl.cbSize = sizeof (WNDCLASSEX);
/** use default icon and mouse-pointer **/
wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
/** no menu **/
wincl.lpszMenuName = NULL;
/** no extra bytes after the window class structure
or the window instance */
wincl.cbClsExtra = 0;
wincl.cbWndExtra = 0;
/** add a custom background color */
wincl.hbrBackground = CreateSolidBrush(RGB(255, 255, 255));
/** Register the window class, if it fails quit the program */
if(!RegisterClassEx(&wincl))
return 0;
/** the class is registered,
let's create the programs main window */
hwnd = CreateWindowEx(
0, // extended possibilities for variation
szClassName, // Class name
_T(" WinBreakoutC++ "), // Title Text
WS_OVERLAPPEDWINDOW, // default window style
CW_USEDEFAULT, // windows decides the position
CW_USEDEFAULT, // where the window ends up on the screen
sizeX, // the window width
sizeY, // and height in pixels
HWND_DESKTOP, // the window is a child-window to desktop
NULL, // no menu
hThisInstance, // the program instance handle
NULL // no window creation data
);
/** if window creation successful return the main window handle
any work in the window will need this handle **/
if(hwnd)
return hwnd;
/** if we get this far we have failed to create
a window for our program, returning 0 will
cause a message to the user and then quit the process. **/
return 0;
}
/** pay sound **/
void playSound(int sound){
switch(sound){
case 1:
PlaySound(MAKEINTRESOURCE(IDW_MYBALLSOUND),
GetModuleHandle(NULL), SND_ASYNC|SND_RESOURCE);
break;
default:;
}
}
/** make the ball **/
void createBall(int sizeDia, int x, int y){
/** set up the ball rectangles **/
ballRect.left = x - sizeDia / 2;
ballRect.top = y - sizeDia / 2;
ballRect.right = x + sizeDia / 2;
ballRect.bottom = y + sizeDia / 2;
ballInfo.sizeDia = sizeDia;
ballInfo.x = ballRect.left;
ballInfo.y = ballRect.top;
/** seed random number gen **/
srand(GetTickCount());
/** provide some random kick to the ball velocity **/
// TODO: this method can result in zero
ballInfo.vx = 2 + (int)(double)rand() / (RAND_MAX + 1) * (3 - -3) + -3;
ballInfo.vy = 4 + (int)(double)rand() / (RAND_MAX + 1) * (3 - -3) + -3;
/** play a starting sound / a natural pause **/
playSound(1);
}
/** make the paddle **/
void createPaddle(int posy, int length, int height){
/** preserve the length of the paddle **/
paddlelength = length;
/** load paddle rectangle **/
paddleRect.left = mwinRect.right / 2 - length / 2;
paddleRect.top = posy;
paddleRect.right = paddleRect.left + length;
paddleRect.bottom = paddleRect.top + height;
/** load the paddle invalidate rectangle **/
paddleRectInvaild = paddleRect;
paddleRectInvaild.left = mwinRect.left;
paddleRectInvaild.right = mwinRect.right;
}
/** update paddle rectangle when ever mouse moves **/
void updatePaddle(){
/** alter paddle position from mouse position **/
paddleRect.left = mpoint_x.x - paddlelength / 2;
paddleRect.right = paddleRect.left + paddlelength;
}
/****************************
* *
* update game data *
* *
****************************/
/* the whole game is a bunch of rectangles */
/* manipulated here */
void updateGame(HWND hwnd){
/**********************
* *
* Move The Ball *
* *
**********************/
/** MS's Offset Rectangle function make moving the rectangle easy **/
OffsetRect(&ballRect, ballInfo.vx, ballInfo.vy);
/** copy new ball rectangle **/
ballRectInvaild = ballRect;
/*************************************************************************
* Adjust the RECT used to invalidate the part of the screen we need *
* to redraw for the ball. The region needed to be redrawn is where *
* the ball is and where it is going to be on the next screen redraw *
*************************************************************************/
if(ballInfo.vx > 0){
ballRectInvaild.left = ballRect.left - ballInfo.vx;
ballRectInvaild.right = ballRect.right + ballInfo.vx;
}
else{
ballRectInvaild.left = ballRect.left + ballInfo.vx;
ballRectInvaild.right = ballRect.right - ballInfo.vx;
}
if(ballInfo.vy > 0){
ballRectInvaild.top = ballRect.top - ballInfo.vy;
ballRectInvaild.bottom = ballRect.bottom + ballInfo.vy;
}
else{
ballRectInvaild.top = ballRect.top + ballInfo.vy;
ballRectInvaild.bottom = ballRect.bottom - ballInfo.vy;
}
/******************************
* *
* Detect Ball Collisions *
* *
******************************/
detectCollisions(hwnd);
}
/** detect collisions of the ball
with window walls, paddle and bricks **/
void detectCollisions(HWND hwnd){
/** get main window rectangle **/
GetClientRect(hwnd, &mwinRect);
/** ball can collide in corner any one or **/
/** two of the following are possible **/
/** if collision left **/
if(ballRect.left <= mwinRect.left){
ballInfo.vx = -ballInfo.vx;
ballRect.left = mwinRect.left + 1;
ballRect.right = ballInfo.sizeDia + 1;
playSound(1);
}
/** if collision top **/
if(ballRect.top <= mwinRect.top){
ballInfo.vy = -ballInfo.vy;
ballRect.top = mwinRect.top + 1;
ballRect.bottom = ballInfo.sizeDia + 1;
playSound(1);
}
/** if collision right **/
if(ballRect.right >= mwinRect.right){
ballInfo.vx = -ballInfo.vx;
ballRect.left = mwinRect.right - ballInfo.sizeDia - 1;
ballRect.right = mwinRect.right - 1;
playSound(1);
}
/** if collision bottom **/
// TODO (ebob): add code to flag event "ball lost"
if(ballRect.bottom >= mwinRect.bottom){
ballInfo.vy = -ballInfo.vy;
ballRect.top = mwinRect.bottom - ballInfo.sizeDia - 1;
ballRect.bottom = mwinRect.bottom - 1;
playSound(1);
}
// TODO: this is messy and not complete
/** detect paddle collisions **/
if(ballRect.bottom > paddleRect.top){
if(paddleRect.left < ballRect.right &&
paddleRect.right > ballRect.left){
ballInfo.vy = -ballInfo.vy;
playSound(1);
}
}
}
/** paint text in window **/
void paintText(HDC hdc, LPCTSTR tsText){
/** write character string to location x, y **/
/* TODO: This code needs work, our exact needs for text display is
not completely defined yet */
int width = 150;
int height = 60;
/** like everything we draw to the screen **/
/** create the text rectangle **/
textRect.left = (mwinRect.right - width) / 2 ;
textRect.top = 60;
textRect.right = textRect.left + width;
textRect.bottom = textRect.top + height;
/** draw the text using rectangle **/
DrawTextEx(hdc, (LPSTR)tsText, _tcslen(tsText), &textRect, DT_CENTER, 0);
}
/** refresh the window here we call all drawing functions **/
void refreshWindow(HWND hwnd, LPCTSTR lpOptionalText){
/** we need to deal with optional text **/
/** calling function can omit the text **/
/** this is demonstrative at this point **/
/** declare a wide CHAR buffer for the Unicode crowd **/
/** for some one you this is a string **/
TCHAR tcText[64];
// TODO: there is a wide character issue here, important ??
sprintf_s(tcText, sizeof(tcText),
"%s\n x = %d y = %d\n paddle x = %d" ,
"ball position",
ballRect.left, ballRect.top, mpoint_x.x);
/** if calling function sends text, us it **/
if(lpOptionalText)
lstrcpy((LPSTR)tcText, lpOptionalText);
/** declare the paint structure object variable **/
/** needed by the device context **/
PAINTSTRUCT ps;
/** get the handle of the device context for our window needed **/
/** by functions that paint or draw to the window **/
HDC hdc = BeginPaint(hwnd, &ps);
/*******************
* *
* DRAW PADDLE *
* *
*******************/
Rectangle(hdc,
paddleRect.left,
paddleRect.top,
paddleRect.right,
paddleRect.bottom);
/**********************************
* *
* PAINT TEXT TO THE WINDOW *
* *
**********************************/
paintText(hdc, (LPCTSTR)tcText);
/** using the device context we can change **/
/** the color of pen and brush for the device context **/
/** changing before drawing the ball causes the ball **/
/** to change to red in this example **/
/** Note: if you don't select on object, PEN, BRUSH **/
/** you can't change it **/
SelectObject(hdc, GetStockObject(DC_PEN));
SetDCPenColor(hdc, RGB(255, 0, 0));
SelectObject(hdc, GetStockObject(DC_BRUSH));
SetDCBrushColor(hdc, RGB(255, 0, 0));
/*********************
* *
* DRAW THE BALL *
* *
*********************/
Ellipse(hdc,
ballRect.left, // int nLeftRect
ballRect.top, // int nTopRect
ballRect.right, // int nRightRect
ballRect.bottom); // int nBottomRect
/** much like free we use end paint to return **/
/** the device context to the system. if we **/
/** don't release the DC the resource will be lost **/
/** as a memory leak **/
EndPaint(hwnd, &ps);
}