0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

C/C++项目实战分享

C语言编程学习基地 来源:C语言编程学习基地 2023-01-14 14:41 次阅读

每天一个C语言小项目,提升你的编程能力!

这是经典的下 100 层游戏。>_<

通过鼠标左右键或者键盘 A、D 按键及左右方向键控制小球左右移动,木板会不断上升,小球到底认定游戏结束,按下键盘后小球会加速到一个最大速度,小球在木板上则会跟着木板上移,达到一定分数启动反向操作。

本程序采用单例设计模式,RollingBall 公有继承 BallAndPlank,protected 便于派生类访问基类数据成员。

木板的颜色随机,位置随机。

界面由初始化界面大小控制,可自行更改。

简单了解游戏后我们就来试试吧!(直接上源码,大家可以看注释)

代码展示:

/*
  项目名称:
    RollingBall
  作者:
    tzdhu.z@qq.com 
  项目介绍:
    本程序由单例设计模式 RollingBall 公有继承 BallAndPlank
    protected 便于派生类访问基类数据成员
    模板颜色随机,位置随机
    界面由初始化界面大小控制,初学者可自行更改
  版权声明:
    本程序完全由作者所创,不涉及任何侵权行为,仅用于学习
*/


// 头文件
#include 
#include 
#include 






// 全局变量
const COLORREF BKCOLOR = BLACK;                              // 绘图窗口背景颜色
const int max_x = 640;                                  // 绘图窗口像素宽度
const int max_y = 480;                                  // 绘图窗口像素高度
const int max_smx = GetSystemMetrics(SM_CXSCREEN);                    // 电脑屏幕像素宽度
const int max_smy = GetSystemMetrics(SM_CYSCREEN);                    // 电脑屏幕像素高度
const int inverse_operate_score = 600;                          // 反向操作启动分数
static IMAGE img_start(80, 40), id_start(80, 40), img_exit(80, 40), id_exit(80, 40);  // 用于按钮效果的 IMAGE






////////////////////////////////////////////////////
// 小球和木板类
class BallAndPlank
{
  //小球结构体
  typedef struct BALL
  {
    int ball_x;            // 小球球心位置 x
    int ball_y;            // 小球球心位置 y
  }Ball;


  //木板结构体
  typedef struct PLANK
  {
    int plank_x;          // 木板左端位置 x
    int plank_y;          // 木板位置 y
    int plank_len;          // 木板长度
    COLORREF plank_color;      // 木板颜色
    int thorn_x;          // 尖刺左端位置 x
    bool is_thorn;          // 木板是否有尖刺
  }Plank;


public:
  // 构造函数初始化参数
  BallAndPlank()
  {
    // 小球
    ball_r = 4;
    ball_ddx = 1;
    ball_dx_min = 0;
    ball_dx_max = 8;
    left_right = STOP;
    ball_dx = ball_dx_min;
    ball_dy = ball_dx_min;
    ball_color = RGB(255, 0, 0);


    // 木板
    plank_dy = 1;
    plank_len_min = 50;
    plank_len_max = 150;
    thorn_len = 32;
    thorn_h = 4;
    plank_gap = (max_y - 1) / plank_num;
  }


  ~BallAndPlank()
  {
    // 未定义
  }


  // 小球颜色绘制小球
  void DrawBall(int x, int y)
  {
    setfillcolor(ball_color);
    solidcircle(x, y, ball_r);
  }


  // 背景颜色清除小球
  void CleanBall(int x, int y)
  {
    setfillcolor(BKCOLOR);
    solidcircle(x, y, ball_r);
  }


  bool IsThorn()
  {
    return (rand() % 1000 > 600) ? true : false;
  }


  // 木板颜色绘制木板
  void DrawPlank()
  {
    for (int i = 0; i < plank_num; i++)
    {
      setlinecolor(plank[i].plank_color);
      line(plank[i].plank_x, plank[i].plank_y, plank[i].plank_x + plank[i].plank_len, plank[i].plank_y);
      if (plank[i].is_thorn == true)
      {
        for (int j = plank[i].thorn_x; j < plank[i].thorn_x + thorn_len; j += 2 * thorn_h)
        {
          line(j, plank[i].plank_y, j + thorn_h, plank[i].plank_y - thorn_h - 1);
          line(j + thorn_h, plank[i].plank_y - thorn_h - 1, j + 2 * thorn_h, plank[i].plank_y);
        }
      }
    }
  }


  // 背景颜色清除木板
  void CleanPlank()
  {
    setlinecolor(BKCOLOR);
    for (int i = 0; i < plank_num; i++)
    {
      line(plank[i].plank_x, plank[i].plank_y, plank[i].plank_x + plank[i].plank_len, plank[i].plank_y);
      if (plank[i].is_thorn == true)
      {
        for (int j = plank[i].thorn_x; j < plank[i].thorn_x + thorn_len; j += 2 * thorn_h)
        {
          line(j, plank[i].plank_y, j + thorn_h, plank[i].plank_y - thorn_h - 1);
          line(j + thorn_h, plank[i].plank_y - thorn_h - 1, j + 2 * thorn_h, plank[i].plank_y);
        }
      }
    }
  }


protected:                        // 保护用于派生类访问数据成员
  // 小球属性
  enum Left_Right { STOP, LEFT, RIGHT };        // 枚举小球左右方向
  int ball_r;                      // 小球半径
  int ball_ddx;                    // 可视为小球加速度
  int ball_dx_min;                  // 小球 x 方向最小步长
  int ball_dx_max;                  // 小球 x 方向最大步长
  int left_right;                    // 小球左右方向
  int ball_dx;                    // 可视为小球 x 方向速度
  int ball_dy;                    // 可视为小球 y 方向速度
  COLORREF ball_color;                // 小球颜色
  Ball ball;                      // 小球结构对象


  // 木板属性
  enum Plank_Num { plank_num = 7 };          // 枚举初始化木板数量
  int plank_dy;                    // 可视为木板速度
  int plank_len_min;                  // 木板最小长度
  int plank_len_max;                  // 木板最大长度
  int plank_gap;                    // 木板间隔
  int thorn_len;                    // 尖刺长度
  int thorn_h;                    // 尖刺高度
  Plank plank[plank_num];                // 木板结构对象数组
};






////////////////////////////////////////////////////
// 单例设计模式 RollingBall派生类
class RollingBall : public BallAndPlank
{
public:
  ~RollingBall()
  {
    // 未定义
  }


  // 获取单例指针
  static RollingBall *GetInstance()
  {
    static RollingBall RB;
    return &RB;
  }


  // 开始前介绍界面
  void Introduce()
  {
    setbkcolor(BKCOLOR);
    cleardevice();
    settextcolor(LIGHTMAGENTA);
    settextstyle(50, 0, _T("黑体"));
    outtextxy((max_x - textwidth(_T("RollingBall"))) / 2, max_y / 5, _T("RollingBall"));
    settextcolor(GREEN);
    settextstyle(25, 0, _T("黑体"));
    outtextxy((max_x - textwidth(_T("ESC退出,空格暂停"))) / 2, max_y / 5 * 2 + 20, _T("ESC退出,空格暂停"));
    outtextxy((max_x - textwidth(_T("控制方向:左右方向键,AD,鼠标左右键"))) / 2, max_y / 5 * 2 + 60, _T("控制方向:左右方向键,AD,鼠标左右键"));


    SetWorkingImage(&img_start);
    setbkcolor(LIGHTGRAY);
    cleardevice();
    settextcolor(BROWN);
    settextstyle(25, 0, _T("黑体"));
    outtextxy((80 - textwidth(_T("开始"))) / 2, (40 - textheight(_T("开始"))) / 2, _T("开始"));


    SetWorkingImage(&id_start);
    setbkcolor(DARKGRAY);
    cleardevice();
    settextcolor(BROWN);
    settextstyle(25, 0, _T("黑体"));
    outtextxy((80 - textwidth(_T("开始"))) / 2, (40 - textheight(_T("开始"))) / 2, _T("开始"));


    SetWorkingImage(&img_exit);
    setbkcolor(LIGHTGRAY);
    cleardevice();
    settextcolor(BROWN);
    settextstyle(25, 0, _T("黑体"));
    outtextxy((80 - textwidth(_T("退出"))) / 2, (40 - textheight(_T("退出"))) / 2, _T("退出"));


    SetWorkingImage(&id_exit);
    setbkcolor(DARKGRAY);
    cleardevice();
    settextcolor(BROWN);
    settextstyle(25, 0, _T("黑体"));
    outtextxy((80 - textwidth(_T("退出"))) / 2, (40 - textheight(_T("退出"))) / 2, _T("退出"));


    SetWorkingImage();
    int yy = max_y / 4 * 3;
    int exit_x = max_x / 2 - 200;
    int start_x = max_x / 2 + 120;
    putimage(start_x, yy, &img_start);
    putimage(exit_x, yy, &img_exit);


    // 检测是否点击相关按钮及按键
    MOUSEMSG msg;
    bool selected = false;
    while (!selected)
    {
      while (MouseHit())
      {
        msg = GetMouseMsg();


        if ((msg.x >= start_x && msg.x <= start_x + 80 && msg.y >= yy && msg.y <= yy + 40 && msg.uMsg == WM_LBUTTONDOWN) || GetAsyncKeyState(VK_RETURN) & 0x8000)
        {
          putimage(start_x, yy, &id_start);
          Sleep(200);
          putimage(start_x, yy, &img_start);
          Sleep(100);
          selected = true;
          break;
        }
        else if ((msg.x >= exit_x && msg.x <= exit_x + 80 && msg.y >= yy && msg.y <= yy + 40 && msg.uMsg == WM_LBUTTONDOWN) || GetAsyncKeyState(VK_ESCAPE) & 0x8000)
        {
          putimage(exit_x, yy, &id_exit);
          Sleep(200);
          putimage(exit_x, yy, &img_exit);
          Sleep(100);
          exit(0);
        }
      }


      Sleep(16);
    }
  }


  // 初始化游戏界面
  void Initialize()
  {
    setbkcolor(BKCOLOR);
    cleardevice();
    setlinecolor(DARKGRAY);
    line(0, 0, 0, max_y - 1);
    line(max_y, 0, max_y, max_y - 1);
    line(0, 0, max_y - 1, 0);
    line(0, max_y - 1, max_y - 1, max_y - 1);


    for (int i = 0; i < max_y; i += 2 * die_top)
    {
      line(i, 0, i + die_top, die_top);
      line(i + die_top, die_top, i + 2 * die_top, 0);
    }


    for (int i = 0; i < plank_num; i++)
    {
      plank[i].plank_y = (i + 1) * plank_gap;
      plank[i].plank_len = rand() % (plank_len_max - plank_len_min) + plank_len_min + 1;
      plank[i].plank_x = rand() % (max_y - plank[i].plank_len);
      plank[i].plank_color = HSVtoRGB(float(rand() % 360), float(1.0), float(1.0));
      plank[i].is_thorn = IsThorn();
      if (plank[i].is_thorn == true)
        plank[i].thorn_x = plank[i].plank_x + rand() % (plank[i].plank_len - thorn_len);
    }


    plank[3].is_thorn = false;
    ball.ball_x = plank[3].plank_x + plank[3].plank_len / 2;
    ball.ball_y = plank[3].plank_y - 1 - ball_r;
    DrawBall(ball.ball_x, ball.ball_y);
    DrawPlank();
    Sleep(sleep_time);
  }


  // 检测是否死亡
  bool IsDead()
  {
    if (ball.ball_y <= die_top + ball_r || ball.ball_y >= max_y - 1 - ball_r || is_dead)
    {
      is_dead = true;
      return true;
    }
    else
      return false;
  }


  // 打印分数,速度及是否开启反向操作
  void PrintScore()
  {
    settextcolor(RED);
    settextstyle(16, 0, _T("黑体"));
    score = time_num / 5;
    TCHAR str[20];
    _stprintf_s(str, _T("当前得分: %d"), score);
    outtextxy(max_y + 5, max_y / 6 * 5 + 10, str);


    if    (score < 50)  plank_dy = 1;
    else if (score < 200)  plank_dy = 2;
    else if (score < 500)  plank_dy = 3;
    else if (score < 1000)  plank_dy = 4;
    else if (score < 1500)  plank_dy = 5;
    else          plank_dy = 6;


    _stprintf_s(str, _T("当前速度: %d/6"), plank_dy);
    outtextxy(max_y + 5, max_y / 2 - 10, str);
    if (score > inverse_operate_score)
      outtextxy(max_y + 5, max_y / 11, _T("反向操作 已开启"));
    else
      outtextxy(max_y + 5, max_y / 11, _T("反向操作 未开启"));
  }


  // 非ESC结束时显示最终分数
  void Finish()
  {
    if (is_dead)
    {
      TCHAR str[50];
      _stprintf_s(str, _T("	     您的最终得分为: %d		"), score);
      MessageBox(GetHWnd(), str, _T("游戏结束"), MB_OK);
    }
  }


  // 游戏运行处理
  void GameRunning()
  {
    // 清除小球和木板
    CleanBall(ball.ball_x, ball.ball_y);
    CleanPlank();


    // 计算木板位置
    for (int i = 0; i < plank_num; i++)
      plank[i].plank_y -= plank_dy;


    // 顶木板是否消失,是 则生成尾木板
    if (plank[0].plank_y < die_top + ball_r + 1)
    {
      for (int i = 0; i < plank_num - 1; i++)
        plank[i] = plank[i + 1];
      plank[plank_num - 1].plank_y = (plank_num)* plank_gap;
      plank[plank_num - 1].plank_len = rand() % (plank_len_max - plank_len_min) + plank_len_min + 1;
      plank[plank_num - 1].plank_x = rand() % (max_y - plank[plank_num - 1].plank_len);
      plank[plank_num - 1].plank_color = HSVtoRGB(float(rand() % 360), float(1.0), float(1.0));
      plank[plank_num - 1].is_thorn = IsThorn();
      if (plank[plank_num - 1].is_thorn == true)
        plank[plank_num - 1].thorn_x = plank[plank_num - 1].plank_x + rand() % (plank[plank_num - 1].plank_len - thorn_len);
    }


    // 计算小球球心 x 位置(加减速效果)
    if ((GetAsyncKeyState(VK_LEFT) & 0x8000) || (GetAsyncKeyState('A') & 0x8000) || (GetAsyncKeyState(VK_LBUTTON) & 0x8000))
    {
      if (score < inverse_operate_score)
      {
        if (left_right == LEFT)
          ball_dx = (ball_dx += ball_ddx) > ball_dx_max ? ball_dx_max : ball_dx;
        else
        {
          ball_dx = ball_dx_min;
          left_right = LEFT;
        }
        ball.ball_x = (ball.ball_x -= ball_dx) < ball_r ? ball_r : ball.ball_x;
      }
      else
      {
        if (left_right == RIGHT)
          ball_dx = (ball_dx += ball_ddx) > ball_dx_max ? ball_dx_max : ball_dx;
        else
        {
          ball_dx = ball_dx_min;
          left_right = RIGHT;
        }
        ball.ball_x = (ball.ball_x += ball_dx) > (max_y - 1 - ball_r) ? max_y - 1 - ball_r : ball.ball_x;
      }
    }
    else if ((GetAsyncKeyState(VK_RIGHT) & 0x8000) || (GetAsyncKeyState('D') & 0x8000) || (GetAsyncKeyState(VK_RBUTTON) & 0x8000))
    {
      if (score > inverse_operate_score)
      {
        if (left_right == LEFT)
          ball_dx = (ball_dx += ball_ddx) > ball_dx_max ? ball_dx_max : ball_dx;
        else
        {
          ball_dx = ball_dx_min;
          left_right = LEFT;
        }
        ball.ball_x = (ball.ball_x -= ball_dx) < ball_r ? ball_r : ball.ball_x;
      }
      else
      {
        if (left_right == RIGHT)
          ball_dx = (ball_dx += ball_ddx) > ball_dx_max ? ball_dx_max : ball_dx;
        else
        {
          ball_dx = ball_dx_min;
          left_right = RIGHT;
        }
        ball.ball_x = (ball.ball_x += ball_dx) > (max_y - 1 - ball_r) ? max_y - 1 - ball_r : ball.ball_x;
      }
    }
    else
    {
      ball_dx -= ball_ddx;
      if (ball_dx > ball_dx_min)
      {
        if (left_right == LEFT)
          ball.ball_x = (ball.ball_x -= ball_dx) < ball_r ? ball_r : ball.ball_x;
        else if (left_right == RIGHT)
          ball.ball_x = (ball.ball_x += ball_dx) > (max_y - 1 - ball_r) ? max_y - 1 - ball_r : ball.ball_x;
      }
      else
      {
        ball_dx = ball_dx_min;
        left_right = STOP;
      }
    }


    // 计算小球球心 y 位置(加速效果)
    int ii = 0;  // 用于确定小球位于哪块木板上方
    while (ball.ball_y - plank_dy > plank[ii].plank_y - 1 - ball_r)
      ii++;


    if (ii < plank_num &&
      ball.ball_x >= plank[ii].plank_x && ball.ball_x <= plank[ii].plank_x + plank[ii].plank_len
      && (ball.ball_y - plank_dy == plank[ii].plank_y - 1 - ball_r || ball.ball_y >= plank[ii].plank_y - 1 - ball_r))
    {
      ball.ball_y = plank[ii].plank_y - 1 - ball_r;
      ball_dy = ball_dx_min;
    }
    else
    {
      ball_dy = (ball_dy += ball_ddx) > ball_dx_max ? ball_dx_max : ball_dy;
      ball.ball_y += ball_dy;
      if (ii < plank_num &&
        ball.ball_x >= plank[ii].plank_x && ball.ball_x <= plank[ii].plank_x + plank[ii].plank_len && ball.ball_y >= plank[ii].plank_y - 1 - ball_r)
      {
        ball.ball_y = plank[ii].plank_y - 1 - ball_r;
        ball_dy = ball_dx_min;
      }
      else if (ball.ball_y > max_y - 1 - ball_r)
        ball.ball_y = max_y - 1 - ball_r;
    }


    // 判断小球是否触碰尖刺
    if (ball.ball_x >= plank[ii].thorn_x - ball_r / 2 && ball.ball_x <= plank[ii].thorn_x + thorn_len + ball_r / 2
      && ball.ball_y == plank[ii].plank_y - 1 - ball_r && plank[ii].is_thorn)
      is_dead = true;


    // 绘制木板和小球
    DrawPlank();
    DrawBall(ball.ball_x, ball.ball_y);
    FlushBatchDraw();
    time_num++;


    // 打印分数,速度及是否开启反向操作
    PrintScore();
    Sleep(sleep_time);
  }


private:
  // 构造函数初始化参数
  RollingBall()
  {
    score = 0;
    die_top = 5;
    time_num = 0;
    sleep_time = 20;
    is_dead = false;
  }


  RollingBall(const RollingBall &rb) {}              // 禁止拷贝构造
  RollingBall &operator = (const RollingBall &rb) {}        // 禁止赋值重载
  int sleep_time;                          // 游戏刷新时间间隔
  int time_num;                          // 记录游戏刷新次数
  int die_top;                          // 顶部尖刺位置
  int score;                            // 记录分数
  bool is_dead;                          // 是否死亡
};






////////////////////////////////////////////////////
// main 主函数
int main()
{
  initgraph(max_x, max_y, NOMINIMIZE);
  srand((unsigned)time(NULL));


  // 获取单例指针
  RollingBall *rb = RollingBall::GetInstance();


  rb->Introduce();
  rb->Initialize();
  BeginBatchDraw();


  while (!(GetAsyncKeyState(VK_ESCAPE) & 0x8000))
  {
    rb->GameRunning();
    if (rb->IsDead())
      break;
    if (_kbhit() && _getwch() == ' ')
      _getwch();
  }


  EndBatchDraw();
  rb->Finish();


  closegraph();
  return 0;
}

大家赶紧去动手试试吧!

审核编辑:汤梓红

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 游戏
    +关注

    关注

    2

    文章

    696

    浏览量

    26040
  • C语言
    +关注

    关注

    180

    文章

    7530

    浏览量

    128722
  • 编程
    +关注

    关注

    88

    文章

    3440

    浏览量

    92400
  • C++
    C++
    +关注

    关注

    21

    文章

    2066

    浏览量

    72900
  • 代码
    +关注

    关注

    30

    文章

    4555

    浏览量

    66771

原文标题:C/C++项目实战:《是男人就下一百层》,530 行源码分享来啦!

文章出处:【微信号:cyuyanxuexi,微信公众号:C语言编程学习基地】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    为什么 C 语言没有被 C++ 取代?

    C++
    电路设计
    发布于 :2022年12月28日 19:17:25

    C语言C++常见学习问题

    C++
    电路设计
    发布于 :2023年01月10日 14:07:23

    C++小白自学基础教程之c++中的三目运算符15

    C++
    电子学习
    发布于 :2023年01月12日 21:11:56

    C++小白自学基础教程之c++类型类型检查加强12

    C++
    电子学习
    发布于 :2023年01月12日 21:19:47

    C++小白自学基础课之简单的C++程序helloworld02

    C++
    电子学习
    发布于 :2023年01月12日 22:27:52

    c++经典书籍

    。书中不但新增大量教学辅助内容,用于强调重要的知识点,提醒常见的错误,推荐优秀的编程实践,给出使用提示,还包含大量来自实战的示例和习题。   对C++基本概念和技术全面而且权威的阐述,对现代C++编程风格的强调,使本书成为
    发表于 10-24 21:06

    编程实战C++快递入门与进阶!

    monitor”开发者在线讲解各种C++开发实战以及分享经历!1.CC++的基本语法的区别2.面向过程向面向对象的转变3.面向对象中各个知识点的关系4.串口软件在自动化监控中的重要
    发表于 08-09 16:21

    C++能用来做什么?

    ,监控工具,包括做的项目也是用了QT开发,有实物出来后,也感觉有了成就感,继续学习也就更加有动力了。如果你学习过C语言,那么其实一开始学习C++是比较容易的,因为C++的基础内容跟
    发表于 08-13 08:00

    Qt Creator如何创建纯C++项目

    Qt Creator如何创建纯C++项目
    发表于 11-09 08:54

    C/C++语言免费分享教程集合 精选资料分享

    C语言(计算机二级必过题库)链接:https://pan.baidu.com/s/1jqgKJaY37MlaE44pIP5RqQ提取码:dd7qVisual C++从入门到精通实战教程(10章)链接
    发表于 07-20 08:04

    如何学习C++,如何学好C++

    最近,很多学员都给我发邮件问我应该如何学习C++,如何学好C++?那么作为一个从C语言小白摸爬滚打、入坑无数到成长为如今的高级C++游戏开发工程师、高级
    发表于 08-20 06:27

    C语言C++运用

    薪资初、中级C/C++软件开发工程师的年薪目前为5万-15万元,高级软件工程师则高达15万-30万元。市场最紧缺的C/C++技术总监或项目
    发表于 11-25 10:47

    如何在新版本中将C项目转换为C++呢?

    我正在尝试将 C 项目转换为 C++。在以前的版本中,属性中有一个“转换为 C++”选项。我在 1.5.1 中找不到这个。如何在新版本中转换为C++
    发表于 01-06 08:13

    使用C++项目的正确方法是什么?

    在 STM32CubeIDE 中,我创建了一个 C++ 项目。但是 `main.c` 仍然以 .c 扩展名生成,并使用 C 编译器编译。这就
    发表于 02-07 08:34

    C/C++项目实战:2D射击游戏开发(简易版)

    关于无阻塞延时,首先,先要 ctime 创建一个 clock_t 变量 a,初始化为 clock(),貌似是自从 1970 年到现在的毫秒数。我们要每隔 0.5 秒执行函数 func() 一次。
    的头像 发表于 01-12 15:57 615次阅读