EasyX精准帧率控制&&打气球小游戏

简介: 之前都用Sleep()来控制画面帧率,忽略了绘制画面的时间如果绘制画面需要很长的时间,那么就不能忽略了。

之前都用Sleep()来控制画面帧率,忽略了绘制画面的时间

如果绘制画面需要很长的时间,那么就不能忽略了。

并且SlQueryPerformanceCounter(&startCount);//获取起始计数,并保存在startCount里面eep()函数也不是特别准确,那么就要精准控制帧率了

🚥🚥🚥🚥🚥🚥

开始时间 结束时间 频率F

LARGE_INTEGER startCount,endCount,F;

获取计数器累加频率

QueryPerformanceFrequency(&F);

获取当前的计数值

QueryPerformanceCounter(&startCount);//获取起始计数,并保存在startCount里面

获取终止计数

QueryPerformanceCounter(&endCount);

计算时差

long long elapse=(endCount.QuadPart-startCount.QuadPart)*1000000/F.QuadPart;//根据LARGE_INTEGER的定义,elapse必须是long long 类型的

查询是否超时

while (elapse < 1000000 / 60){//最长等待时间为60帧每秒
  Sleep(1);//先休眠1ms,防止CPU使用率过高
    QueryPerformanceCounter(&endCount);
    elapse = (endCount.QuadPart - startCount.QuadPart) * 1000000 / F.QuadPart;
}

这时候如果执行代码会发现,虽然我们指定了Sleep(1),但是还是休眠了15ms

这是因为

image.png

要解决这个问题,就要加上

timeBeginPeriod(1);//改变定时器时钟分辨率为1ms
timeEndPeriod(1);

这里如果运行的话会报错,因为

timeBeginPeriod()和 timeEndPeriod()的实现在 库winmm.lib中

解决方法如下

image.png

image.png

把winmm.lib添加到附加依赖项中

image.png

当然,绘制画面也得加上下面的函数

BeginBatchDraw();
EndBatchDraw();

🚥🚥🚥🚥🚥🚥

下面咱们来看一个打气球小游戏,把上面的帧率控制代码进行实操

//看着气球在画面中是分散的,其实可以把他们想象成是在一个数组里面的,然后进行操作
#include<easyx.h>
#include<stdio.h>
#include<math.h>
#define WIDTH 800//窗体宽度
#define HEIGHT 600//窗体高度
#define NUM 5//窗体中的气球数量
#define RADIUS 30//气球半径
//保存气球的各项数据
typedef struct {
  int x;
  int y;
  int r;//半径
  int v;//速度
  COLORREF color;
}balloon;
//生成气球,并初始化
balloon generateBalloon()//因为arrBalloons[]是balloon类型的,所以generateBalloon()也应该是balloon类型的
{
  balloon b;
  int m, n;
  m = 100;
  n = 700;
  b.x = rand() % (n - m + 1) + m;
  b.y = HEIGHT;
  b.r = RADIUS;
  m = 1;
  n = 3;
  b.v = rand() % (n - m + 1) + m;
  b.color = RGB(rand() % 256, rand() % 256, rand() % 256);
  return b;
}
int main()
{
  initgraph(WIDTH, HEIGHT);
  setbkcolor(WHITE);
  cleardevice();
  balloon arrBalloons[NUM];//保存目前窗体出现的气球数量
  for (int i = 0; i < NUM; i++)//给每一个气球赋值(大小,颜色)
  {
    arrBalloons[i] = generateBalloon();
  }
  int current = NUM;//当前出现的气球数量
  int mouseX = 0, mouseY = 0;//鼠标位置
  timeBeginPeriod(1);
  LARGE_INTEGER startCount, endCount, F;
  QueryPerformanceFrequency(&F);
  BeginBatchDraw();
  while (1)
  {
    QueryPerformanceCounter(&startCount);
    cleardevice();
    for (int i = 0; i < NUM; i++)//绘制气球
    {
      setfillcolor(arrBalloons[i].color);
      solidcircle(arrBalloons[i].x, arrBalloons[i].y, arrBalloons[i].r);
    }
    for (int i = 0; i < NUM; i++)//移动气球
    {
      arrBalloons[i].y -= arrBalloons[i].v;
    }
    int i = 0;
    while (i < current)
    {
      int y = arrBalloons[i].y;
      if (y < -RADIUS)//气球飞出窗体
      {
        for (int j = i; j < current - 1; j++)//删除飞出的气球
        {
          arrBalloons[j] = arrBalloons[j + 1];
        }
        current--;
      }
      else
      {
        i++;
      }
    }
    if (current < NUM)//新增气球
    {
      arrBalloons[current] = generateBalloon();
      current++;
    }
    //绘制准心
    setlinecolor(RGB(237, 178, 29));
    setlinestyle(PS_SOLID, 3);
    circle(mouseX, mouseY, 20);
    line(mouseX - 20, mouseY, mouseX + 20, mouseY);//准心使用了mouseX,mouseY,便于准心可以与鼠标指针一起移动
    line(mouseX, mouseY - 20, mouseX, mouseY + 20);
    QueryPerformanceCounter(&endCount);
    long long elapse = (endCount.QuadPart - startCount.QuadPart) * 1000000 / F.QuadPart;
    while (elapse < 1000000 / 60)//最长等待时间为60帧每秒
    {
      Sleep(1);
      ExMessage msg;
      bool isOK = peekmessage(&msg, EX_MOUSE);
      if (isOK = true)
      {
        if (msg.message == WM_MOUSEMOVE)//鼠标移动消息
        {               //更新鼠标位置
          mouseX = msg.x;
          mouseY = msg.y;
        }
        else if (msg.message == WM_LBUTTONDOWN)//鼠标点击消息
        {
          int i = 0;
          while (i < current)
          {
            int ballX = arrBalloons[i].x;
            int ballY = arrBalloons[i].y;
            int distance = (int)sqrt(pow(ballY - msg.y, 2) + pow(ballX - msg.x, 2));
            if (distance < RADIUS)//如果点击到了气球,就把气球删除
            {
              for (int j = i; j < current - 1; j++)
              {
                arrBalloons[j] = arrBalloons[j + 1];
              }
              current--;
            }
            else
            {
              i++;
            }
          }
        }
      }
      QueryPerformanceCounter(&endCount);
      elapse = (endCount.QuadPart - startCount.QuadPart) * 1000000 / F.QuadPart;
    }
    FlushBatchDraw();
  }
  EndBatchDraw();
  timeEndPeriod(1);
  closegraph();
  return 0;
}
//看着气球在画面中是分散的,其实可以把他们想象成是在一个数组里面的,然后进行操作

Code over!

相关文章
|
算法 异构计算 Python
|
C++ 索引 容器
c++string容器-子串获取讲解
c++string容器-子串获取讲解
1295 0
|
应用服务中间件 Linux nginx
|
5月前
|
人工智能 Linux iOS开发
OpenClaw+QMT‑MCP量化交易实战:AI交易员全流程部署、模型配置与自动交易实现(附阿里云/Windows/macOS/Linux部署OpenClaw教程)
在量化交易领域,自动化执行与策略智能化已成为主流方向。OpenClaw(Clawdbot)作为开源AI Agent框架,可充当交易系统的“大脑”,负责理解指令、分析行情、拆解逻辑、规划执行;QMT‑MCP则遵循MCP(Model Context Protocol)协议,将本地QMT交易客户端封装为标准接口,成为AI交易员的“执行双手”,完成下单、撤单、查询持仓、查询资产等真实交易操作。
3250 7
|
5月前
|
人工智能 安全 Linux
阿里云/本地部署OpenClaw 及效率翻倍秘籍:10大核心Skill安装、与百炼大模型对接实战教程
很多用户在部署完OpenClaw(Clawdbot,社区昵称“小龙虾”)后,都会陷入“觉得不好用”的误区——问问题像普通聊天AI,没有实际生产力价值。其实问题不在OpenClaw本身,而在于你没有解锁它的核心玩法:**Skill技能扩展**。OpenClaw就像一台顶配电脑,默认状态下只装了基础系统,只有装上对应的“专业软件”(Skill),才能从“聊天工具”变身“全自动生产力助手”。
1066 5
|
C++
C++编程技巧:sort()函数中的greater<int>()参数使用讲解
所以你看,`sort()`函数和 `greater<int>()`的组合就像一个魔法工具箱,可以轻松地完成从大到小的排序任务。希望这个小小的技巧能为你的C++编程之路增添一份乐趣。
718 23
|
安全 网络虚拟化 数据安全/隐私保护
华为ensp模拟器 配置ACL访问控制列表
华为ensp模拟器,模拟配置acl访问规则,配置acl访问规则的详细解释和操作。
1964 0
华为ensp模拟器 配置ACL访问控制列表
|
C语言
C语言中的函数定义与调用详解及代码实例
C语言中的函数定义与调用详解及代码实例
912 0
|
Web App开发 存储 前端开发
《Chrome谷歌插件Top10》开发最好用的谷歌插件
本文介绍了多个实用的浏览器插件及其安装方法。包括CSDN浏览器助手,提供高效开发工具;FeHelper,前端必备工具,支持格式化、压缩等功能;uBlock Origin,有效屏蔽广告和弹窗;PageLiner,网页标尺工具,便于前端设计;Fatkun,批量下载图片;Smallpdf,文件转换工具;Octotree,GitHub代码树插件;Awesome Screenshot,截图与录屏工具;ColorZilla,颜色拾取器;Dark Reader,暗黑模式阅读插件。安装方式有通过Chrome商店搜索或下载crx插件本地安装。
847 11
|
测试技术 开发工具 git
掌握Git分支管理,提升团队协作效率
掌握Git分支管理,提升团队协作效率
421 0

热门文章

最新文章