惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
T
Tenable Blog
Webroot Blog
Webroot Blog
L
Lohrmann on Cybersecurity
S
Securelist
S
Schneier on Security
NISL@THU
NISL@THU
Know Your Adversary
Know Your Adversary
C
Cybersecurity and Infrastructure Security Agency CISA
T
The Exploit Database - CXSecurity.com
L
LINUX DO - 热门话题
C
CXSECURITY Database RSS Feed - CXSecurity.com
O
OpenAI News
I
Intezer
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
TaoSecurity Blog
TaoSecurity Blog
S
Secure Thoughts
Application and Cybersecurity Blog
Application and Cybersecurity Blog
P
Privacy International News Feed
H
Hacker News: Front Page
N
Netflix TechBlog - Medium
M
MIT News - Artificial intelligence
博客园 - Franky
PCI Perspectives
PCI Perspectives
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Microsoft Azure Blog
Microsoft Azure Blog
MongoDB | Blog
MongoDB | Blog
L
LangChain Blog
P
Proofpoint News Feed
S
Security Affairs
WordPress大学
WordPress大学
The Last Watchdog
The Last Watchdog
S
SegmentFault 最新的问题
小众软件
小众软件
F
Full Disclosure
博客园 - 叶小钗
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
T
The Blog of Author Tim Ferriss
Simon Willison's Weblog
Simon Willison's Weblog
P
Palo Alto Networks Blog
Security Latest
Security Latest
P
Proofpoint News Feed
月光博客
月光博客
T
Tailwind CSS Blog
Scott Helme
Scott Helme
Hacker News - Newest:
Hacker News - Newest: "LLM"
Google Online Security Blog
Google Online Security Blog
T
Threat Research - Cisco Blogs
Help Net Security
Help Net Security
Project Zero
Project Zero

机核

游戏性能旗舰最强之选,一加 Ace 6 至尊版国补到手价2999元起 6元钱自己更换电动车刹车线 《生化危机9:安魂曲》编剧Haris Orkin专访 摸金游戏?音乐游戏!暗区新赛季这把能弹的琴有何来历? 好评国产武侠SRPG《息风谷战略》免费DLC现已推出 | 机核 GCORES 碎片 《生化危机:安魂曲》将于5月实装首个免费更新内容 | 机核 GCORES 新锐东方游戏,谱写世界新章! 沉浸式恋爱视觉小说游戏《心象演算》免费试玩版现已正式上线 | 机核 GCORES 互动影游《代号三国:龙起》上线!穿越三国与曹操并肩、与佳人同行、与权谋博弈! 《老头收集梦想生活》,游戏酒桌会6,录音笔VOL.689 | 机核 GCORES 破界·共生——《白日梦:无限世界》五大核心游戏特点解析 价格已到史低,锐龙5 9600X/锐龙7 9700X正适合抄底 时间循环之旅即刻启程!《归环》一周目测试今日开启 热门在线韩游变魂游,洛奇衍生作能否打破“花瓶”魔咒? LG UltraGear evo 全新高端显示器系列:当“5K”遇见“AI”,不止强大,更懂热爱 反套路三国互动影游《代号三国:龙起》今日上线! 愿望单登记人数突破10万!备受瞩目的“女儿养成游戏” 《梦幻魔法公主》今日于Steam平台上线!限时八折优惠中 《黑神话:悟空》全球音乐会2026巡演将于4月29日12时开票 | 机核 GCORES “Snowguelike”生存肉鸽挖矿新作《蛙穿雪境》公布发售日期,5月7日正式上线 | 机核 GCORES 重塑移动办公、AI创作新境!全新华硕灵耀Air系列、ProArt 骁龙版震撼首发,创芯未来 均分88:《Saros》媒体评分汇总 | 机核 GCORES 上海烛龙公布合作遗迹探险游戏《吉时已到》首支预告片 | 机核 GCORES 首个独立游戏《萝薇日记》已上线Steam! 烛龙新IP《吉时已到》首曝,打造国内首款中式合作遗迹探险游戏 喜加一:《暗黑破坏神Ⅳ》国服现已开启限时免费领取本体活动 | 机核 GCORES 新版《生化危机》电影定于9月18日上映,官方网站现已上线 | 机核 GCORES 《冲就完事模拟器2》“星球大战”联动DLC正式宣布 | 机核 GCORES SteamController将于5月4日发售,售价99美元 | 机核 GCORES 事已至此,内存这么用也算省钱了,“2+1”非对称双通道应用实测 505游戏母公司现已收购《明末:渊虚之羽》IP | 机核 GCORES 基石 手动杂谈12|格斗游戏也能讲好故事 | 机核 GCORES I Love You Mr Snowball 我的向日葵小姐 会比GTA6先发售吗?最硬核的生存游戏《DAYZ》要推新DLC了 周记02:在2026遇到新怪谈 什么硬件,能让游戏Loading界面快速消失? | 机核 GCORES GadioSpec《百年风云世界杯》免费试听集 | 机核 GCORES 百年风云世界杯Vol.1丨足球崛起 | 机核 GCORES 100年前,一群人提前替我们经历了AI恐惧 | 机核 GCORES 《呼啸山庄》2026 ——一辆当代艺术的大卡车冲撞了我的大脑 手游《天穗之咲稻姬:日之香巡灵传》宣布将于7月27日停服 | 机核 GCORES 集结梦之队,征战世界杯,《最佳球会ONLINE》上线Steam 山水绝景随心拼 休闲建造游戏《千里山河录》Steam商店页公开 巫师帽、法袍、魔杖,为什么它们是影视、游戏里的法师必备三件套? 《无鞘信使》-第一章 复古风自动战斗肉鸽《终结之终结》Steam商店页面现已上线 电脑里有一款不破不立的MMO,录音笔VOL.688 | 机核 GCORES 《生化危机》30周年纪念周边发售,这次是真的“保护伞” 可靠耐用+AI全能,惠普战66 2025锐龙版深度体验 《时之书:无尽终章》关卡“大航海时代”全球首次公开 经典名作《乌龙派出所》改编经营模拟游戏《乌龙派出所~阿两的商店街物语~》正式宣布支持简体中文 明日开冲,解锁反套路三国互动影游《代号三国:龙起》即将上线 独立游戏《这是我的宝藏!》已发售~ 降低难度不是唯一解,无压力死亡也是好体验 Netflix官宣新片《普通人》:讲述韩国现代史上的权力风暴 《生化危机:安魂曲》全球销量现已突破700万份 《绝地鸭卫》PC版5月15日正式发售 亡妻回忆录?女性向情感叙事游戏《S-mail》现已正式发售 全新酷黑风格,酷睿Ultra 200S PLUS的高性价比搭档!七彩虹BATTLE-AX B860M-PLUS S WIFI7 V20 超级黑刃主板测评 胖狗 索尼发布了港台地区PS5产品价格调整公告,将于5月1日起实施 二次元怪猎+性感美女!《碧蓝幻想:无尽黄昏》开启Beta公测 这款怪谈类型中式恐怖游戏居然更新了?! 《百日战纪 -最终防卫学园-》改编漫画将于今年冬季开启连载 《如被附身,请致电我们》:匈牙利黑色幽默恐怖小说 《竹屿山房杂部卷五》(译文) 大树 超越引擎 摄影分享丨四月 碎片杂记vol.72 当老式FPS与老式动画技术碰撞出新时代的火花—《神探杰克鼠》 想做独游,如何避免首个项目就褒姒? 在线多人动作游戏《OCTOPinbs》将于5月12日上线Steam! 《酒鬼女神的酒诡》确定将于2026年登陆Steam 《CRYMELIGHT》将于11月5日(周四)正式发售!4月25日开始预购! 钢铁国度MKI部落Evolution,蛮兵部分 【钢铁国度】部落-Primal MK I,蛮兵部分 这是一个高中生用ai跑出来的作品,我自称他为物理神话 钢铁国度MKI部落Metamorphosis,蛮兵部分上 钢铁国度MKI部落Metamorphosis,蛮兵部分下 INDIE Live Expo于4月25日举办:首发9款新作,超200款独立游戏亮相 暗黑卡牌策略新作《魔忌:穷鼠啮狸》发布全新中文试玩版和发售预告片,4月30日正式上线在即 战锤40K长篇小说:变节者・苦难主宰(三)(全书完) 战锤40K长篇小说:变节者・苦难主宰(二) 战锤40K长篇小说:变节者・苦难主宰(一) 英语语言学习丨短语专题1:短语的特点 唯一的EVE战争 【少前同人】【M200】战术人形会梦见音乐会吗 【昏迷3】即将发售,前作主角悉数到场!“恶灵”宋老师化身可操控角色 业内人士:游戏公司“十有八九”使用生成式AI,包括卡普空 欢庆一周年:《光与影:33号远征队》全新纪念艺术图、超值折扣与免费更新同步上线 才刚刚开始呢 【抽奖】《星际卡车司机》推出免费大型更新,四折平史低折扣进行中 四人合作FPS游戏《佣兵猎手》抢先体验重大更新 1 现已上线 Raw Fury新作《深馅地牢 Deep Dish Dungeon》将于今秋加入 XGP 塔防幸存者游戏《魔怪来袭》推出首个 DLC 《饿狼传说:群狼之城》1周年纪念!新DLC“沃尔夫冈·克劳萨”今日参战 猫狗相伴 欢乐闯关 双人合作平台跳跃游戏《猫狗同行》上线Steam商店页 《同行:月球逃脱》(Together: Moon Escape)上线将于明日上线Steam
节奏游戏开发指南 #0:如何拆除原子弹
游戏古登堡计划 · 2019-01-27 · via 机核

这是一篇写于几年前的文章,重新发布在机核网,除了分享目的外,也希望能借此宣传一下这款国内玩家还并不熟悉的独特音乐游戏。

本文原载于游戏开发论坛TIGsource,此处为授权译文,记载和分享了7th Beat Games开发节奏类游戏的经验与心得。本文是系列的第一篇。文中作为范例的游戏叫做《冰与火之舞》(A Dance of Fire and Ice),是一款难度很高的单键节奏游戏,巧妙地将几何变换与音乐节奏两种元素融为一炉。

这里可以找到一个比较早期的试玩版本:这里。  steam上则可以找到未来还会继续更新的正式版本:这里

开发者 Hafiz Azman 来自 7th Beat Games 工作室。他们的另外一款游戏参与过前两次北京核聚变的小伙伴可能会有一些印象:仍在开发当中的《节奏医生》(Rhythm Doctor)。7thbeat Games 工作室一直专注于节奏游戏的开发,团队成员的实力雄厚:美术 Kyle 曾绘制过网络漫画 Soul Symphony,而《冰与火之舞》这款游戏的音乐制作人 Jade 目前则就读于伯克利音乐学院(Hafiz 也参与了一部分作曲工作)。有兴趣的读者可以前往他们的itch.io 页面了解更多信息。

下文均以 Hafiz 的口吻来叙述。

我已经尝试制作过好几款节奏游戏。实话,这也是我唯一真正投入其中并一直想做的游戏类型。最初尝试制作这类游戏时,我发现很少有文档涉及节奏类游戏的一般架构。因此,我将会以自己的一款节奏游戏《冰与火之舞》(A Dance of Fire and Ice)为例,向读者介绍一些简单粗暴但非常有效的技术,来展现我是如何架构此类游戏的。

大家可以看一下冰与火之舞的宣传片,它能演示这款游戏的基本玩法和机制:

节奏游戏法则第 1 条

节奏游戏需要专门编写一个类来负责保持节奏

在我自己的游戏中,一般会给这个类其名为 Conductor(中文意思即指挥家)。

这个类需要提供一个简单的成员函数/变量来标注乐曲位置,以便用到游戏中需要和节奏同步的一切事物上。以示范游戏为例,Conductor 类拥有一个名为 songposition 的成员变量,它可谓游戏中其他一切的基石。

// Conductorint crotchetsperbar = 8; public float bpm = 180; public float crotchet; public float songpostion; public float deltasongpos; public float lasthit;// = 0.0f; //上次按键的时间(已与拍子对齐) public float actuallasthit; float nextbeattime = 0.0f; float nextbartime = 0.0f; public float offset = 0.2f; //调整歌曲开头的位置 public float addoffset; //针对每首乐曲单独的调整值 public static float offsetstatic = 0.40f; public static bool hasoffsetadjusted = false; public int beatnumber = 0; public int barnumber = 0;

上面列出了 Conductor 类中的成员变量。其中一部分是专门用于我这款游戏的,但很多都是节奏游戏中通常会用到的:

  • bpm -- 用于指定乐曲的 bpm (即每分钟节拍数);

  • crotchet -- 指定四分音符(crotchet)时长,通过 bpm 计算得出;

  • offset -- 偏移量,非常重要的一个变量,因为事实上 mp3 文件开始处总是会存在微小的间隙,文件开头会用于存放一些数据(包含如艺术家名字,曲名等等的信息);

  • songpostion -- 乐曲位置,一个应当直接从 Audio 对象的对应变量获取“值”的变量。每个引擎中对应的变量不同,对 Unity 来说,我们可以使用 AudioSettings.dspTime。而我的做法是,在播放乐曲的每一帧都记录变量 dspTime的值并将其赋给 songpostion,这样在的乐曲为止变量每一帧都会像下面的代码这样设置:

songposition = (float)(AudioSettings.dspTime – dsptimesong) * song.pitch – offset;

附注:Unity 有一个内建的变量 song.pitch 用于指定正在播放的乐曲的速度。将其作为计算乐曲位置变量的因数,我就能够在改变乐曲播放速度的同时仍然保持节奏同步。利用这个特性我把游戏里的所有乐曲的速度都下调了 20%,因为编完曲后我才发现难度设置得有些略高了。

总而言之,这样一来,Conductor 类就初步设置完毕了,接下来我们来研究如何让对象来与它同步。

节奏游戏法则第 2 条

所有需要同步的对象仅使用乐曲位置进行同步,不使用其他任何方法。

这里的意思是指,不要用定时器,不要用补间方法。这些方法都无法持续工作。

随着帧更新的自增定时器(例如将它放在 Update 函数中)并不靠谱,只要 FPS 不稳就会导致一切都毁掉。

统计离逝时间的函数也依然不够精准,尤其是当我们出于某些原因需要快进或者跳过歌曲的时候,也会出现严重问题。

所以说,只考虑使用乐曲位置变量,不要用定时器。

(设计心得:尽可能让游戏里的所有元素都随着节拍舞动!让整个游戏都变得动感!)

但在那之前,还有一件非常微妙的事情你需要予以关注——这也是我最开始被困扰之处。

你应该有注意到,即便我们打算把乐曲位置变量用到所有需要同步的游戏元素中,我们也还是需要一些用与检查乐曲位置的参考点:比如,所有的乐曲都会需要用乐曲位置变量去检查乐曲起始处的原点。

来举一个实际的例子吧,现在有四道光,你想要让它们在乐曲的头四个节拍处亮起来。于是你编写了一个名为 Spotlight (聚光灯)的类的脚本。

代码如下:

    int beatnumber = 1; //或者 2 或者 3 或者 4     bool islitup = false;     float bpm = 140;     float crotchet;  //四分音符长度     void Start(){      crotchet = 60 / bpm;     }     void Update(){      if (Conductor.songposition > crotchet * beatnumber)      islitup = true;     }

但有时候你需要的并非只执行一次的动作,而是周期执行的动作。尝试引入这种系统的时候经常会造成节拍同步误差。而我从这些惨痛教训里学到的最宝贵但非常简单的经验是:

节奏游戏法则第 3 条

不要随意更新参考点。只对它进行增量。

仅仅给出一条抽象的概括还是略显微妙,我们依然还是结合实例来说明。我们希望每个拍子都伴随一次闪光,而不是只触发一次效果。下面这种实现看起来比较简单……然而,它是错误的,你能看出来原因吗?

代码如下:

    float lastbeat; //这就是那个“会动的参考点”     float bpm = 140;     void Start(){         lastbeat = 0;         crotchet = 60 / bpm;     }     void Update(){         if (Conductor.songposition > lastbeat + crotchet) {             Flash();             lastbeat = Conductor.songposition;         }     }

字面上就五行代码。看起来没什么问题对吧?每当我们需要移动到下一个拍子上,我们就把参考点设置为当前时间,然后等下一个拍子经过。

但是……这样不行!这样想当然地做下去最后准得哭鼻子。会有越来越多没和拍子同步的闪光亮起来,每拍都可能会与节拍错开最多六十分之一秒。(哪里出问题了呢,我这里已经提示地非常明显了)。

问题精确地反映在了我上面列出的原则里:不要随意更新参考点。只对它进行增量。

我们将当前歌曲位置赋值给上一拍的做法正是我说的“随意更新参考点”的行为。问题在于,你的游戏总是工作在特定帧率下,比如 60 fps,因此在一秒内也最多只能检查 60 次。这样一来,当返回状态为真时,你可能刚好错开了六十分之一秒。这时,你赋给的上一拍 lastbeat 的时间点并非真正的上一拍,而是接下来的一拍!

因此,正确的做法是什么呢?像我反复强调过的那样——只增量不赋值:

代码如下:

float lastbeat; //这就是那个“会动的参考点” float bpm = 140; void Start(){ lastbeat = 0; crotchet = 60 / bpm; } void Update(){ if (Conductor.songposition > lastbeat + crotchet) {      Flash();      lastbeat += crotchet;// 关键差别在这里 } }

这条原则虽然简单但很重要。

将上述原则运用到游戏中

说实话,这些技巧在我去年开始制作第一款节奏游戏的时候就早已经掌握了,我比较在意的是如何呈现更加复杂的场景。

你会留意到,在我的游戏中,两个星球互相环绕飞行,并且遵循乐曲播放速度:半圈恰好为一拍。当玩家按下按钮时,环绕飞行的星球和不动的星球会交换角色。因此,如果玩家每拍按一次按钮,环绕的双星会优雅地走出一条笔直的线条。

在某一帧内:若乐曲位置在 0° 的上一拍处,那么接下来一拍应该落在上一拍时间点加上 180° 的四分音符长度处,因此,转角的增量应该是 (deltaTime / crotchet) * 180 degrees。

这样,每经过一个四分音符,我们移动 180° 就可以了。似乎并不麻烦!

难点在于,玩家按键并非精准地落在拍子上(当然那几乎不可能做到)。这款游戏基于网格——为了提供充分的乐趣,不必要求玩家一定要精准地抓住时机,略微早点或晚点都问题不大。因此,问题在于,我应当如何将星球对齐到格子中,不让一切东西都发生偏移。

(天才如)我想出一个蛮厉害的方法来解决这个问题:在按键同时,游戏可以完成许多事情:

  • 记录正在移动的星球的位置转过的角度,将它对齐到网格中(如果双星沿着直线运动,那对齐角度应为 180°)。

  • 将转动星球对齐到网格,并将其作为锚点(不动的星球)。

  • 而之前作为锚点不动星球,略微调整它的起始角度(令其抵消掉上一次转动星球超前或延迟的角度),并将其设为转动星球。

按键前的一帧: 

一帧后: 

目前来看,这种方法效果非常出色 - 通过抵消上一拍的误差,接下来的一拍会总是依然保持在 180° 的位置!

这种方法具体如何实现呢?可得费一番功夫!

让一切可以动的东西同步起来

一开始,一切都很同步,效果很时髦,但随着乐曲继续播放,同步率开始越来越糟。如果你已经读过前面的说明,应该知道为什么游戏会慢慢变得不同步。

是的,这是因为这种写法违背了我之前列出的一条原则:所有需要同步的对象仅使用乐曲位置进行同步,不使用其他任何方法。在上面的例子中,我在更新星球角度时对比了 deltaTime 和乐曲位置。这是错误的做法。

但是,当我试图直接使用每帧的乐曲位置变化值 timeDifference 来替换 deltaTime,却发现,问题依然没有解决!情况变得有些复杂起来。

原因实在微妙:在增加每帧角度时,我隐式地使用乐曲位置作为前一帧的“参考时间点”。这个参考时间点每次的增量取决于两帧的间隔。经过这些计算的过程中,逐渐积累的微小误差会让结果慢慢偏离正确的数值,游戏也随之不再同步。

(是的,节奏游戏的开发就是这样棘手。在制作节奏游戏的过程中,确保你的游戏引擎能够保持毫秒级的精准是尤为关键的。臻于完美的过程需要花费了大量时间来仔细调整,但这种付出是完全值得的。)

最终我想办法修复了这个问题,通过遵守下面这条金科玉律:使用一个不依赖帧率的方法来计算参考时间点的增量。

这个终极解决方案的注意事项如下所列:

  • 不要使用两帧间隔时间来计算角度增量了。而是,使用插值法!记录上次双星交换角色时的乐曲位置(即所谓的 lasthit)以及此时星球转过的。这样,每帧转角的计算代码可以使用下面的方法:  

angle = snappedlastangle + ((conductor.songpostion - conductor.lasthit) / conductor.crotchet) * Mathf.PI * controller.speed; 

  • 如何解决玩家无法精准按键的问题:不要将 lasthit 作为按键时机,而是将其作为按键的最后期限。换言之,lasthit变量只随着节拍增加。这样一来,就完全排除了随意更新参考点作为计算值的问题(就像之前曾提到过那个随着节拍闪光的问题)。也就是说,玩家按键输入的确切时间并不参与我们的计算,这样许多麻烦的问题也就迎刃而解了。

(这里就不再具体涉及如何计算按键应当按下的时间段,这基本上只是一个数学问题,实践起来也不想转角或者几何学那样有趣,如果你真的很感兴趣,也欢迎前往 TIGsource 论坛原帖咨询我。)

结语

第一篇教程就到这里结束了。希望这篇入门教程,能向大家揭示节奏游戏开发中这个非常关键的秘密:细小的时间差别就会造成巨大的效果反差。后续的几篇教程我们也会持续整理发表过来。如果大家有朝一日也开始制作自己的节奏游戏,也可以和我们一起来交流心得噢。也欢迎大家尝试一下《冰与火之舞》的发售版本,看看我们最终做出了怎样的效果。