标签:
转载请标明地址:http://www.cnblogs.com/wangmengmeng/
效果图:
源代码:
1 #undef UNICODE 2 #undef _UNICODE 3 #include <graphics.h> 4 #include <conio.h> 5 #include <stdio.h> 6 #include <time.h> 7 8 #define MAXTASK 50 // 定义游戏需要完成的黑块数量 9 10 // 定义宏 __sprintf 自适应 vc6 与 vc2013 11 #if _MSC_VER > 1200 12 #define __sprintf(...) sprintf_s(__VA_ARGS__) 13 #else 14 #define __sprintf sprintf 15 #endif 16 17 18 // 精确延时函数(可以精确到 1ms,精度 ±1ms) 19 // 摘自 www.easyx.cn 20 void HpSleep(int ms) 21 { 22 static clock_t oldclock = clock(); // 静态变量,记录上一次 tick 23 24 oldclock += ms * CLOCKS_PER_SEC / 1000; // 更新 tick 25 26 if (clock() > oldclock) // 如果已经超时,无需延时 27 oldclock = clock(); 28 else 29 while (clock() < oldclock) // 延时 30 Sleep(1); // 释放 CPU 控制权,降低 CPU 占用率 31 } 32 33 34 // 游戏状态常量 35 enum STATUS{BEGIN, // 游戏开始 36 RUNNING, // 游戏运行中 37 PASSANI, // 游戏通过的动画 38 PASS, // 游戏通过 39 FAILANI, // 游戏失败的动画 40 FAIL }; // 游戏失败 41 42 43 // 游戏者类(每个游戏者都有一个独立的游戏区域) 44 class PLAYER 45 { 46 private: 47 STATUS m_status; // 游戏状态 48 char* m_strName; // 游戏者名称 49 POINT m_offset; // 界面的偏移量 50 char* m_keys; // 按键 51 52 // 任务 53 byte m_Task[MAXTASK]; // 任务列表 54 byte m_iTask; // 当前需要执行的任务 ID 55 int m_nextTaskY; // 界面中下一个任务的 Y 坐标 56 57 // 时钟和游戏记录 58 clock_t m_beginClock; // 游戏开始的时钟计数 59 float m_bestTime; // 最佳纪录的完成时间 60 float m_lastTime; // 最后一次的完成时间 61 62 // 控制失败动画的变量 63 byte m_failErrorKey; // 按错的键的序号(值为 0、1、2、3) 64 RECT m_failRect; // 按错的键的区域 65 int m_failFrame; // 失败后的动画的帧计数 66 67 public: 68 PLAYER(char* name, char* keys, int offsetx, int offsety); // 构造函数 69 void Hit(char key); // 处理游戏者按键 70 void Draw(); // 绘制该游戏者的游戏界面 71 private: 72 void Init(); // 初始化当前游戏者的游戏信息 73 void DrawFrame(); // 绘制游戏界面的外框 74 void DrawRow(int baseY, int iTask); // 绘制游戏界面中的一行任务 75 void DrawPass(); // 绘制通过游戏后的界面 76 void DrawFail(); // 绘制游戏失败后的界面 77 78 // 进行偏移量计算的绘图函数 79 void OutTextXY(int x, int y, LPCTSTR s) // 在指定位置输出字符串 80 { 81 outtextxy(m_offset.x + x, m_offset.y + y, s); 82 } 83 void OutTextXY(int x, int y, char c) // 在指定位置输出字符 84 { 85 outtextxy(m_offset.x + x, m_offset.y + y, c); 86 } 87 void Rectangle(int x1, int y1, int x2, int y2) // 绘制矩形 88 { 89 rectangle(m_offset.x + x1, m_offset.y + y1, m_offset.x + x2, m_offset.y + y2); 90 } 91 void FillRectangle(int x1, int y1, int x2, int y2) // 绘制有边框填充矩形 92 { 93 fillrectangle(m_offset.x + x1, m_offset.y + y1, m_offset.x + x2, m_offset.y + y2); 94 } 95 void SolidRectangle(int x1, int y1, int x2, int y2) // 绘制无边框填充矩形 96 { 97 solidrectangle(m_offset.x + x1, m_offset.y + y1, m_offset.x + x2, m_offset.y + y2); 98 } 99 }; 100 101 102 // 构造函数 103 // 参数: 104 // name: 游戏者名称 105 // keys: 游戏者所用按键(指向长度为 4 的字符串) 106 // offsetx, offsety: 游戏者对应的游戏区域在主窗口中的偏移量 107 PLAYER::PLAYER(char* name, char* keys, int offsetx, int offsety) 108 { 109 m_strName = name; 110 m_keys = keys; 111 m_offset.x = offsetx; 112 m_offset.y = offsety; 113 114 m_bestTime = 99; // 设置最佳成绩 115 116 Init(); // 初始化游戏者 117 } 118 119 120 // 初始化当前游戏者的游戏信息 121 void PLAYER::Init() 122 { 123 // 初始化任务 124 for (int i = 0; i < MAXTASK; i++) 125 m_Task[i] = rand() % 4; 126 127 m_iTask = 0; // 从第一个任务开始 128 m_nextTaskY = 200; // 设定下一行任务的 Y 坐标,100 是基准,200 表示开始会有下落的动画 129 m_status = BEGIN; // 设置游戏初始状态 130 m_failFrame = 0; // 重置失败后的动画的帧计数 131 132 // 初始化游戏界面 133 DrawFrame(); 134 } 135 136 137 // 绘制该游戏者的游戏界面 138 void PLAYER::Draw() 139 { 140 switch (m_status) 141 { 142 case PASSANI: // 游戏成功后的动画 143 if (m_nextTaskY == 100) 144 { 145 m_status = PASS; 146 DrawPass(); 147 break; 148 } 149 150 case BEGIN: // 游戏初次开始 151 case RUNNING: // 游戏运行中 152 { 153 // 如果画面处于静止,直接返回不再重绘 154 if (m_nextTaskY == 100) 155 return; 156 157 m_nextTaskY -= (m_nextTaskY - 100 + 9) / 10; 158 159 // 绘制完成的任务区 160 int rowy = m_nextTaskY; 161 int itask = m_iTask; 162 do 163 { 164 rowy -= 100; 165 itask--; 166 DrawRow(rowy, itask); 167 } while (rowy > 0); 168 169 // 绘制未完成的任务区 170 rowy = m_nextTaskY; 171 itask = m_iTask; 172 do 173 { 174 DrawRow(rowy, itask); 175 rowy += 100; 176 itask++; 177 } while (rowy < 400); 178 179 break; 180 } 181 182 case FAILANI: // 游戏失败后的动画 183 DrawFail(); 184 break; 185 186 case PASS: // 游戏通过后的成绩显示 187 case FAIL: // 游戏失败后的成绩显示 188 break; 189 } 190 } 191 192 193 // 绘制游戏界面的外框 194 void PLAYER::DrawFrame() 195 { 196 // 画外框 197 setlinecolor(0xfb9700); 198 Rectangle(0, 0, 243, 464); 199 setfillcolor(0xeca549); 200 settextcolor(BLACK); 201 settextstyle(16, 0, "Verdana"); 202 setbkmode(TRANSPARENT); 203 204 // 画姓名区 205 SolidRectangle(2, 2, 241, 21); 206 int w = textwidth(m_strName); 207 OutTextXY((244 - w) / 2, 4, m_strName); 208 209 // 画成绩区 210 SolidRectangle(2, 23, 241, 42); 211 char tmp[50]; 212 __sprintf(tmp, "最好记录:%.3f 秒", m_bestTime); 213 OutTextXY(10, 26, tmp); 214 215 // 2 <= x <= 241, 44 <= y <= 443 为游戏区 216 217 // 画控制区 218 SolidRectangle(2, 445, 241, 462); 219 for (int i = 0; i < 4; i++) 220 OutTextXY(2 + i * 60 + 26, 446, m_keys[i]); 221 } 222 223 224 // 绘制游戏界面中的一行任务 225 void PLAYER::DrawRow(int baseY, int iTask) 226 { 227 int fromY = baseY; // 任务行的起始 y 坐标 228 int toY = baseY + 99; // 任务行的终止 y 坐标 229 230 // 如果 y 坐标超出显示范围,做调整 231 if (fromY < 0) fromY = 0; 232 if (toY > 399) toY = 399; 233 234 COLORREF c[4]; // 任务行四个方块的颜色 235 if (iTask < 0) 236 { 237 for (int i = 0; i < 4; i++) 238 c[i] = YELLOW; 239 } 240 else if (iTask >= MAXTASK) 241 { 242 for (int i = 0; i < 4; i++) 243 c[i] = GREEN; 244 } 245 else 246 { 247 for (int i = 0; i < 4; i++) 248 c[i] = WHITE; 249 250 c[m_Task[iTask]] = (iTask < m_iTask)? LIGHTGRAY : BLACK; 251 } 252 253 // 画任务行的四个方块 254 setlinecolor(0xe9dbd6); 255 for (int i = 0; i < 4; i++) 256 { 257 setfillcolor(c[i]); 258 FillRectangle(2 + i * 60, 44 + 399 - fromY, 2 + i * 60 + 59, 44 + 399 - toY); 259 } 260 261 // 如果是第一行,在方块儿上写“开始”两个字 262 if (iTask == 0 && m_iTask == 0) 263 { 264 int w = textwidth("开始"); 265 int h = textheight("开始"); 266 int x = 2 + m_Task[iTask] * 60 + (60 - w) / 2; 267 int y = 44 + 399 - 99 - fromY + (100 - h) / 2; 268 settextcolor(WHITE); 269 settextstyle(16, 0, "Verdana"); 270 OutTextXY(x, y, "开始"); 271 } 272 } 273 274 275 // 绘制通过游戏后的界面 276 void PLAYER::DrawPass() 277 { 278 // 绘制成功的背景 279 setfillcolor(GREEN); 280 SolidRectangle(2, 44, 241, 443); 281 282 // 输出"成功" 283 settextcolor(WHITE); 284 settextstyle(60, 0, "Verdana"); 285 int w = textwidth("成功"); 286 OutTextXY((244 - w) / 2, 100, "成功"); 287 288 // 输出成绩 289 char tmp[100]; 290 settextstyle(32, 0, "Verdana"); 291 __sprintf(tmp, "成绩:%.3f 秒", m_lastTime); 292 w = textwidth(tmp); 293 OutTextXY((244 - w) / 2, 200, tmp); 294 __sprintf(tmp, "速度:%.3f/s", MAXTASK / m_lastTime); 295 OutTextXY((244 - w) / 2, 240, tmp); 296 297 // 输出重新开始的提示 298 settextstyle(16, 0, "Verdana"); 299 w = textwidth("按任意控制键重新开始"); 300 OutTextXY((244 - w) / 2, 400, "按任意控制键重新开始"); 301 } 302 303 304 // 绘制游戏失败后的界面 305 void PLAYER::DrawFail() 306 { 307 if (m_failFrame == 0) 308 { // 初始化,计算闪烁效果的区域 309 m_failRect.left = 3 + m_failErrorKey * 60; 310 m_failRect.right = m_failRect.left + 57; 311 m_failRect.bottom = m_nextTaskY + 1; 312 m_failRect.top = m_nextTaskY + 98; 313 314 if (m_failRect.top > 398) m_failRect.top = 398; 315 m_failRect.bottom = 44 + 399 - m_failRect.bottom; 316 m_failRect.top = 44 + 399 - m_failRect.top; 317 } 318 319 if (m_failFrame < 60) 320 { // 实现闪烁效果 321 setfillcolor(((m_failFrame / 6) % 2 == 0) ? RED : LIGHTRED); 322 SolidRectangle(m_failRect.left, m_failRect.bottom, m_failRect.right, m_failRect.top); 323 m_failFrame++; 324 } 325 else 326 { 327 // 改变游戏状态 328 m_status = FAIL; 329 330 // 绘制失败的背景 331 setfillcolor(RED); 332 SolidRectangle(2, 44, 241, 443); 333 334 // 输出"失败" 335 settextcolor(WHITE); 336 settextstyle(60, 0, "Verdana"); 337 int w = textwidth("失败"); 338 OutTextXY((244 - w) / 2, 100, "失败"); 339 340 // 输出历史成绩 341 settextstyle(20, 0, "Verdana"); 342 char tmp[100]; 343 __sprintf(tmp, "历史最好成绩:%.3f 秒", m_bestTime); 344 w = textwidth(tmp); 345 OutTextXY((244 - w) / 2, 200, tmp); 346 347 // 输出重新开始的提示 348 settextstyle(16, 0, "Verdana"); 349 w = textwidth("按任意控制键重新开始"); 350 OutTextXY((244 - w) / 2, 400, "按任意控制键重新开始"); 351 } 352 } 353 354 355 // 处理游戏者按键 356 void PLAYER::Hit(char key) 357 { 358 switch (m_status) 359 { 360 case BEGIN: // 游戏初次开始 361 if (strchr(m_keys, key) != NULL) 362 { 363 m_beginClock = clock(); // 记录游戏开始时的时钟 364 m_status = RUNNING; // 改变游戏状态 365 } 366 367 case RUNNING: // 游戏运行中 368 { 369 char* pdest = strchr(m_keys, key); 370 byte pos; 371 if (pdest != NULL) // 判断是否是当前游戏者按键 372 { 373 pos = pdest - m_keys; // 计算按键对应的位置 374 375 if (pos == m_Task[m_iTask]) // 判断按键是否正确 376 { 377 // 按键正确 378 m_iTask++; 379 m_nextTaskY += 100; 380 381 if (m_iTask == MAXTASK) // 如果完成了全部任务 382 { 383 // 计算完成时间 384 clock_t t = clock(); 385 m_lastTime = ((float)(clock() - m_beginClock)) / CLOCKS_PER_SEC; 386 387 // 更新最好记录 388 if (m_lastTime < m_bestTime) 389 m_bestTime = m_lastTime; 390 391 // 将最后一条任务滚动出屏幕 392 m_iTask++; 393 m_nextTaskY += 100; 394 m_status = PASSANI; 395 } 396 } 397 else 398 { 399 // 按键失败 400 m_failErrorKey = pos; 401 m_status = FAILANI; 402 } 403 } 404 405 break; 406 } 407 408 case PASSANI: // 游戏成功后的动画 409 case FAILANI: // 游戏失败后的动画 410 break; 411 412 case PASS: // 游戏通过后的成绩显示 413 case FAIL: // 游戏失败后的成绩显示 414 if (strchr(m_keys, key) != NULL) 415 Init(); 416 break; 417 } 418 } 419 420 421 // 程序入口主函数 422 void main() 423 { 424 425 initgraph(640, 480); // 创建绘图窗口 426 srand((unsigned)time(NULL)); // 设置随机函数种子 427 428 setbkcolor(0x01bbfb); 429 cleardevice(); 430 431 PLAYER p1("玩家一", "asdf", 38, 8); // 创建游戏者 喜羊羊 432 PLAYER p2("玩家二", "jkl;", 358, 8); // 创建游戏者 灰太狼 433 434 char c = 0; 435 436 while (c != 27) 437 { 438 while (_kbhit()) // 判断是否有按键 439 { 440 // 按键处理 441 c = _getch(); 442 p1.Hit(c); 443 p2.Hit(c); 444 } 445 446 // 绘制游戏场景 447 p1.Draw(); 448 p2.Draw(); 449 450 // 延时 451 HpSleep(16); 452 } 453 454 // 结束游戏 455 closegraph(); // 关闭绘图窗口 456 }
标签:
原文地址:http://www.cnblogs.com/wangmengmeng/p/4716458.html