• 双人黑白块


    转载请标明地址: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 }
  • 相关阅读:
    再次或多次格式化导致namenode的ClusterID和datanode的ClusterID之间不一致的问题解决办法
    Linux安装aria2
    POJ 3335 Rotating Scoreboard 半平面交
    hdu 1540 Tunnel Warfare 线段树 区间合并
    hdu 3397 Sequence operation 线段树 区间更新 区间合并
    hud 3308 LCIS 线段树 区间合并
    POJ 3667 Hotel 线段树 区间合并
    POJ 2528 Mayor's posters 贴海报 线段树 区间更新
    POJ 2299 Ultra-QuickSort 求逆序数 线段树或树状数组 离散化
    POJ 3468 A Simple Problem with Integers 线段树成段更新
  • 原文地址:https://www.cnblogs.com/wangmengmeng/p/4716458.html
Copyright © 2020-2023  润新知