引擎之旅 Chapter.1 高分辨率时钟
我们如何理解时间。在现实生活中,时间就是一个有方向的直线。从一个无穷远到另一个无穷远。用数学去抽象地思考,它就是一个从无穷小到无穷大的一维轴。
那么,对于游戏而言,我们需要考虑关于时间的哪一部分?游戏本质就是开发者作为上帝创造的产物,在我们人为创造的世界中,自我们开始游戏的时候,这个世界的所有物体就应该根据一个既定的时间线作为参照物来进行运动——这个标准不是服务于策划文案中的年代、一天的早中晚等写实的时间线,而是动画、逻辑等相关仿真运行的参照。
那么我们应该如何去用代码实现游戏中的时间线呢?我们先对游戏中的时间线进行分析。
游戏中的时间线
真实时间线
真实的时间线指的是和现实的时间线的频率保持一致的时间线。首先我们先明确一点,对于未运行的游戏而言,时间线是没有意义的,因此,游戏中真实的时间线是以软件启动的时刻作为起点,而以软件关闭作为时间线的终点。
游戏引擎一般将真实时间线作为引擎的全局时间线,即全局时钟。全局时钟是游戏时间线的基石,若全局时间线出问题,则游戏所有与时间相关的操作都会发生错误。因此,它的频率严格一致,计时应当是高精度的,且不随游戏的其他因素的变化而变化。此模块的代码将置于核心代码中的最底层,并对其修改操作进行严格的限制。
游戏时间线
游戏时间线依托于真实时间线(全局时钟)。根据不同的需求(一般指快进和减速),游戏中将拥有各种各样的时间线,这些时间线服务于相对应游戏物体的运动和变化。相较于全局时钟严格的不可变性,游戏时钟可以很好的实现设计者的如下需求:
- 游戏暂停
- 游戏恢复
- 人物动画加速
- 人物动画减速
- 时间回溯
- ...
全局时钟的实现方式
大多数操作系统都提供获取系统时间的函数,但这些函数都是低分辨率的,精确到秒或毫秒,则对于全局时钟而言,是不满足要求的。那么,我们应该如何实现呢?CPU恰好提供了硬件级别的高精度时钟供我们去计时。
CPU的高分辨率计时器:每个CPU都存在一个计时器,其功能就是将计算机自启动或重置起经过CPU的周期数记录到对应的寄存器中。
不同硬件访问CPU寄存器的方式是不同的,幸运的是Windows这一个巨大的抽象层帮助我们忽略了各式各样硬件的差异。在Windows API中,获取计时寄存器的相关函数如下:
- QueryPerformanceCounter():计时寄存器数值
- QueryPerformanceFrequency():每一秒计时器递增的次数(即计时器频率)
利用这两个函数,我们可以实现一个高精度的计时器作为游戏引擎的全局时钟。极简代码如下所示。
#include "Windows.h"
class Timer
{
public:
Timer()
{
QueryPerformanceFrequency((LARGE_INTEGER*)&m_Frequence);
QueryPerformanceCounter((LARGE_INTEGER*)&m_StartStamp);
}
//获取至生成计时器以来运行的秒数
double RunSeconds()
{
__int64 current=0;
QueryPerformanceCounter((LARGE_INTEGER*)¤t);
return (current-m_StartStamp)/(double)m_Frequence;
}
private:
__int64 m_StartStamp;
__int64 m_Frequence;
}
引擎之旅 Chapter.1 高分辨率时钟的更多相关文章
- 引擎之旅 Chapter.4 日志系统
关于近段时间为何没有更新的解释:Find a new job. 目录 引言 日志语句的分类 控制台窗体 和 VSOutput Tab的日志打印 存储至特定的文件中 展示堆栈信息 引言 一般来说,一个优 ...
- 引擎之旅 Chapter.2 线程库
预备知识可参考我整理的博客 Windows编程之线程:https://www.cnblogs.com/ZhuSenlin/p/16662075.html Windows编程之线程同步:https:// ...
- 引擎之旅 前传:C++代码规范
自己以前写代码时,一个项目一个风格.单人开发的工作使得我并没有注意到代码规范性和可读性的问题.每当项目结束后,看到自己杂乱无章的代码,完全没有二次开发和重构的欲望. 写代码就应该像写诗一样优雅. by ...
- Linux时间管理涉及数据结构和传统低分辨率时钟的实现
上篇文章大致描述了Linux时间管理的基本情况,看了一些大牛们的博客感觉自己写的内容很匮乏,但是没办法,只能通过这种方式提升自己……闲话不说,本节介绍下时间管理下重要的数据结构 设备相关数据结构 // ...
- linux 时间管理——概念、注意点(一)【转】
转自:http://www.cnblogs.com/openix/p/3324243.html 参考:1.http://bbs.eyeler.com/thread-69-1-1.html ...
- 14.1.1 InnoDB as the Default MySQL Storage Engine
14.1 Introduction to InnoDB 14.1.1 InnoDB as the Default MySQL Storage Engine 14.1.2 Checking InnoDB ...
- 初探linux子系统集之timer子系统(二)
想着博客中还没有翻译过一篇文章,虽然英文水平有限,但是借助google翻译慢慢地翻译出一篇文章也是不错的选择.那就来学习下hrtimer的文档吧,翻译的略搓,可以直接跳过这篇,这里仅作为学习的过程!^ ...
- c++ 常用头文件
1.#include<iostream> iostream 的意思是输入输出流.#include<iostream>是标准的C++头文件,任何符合标准的C++开发环境都有这个头 ...
- Linux内核入门到放弃-时间管理-《深入Linux内核架构》笔记
低分辨率定时器的实现 定时器激活与进程统计 IA-32将timer_interrupt注册为中断处理程序,而AMD64使用的是timer_event_interrupt.这两个函数都通过调用所谓的全局 ...
随机推荐
- NC16618 [NOIP2008]排座椅
NC16618 [NOIP2008]排座椅 题目 题目描述 上课的时候总有一些同学和前后左右的人交头接耳,这是令小学班主任十分头疼的一件事情.不过,班主任小雪发现了一些有趣的现象,当同学们的座次确定下 ...
- while循环--和do-while循环
对于循环语句来说他会有一个回上去的箭头,这个回上去的箭头就形成了一个重复做的事情,那种重复做的事情我们就叫做循环 while循环 ~如果我们把while翻译作"当",那么一个whi ...
- 攻防世界MISC—进阶区32—37
32.normal_png 得到一张png,扔进kali中binwalk 和 pngcheck一下,发现CRC报错 尝试修改图片高度,我是把height的2改为4,得到flag 33.很普通的数独 得 ...
- 千万不要把Request传递到异步线程里面!有坑!
你好哇,我是歪歪. 前几天在网上冲浪的时候看到一篇技术文章,讲的是他把一个 request 请求传递到了线程池里面,然后遇到了一个匪夷所思的情况. 他写了这篇文章,把自己针对这个问题的探索过程分享了出 ...
- [HNOI2009]【一本通提高组合数学】有趣的数列
[HNOI2009]有趣的数列 题目描述 我们称一个长度为 2 n 2n 2n 的数列是有趣的,当且仅当该数列满足以下三个条件: 它是从 1 ∼ 2 n 1 \sim 2n 1∼2n 共 2 n 2n ...
- ESP8266 使用 DRV8833驱动板驱动N20电机
RT 手里这块ESP8266是涂鸦的板子,咸鱼上三块一个买了一堆,看ESP8266-12F引脚都差不多的.裸焊了个最小系统,加两个按钮(一个烧录,一个复位) 1. 准备工作 搜索过程中发现 DRV88 ...
- Netty源码解读(一)-前置准备
前置条件 源码版本netty4.1 了解Java NIO.Reactor模型和Netty的基本使用. 解释一下: Java NIO:了解BIO和NIO的区别以及Java NIO基础API的使用 Rea ...
- 从零开始Blazor Server(1)--项目搭建
项目介绍 本次项目准备搭建一个使用Furion框架,Blazor的UI使用BootstrapBlazor.数据库ORM使用Freesql的后台管理系统. 目前的规划是实现简单的注册,登录.增加管理员跟 ...
- 手动注入bean到spring容器
ApplicationContext applicationContext = SpringContextUtils.getApplicationContext(); //将applicationCo ...
- 【AGC】开放式测试示例
前言:这一次我们来带大家进入AGC领域,学习AGC相关的知识.所谓AGC就是AppGallery Connect的简称,为用户的应用进行创意.开发.分发.运营.经营各环节提供一站式服务,说的通俗一点, ...