双人黑白块

转载请标明地址: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 }
原文地址:https://www.cnblogs.com/wangmengmeng/p/4716458.html