引擎之旅 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.这两个函数都通过调用所谓的全局 ...
随机推荐
- ansible管理windows主机
1. 在windows开启winrm winrm service 默认都是未启用的状态,先查看状态:如无返回信息,则是没有启动: winrm enumerate winrm/config/listen ...
- MySQL十种锁,一篇文章带你全解析
MySQL有两个核心的知识点,索引和锁.前几篇文章已经详细讲解了MySQL索引实现机制,今天再一起学习一下MySQL的锁. 1 为什么要加锁? 当多个事务并发操作同一批数据的时候,如果不加锁,就无法保 ...
- static关键字续、继承、重写、多态
static关键字 1.对于实例变量,每个java对象都拥有自己的一份,存储在堆内存当中,在构造方法执行的时候初始化. 2.所有对象都拥有同一个属性时,并且值相同,建议声明为static变量. 3.静 ...
- 【小程序自动化Minium】二、元素定位-Page接口中的 get_element() 与 get_elements()
UI自动化中的重要工作就是元素定位了,高效精准的定位方法可以让工作事半功倍. 在过去的一段web自动化经历中,使用的selenium库支持了多种定位方法,我们可以利用这些定位方法来做进一步封装,写出符 ...
- Systemverilog-- OOP--对象的拷贝
目录 浅拷贝: 定义拷贝函数: 拷贝函数总结: 浅拷贝: Packet p1; Packet p2; p1 = new; p2 = new p1; 在创建p2对象时,将从p1拷贝其成员变量例如 i ...
- halcon变量窗口的图像变量不显示,重启软件和电脑都没用
有幸遇到halcon变量窗口的图像变量不显示,重启软件和电脑都没用这个沙雕问题,也是找了蛮久才发现解决办法特意记录一下. 这是正常情况下的窗口(左边)和图像变量不显示的窗口(右边): 解决方法: 鼠标 ...
- 坐标PCB公司,想做实时数仓、推生产线看板,和Tapdata Cloud的偶遇来得就是这么凑巧
Tapdata Cloud 是一款很有「前途」的产品.--Tapdata Cloud 用户 | 一线DBA@某PCB全球百强企业 从首次提出这一概念起,已经 10 年过去了,"工业互 ...
- day11 - 多线程
1内容 进程.线程介绍 Java中 线程的实现方式 Thread 类 Runnable 接口 Callable 接口 线程相关的方法 线程安全问题 - 同步技术 线程等待唤醒机制 进程(Process ...
- 从傅里叶级数(Fourier series)到离散傅里叶变换(Discrete Fourier transform)
从傅里叶级数(Fourier series)到离散傅里叶变换(Discrete Fourier transform) 一. 傅里叶级数(FS) 首先从最直观的开始,我们有一个信号\(x(t)\)(满足 ...
- java 九九乘法表(for循环)
package study5ran2yl.study; public class ForDemo01 { public static void main(String[] args) { int h; ...