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

推荐订阅源

H
Hackread – Cybersecurity News, Data Breaches, AI and More
C
Check Point Blog
Hacker News: Ask HN
Hacker News: Ask HN
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
WordPress大学
WordPress大学
P
Proofpoint News Feed
V
Visual Studio Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
N
Netflix TechBlog - Medium
C
CXSECURITY Database RSS Feed - CXSecurity.com
博客园 - 聂微东
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
博客园 - 叶小钗
Cisco Talos Blog
Cisco Talos Blog
S
Schneier on Security
T
Threat Research - Cisco Blogs
腾讯CDC
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
The Hacker News
The Hacker News
Google DeepMind News
Google DeepMind News
Microsoft Security Blog
Microsoft Security Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
GbyAI
GbyAI
N
News | PayPal Newsroom
L
LINUX DO - 最新话题
酷 壳 – CoolShell
酷 壳 – CoolShell
P
Palo Alto Networks Blog
T
Tenable Blog
S
Secure Thoughts
T
Threatpost
V2EX - 技术
V2EX - 技术
大猫的无限游戏
大猫的无限游戏
Martin Fowler
Martin Fowler
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Vercel News
Vercel News
罗磊的独立博客
P
Privacy & Cybersecurity Law Blog
Engineering at Meta
Engineering at Meta
小众软件
小众软件
Google DeepMind News
Google DeepMind News
N
News and Events Feed by Topic
Y
Y Combinator Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
C
Cybersecurity and Infrastructure Security Agency CISA
P
Proofpoint News Feed
L
Lohrmann on Cybersecurity
P
Privacy International News Feed
H
Heimdal Security Blog
量子位
B
Blog

机核

游戏性能旗舰最强之选,一加 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
译介丨简单介绍 Bresenham 直线算法
mnikn · 2022-05-08 · via 机核
  • 作者:Sébastien Bénard

  • 译者:mnikn

1. 什么是 Bresenham 直线算法

Bresenham 直线算法是个很通用的算法,几个月前我才刚刚了解到它,它在许多用途上都很实用。这个算法基本用来在两点之间基于网格(例如像素网格)绘制一条直线,绘制出来的直线是 pixel perfect 的,看起来很酷(译注:pixel perfect 的定义参考这篇文章中有关线条的说法,幸好学过像素画不然还不知道这个是什么)。

不过这个算法还有很多有趣的用途:

  • 视线检测

  • 寻路算法优化

  • 平滑寻路路径

  • 视野检测(圆锥)

  • 光照

  • ...

2. 实现

你可以看下基于 Haxe 的实现,这部分代码在我的 GitHub 仓库里面:BRESENHAM.HX

以下是基于 Haxe 的实现:

function getLine(x0:Int, y0:Int, x1:Int, y1:Int) : Array<{x:Int, y:Int}> { var pts = []; var swapXY = fastAbs( y1 - y0 ) > fastAbs( x1 - x0 ); var tmp : Int; if ( swapXY ) { // 交换 x 和 y tmp = x0; x0 = y0; y0 = tmp; // 交换 x0 和 y0 tmp = x1; x1 = y1; y1 = tmp; // 交换 x1 和 y1 } ​ if ( x0 > x1 ) { // 确保 x0 < x1 tmp = x0; x0 = x1; x1 = tmp; // 交换 x0 和 x1 tmp = y0; y0 = y1; y1 = tmp; // 交换 y0 和 y1 } ​ var deltax = x1 - x0; var deltay = fastFloor( fastAbs( y1 - y0 ) ); var error = fastFloor( deltax / 2 ); var y = y0; var ystep = if ( y0 < y1 ) 1 else -1; if( swapXY ) // Y / X for ( x in x0 ... x1+1 ) { pts.push({x:y, y:x}); error -= deltay; if ( error < 0 ) { y = y + ystep; error = error + deltax; } } else // X / Y for ( x in x0 ... x1+1 ) { pts.push({x:x, y:y}); error -= deltay; if ( error < 0 ) { y = y + ystep; error = error + deltax; } } return pts; }

注意 fastAbsfastFloor都是 absolute 和 floor 的极致性能实现版:

static inline function fastAbs(v:Int) : Int { return (v ^ (v >> 31)) - (v >> 31); } ​ static inline function fastFloor(v:Float) : Int { return Std.int(v); // 实际上更多时候是在去除后面的小数值而不是向零取舍 }

对于 Bresenham 算法,你需要了解的是:

  • 它很容易实现,而且很高效。

  • 为了性能,我把 if( swapXY ) 移到了循环外面(只有当调用次数很多的时候才需要这么做)。

  • 数组的内存分配(例如 var pts = [])是有性能损耗的。出于性能考虑你可能想要特殊处理下这个函数,不让这个函数每次执行都新建一个数组存点。

  • 数组里点的顺序可能会变化。这点非常重要!这意味着数组可能返回 x0,y0 到 x1,y1的点或者刚好相反,这取决于直线的角度。

我建议你读下 Wikipedia 上有关 Bresenham 直线算法的常规实现,尤其是如果你想要根据情况对它做一些特殊处理,在上面你还能发现一些有趣的优化技巧。

所以这个算法我们要怎么去用呢?

3. 使用案例

3.1. AI

当你想要写一个怪物敌人的 AI 时,你通常会遇到两个很基础的问题:

  • 怪物和玩家接近吗(基础的做法是检查之间的距离)

  • 怪物能够看到玩家吗

第二个问题可以用 Bresenham 直线算法来轻松解答,只需要用它来检测怪物的视线和玩家中是否有障碍物就好。

function checkLine(x0:Int, y0:Int, x1:Int, y1:Int, rayCanPass:Int->Int->Bool) { var swapXY = fastAbs( y1 - y0 ) > fastAbs( x1 - x0 ); var tmp : Int; if ( swapXY ) { // swap x and y tmp = x0; x0 = y0; y0 = tmp; // swap x0 and y0 tmp = x1; x1 = y1; y1 = tmp; // swap x1 and y1 } ​ if ( x0 > x1 ) { // make sure x0 < x1 tmp = x0; x0 = x1; x1 = tmp; // swap x0 and x1 tmp = y0; y0 = y1; y1 = tmp; // swap y0 and y1 } ​ var deltax = x1 - x0; var deltay = fastFloor( fastAbs( y1 - y0 ) ); var error = fastFloor( deltax / 2 ); var y = y0; var ystep = if ( y0 < y1 ) 1 else -1; ​ if( swapXY ) // Y / X for ( x in x0 ... x1+1 ) { if( !rayCanPass(y,x) ) return false; ​ error -= deltay; if ( error < 0 ) { y = y + ystep; error = error + deltax; } } else // X / Y for ( x in x0 ... x1+1 ) { if( !rayCanPass(x,y) ) return false; ​ error -= deltay; if ( error < 0 ) { y = y + ystep; error = error + deltax; } } return true; }

这个版本没有返回任何位置点,它只是对于线上的每个点都调用下函数(rayCanPass),如果函数返回为 false,那么整个 checkLine 就停止运行然后返回 false。

例如:

checkLine( mob.x, mob.y, player.x, player.y, function(x,y) return collisionMap[x][y] == false );

这样的实现很简洁而且高效,尤其是当发现障碍就停止循环。注意如果你让 checkLine 函数循环太多次的话,Flash 上的性能可能不会太好,因为 Flash 函数调用性能损耗挺高。 (译注:这篇文章发布在 2013 年,现在 Flash 都已经进入坟墓了……)

3.2. 寻路算法优化

当你在写寻路算法的时候(例如 A-star 算法),现实会让你发现调用这些算法在实时游戏中会消耗不少性能。所以你要尽可能不去调用寻路算法。

根据我们上述的例子,如果你能够回答“怪物能够看到玩家吗”这个问题,你就可以利用这种方式来减少不必要的寻路算法调用。

3.3. 平滑寻路路径

大部分的寻路算法会返回从起点到终点间的所有的位置点,不过对于网格来说会有点生硬。

Bresenham 直线算法可以很轻松就让寻路算法的结果更加“平滑”一些,你需要做的是:

  • 设置一个叫 REF 点,这个点等于起点

  • 检测 REF 点能否在路径上“看见”第三个点,如果可以的话,把第二个点去掉,因为这个点没用

  • 重复检测操作,如 REF 点能否看到第四个点,以此类推

  • 如果 REF 点不能看到我们给过去的点,那么上一个点就有用了,我们保留它。在这种情况下,REF 点就变成了上一个点,然后对于剩下的点,重复刚才的算法

  • 最后,通过只保留能够相互看见的有用点,你就可以让路径更加简洁

3.4. 视野检测(圆锥)

想要实现像 Metal Gear Solid or Commando 那样的圆锥视野不难。

检测敌人是否看到玩家的做法:

  • 检查下两者间的距离

  • 如果在范围内,计算敌人和玩家之间的角度(Math.atan2)

  • 如果计算出来的角度和敌人的方向角度之间的角距离小于圆锥范围 / 2,开始 Bresenham 直线算法检测

  • 如果玩家没有躲着什么障碍物后面...警报响起来吧!

3.5. 关于对角的注意点

注意如果你的游戏中有对角的墙,基础的 bresenham 直线算法还是能够穿墙而望的。

3.6. 光照

如果你需要遍历范围内的所有点,例如 rouge-like 游戏里面的火炬,你可以使用 Bresenham 直线算法来检测火炬是否能够看到某个特定点。如果可以,那你就可以点亮那个单元格,然后光照的亮度等于单元格距离火炬的距离 / 最大光照距离。

var intensity = 1 - dist / maxDist; // 0=没有光照, 1=全光照

这样一个算法实时运行的话会相当耗性能,因为你需要遍历每个点和火炬来计算光照值。不过如果你不需要很实时,例如火炬不需要动态移动,你可以在游戏开始前预计算你的光照值,这样消耗基本上就忽略不计了,而且这实现起来一点都不难。

for (dx in -radius...radius+1) for(dy in -radius...radius+1) { var x = source.x + dx; var y = source.y + dy; ​ var d = distance(source.x, source.y, x, y); if( d<=radius && checkLine(source.x,source.y, x,y, hasNoCollision) ){ var intensity = 1 - d/radius; // 更新光照值,绘制,... } }

对于玩家你还是可以拥有动态光照的,像 roguelike 游戏,只有在玩家的单元格发生变化时才重新计算光照(例如移动)。这里面还有很多优化的空间,不过具体如何实现取决于你的需求。

3.7. Pixel perfect 的圆

Bresenham 直线算法通常用作画直线,不过其实也可以用来画圆。实际上实现这一点的作者并不是 Bresenham 本人,不过这个实现方法深受 Bresenham 的启发。

它不像直线那么常用,但是在一些情况下还是有作用的,而且很容易实现。更多的细节可以在 Wikipedia “Midpoint circle algorithm” 词条页面上看到。

在检测中通过添加一些额外的点来修正圆:你可以修正圆里面的中断的地方(例如 error < 0),检测中断周围的其它点。效果是在不影响水平线和垂直线的情况下,让对角线变得更粗。(译注:这段作者说得有点云里雾里,简单来说就是让一个不 pixel perfect 的圆通过修正变得 pixel perfect)

译者的话

现在大部分游戏引擎都内置了寻路算法、动态光照等,可能大多数情况下使用内置的算法就能解决问题,不过文章中的思路还是很值得借鉴的,尤其是当发现内置的算法不满足需求时。