zoukankan      html  css  js  c++  java
  • 双人黑白块

    转载请标明地址: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 }
  • 相关阅读:
    Python中的try...except...finally
    JavaScript 实现双向队列并用此来测试一个单词是否为回文
    js 触发 change 事件
    MySQL8 重置改root密码及开放远程访问
    根据数组下标在MongoDB中修改数组元素
    pymongo CursorNotFound错误
    mongodb根据子项中的指标查找最小或最大值
    正则文本过滤时的一些注意事项
    github page更新后不生效
    Xpath同时选取不同属性的元素
  • 原文地址:https://www.cnblogs.com/wangmengmeng/p/4716458.html
Copyright © 2011-2022 走看看