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

推荐订阅源

T
Tenable Blog
Vercel News
Vercel News
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Recorded Future
Recorded Future
Y
Y Combinator Blog
Stack Overflow Blog
Stack Overflow Blog
Recent Announcements
Recent Announcements
T
Tailwind CSS Blog
IT之家
IT之家
人人都是产品经理
人人都是产品经理
博客园 - 【当耐特】
B
Blog
博客园 - 聂微东
V
V2EX
U
Unit 42
美团技术团队
Apple Machine Learning Research
Apple Machine Learning Research
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
S
Secure Thoughts
M
MIT News - Artificial intelligence
腾讯CDC
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Google Online Security Blog
Google Online Security Blog
雷峰网
雷峰网
S
Security Archives - TechRepublic
T
Troy Hunt's Blog
博客园 - 叶小钗
P
Palo Alto Networks Blog
S
Security @ Cisco Blogs
D
Darknet – Hacking Tools, Hacker News & Cyber Security
云风的 BLOG
云风的 BLOG
月光博客
月光博客
博客园 - 三生石上(FineUI控件)
H
Help Net Security
WordPress大学
WordPress大学
E
Exploit-DB.com RSS Feed
www.infosecurity-magazine.com
www.infosecurity-magazine.com
S
Schneier on Security
GbyAI
GbyAI
J
Java Code Geeks
Hugging Face - Blog
Hugging Face - Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
小众软件
小众软件
Engineering at Meta
Engineering at Meta
Cloudbric
Cloudbric
N
Netflix TechBlog - Medium
Scott Helme
Scott Helme
NISL@THU
NISL@THU
罗磊的独立博客

机核

游戏性能旗舰最强之选,一加 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
游戏开发中的细节——“描述”
mnikn · 2022-10-01 · via 机核

The devil is in the details

大部分的教程都会教你怎么实现一个功能,不过却很少从工程的角度去分析和解释该怎么去实现一些必须要有但是又难以管理的细节。

这次我们讨论的主题是——描述。在游戏中,想要表达一个技能、一项物品有什么功能效果,肯定会出现一大段字来描述,这篇文章重点关注的就是这段字要怎么显示的问题。

单纯一看这个问题好像很简单,所有游戏引擎都会有显示文本的功能,直接把值显示上去不就好了吗?别急!我们要做的是从工程角度上去思考,就以实际案例《欺诈之地》这一个卡牌游戏为例吧。

方案1

我们先拿一张攻击牌,看看它的描述:

我们先设计下这张卡牌的数据格式,这里就以 json 为例吧:

{ "id": "big_hammer", "name": "大锤", "type": "attack", "desc": "当你有任意连击点时,造成2额外伤害,获得2连接点", // 额外伤害 "extra_damage": 2, // 连击点 "extra_combo_point": 2 }

按照这结构,好像我们直接用 desc 显示就行了,但是如果我们在后期调试数值时,修改 extra_damageextra_combo_point 的值,desc 并不会随着值得变化而更改描述,尤其是当你有成百上千张卡牌时,每张卡牌都在描述里写死数值就不太实际了。

方案二

我们在代码里根据不同的占位拿对应的值去展示,desc 为了表示数值占位就改为:"当你有任意连击点时,造成{{extra_damage}}额外伤害,获得{{extra_combo_point}}连接点"

在代码里我们直接用正则匹配 {{}},拿 {{}} 里面的内容当字段名读取,这里为了方便就先用 javascript 来演示,至于像 c# 的静态语言就各显神通(例如反射)自行处理吧:

const skillData = { "id": "big_hammer", "name": "大锤", "type": "attack", "desc": "当你有任意连击点时,造成{{extra_damage}}额外伤害,获得{{extra_combo_point}}连接点", "extra_damage": 2, "extra_combo_point": 2 } ​ let realDesc = skillData.desc; // 匹配 {{}} 里的值,做一个值的映射表 const valueMap = realDesc.match(/\{\{\w+\}\}/g).reduce((r, key) => { r[key] = skillData[key.substring(2,key.length-2)]; return r; }, {}); ​ // 根据映射表替换文本 Object.keys(valueMap).forEach((key) => { realDesc = realDesc.replace(key, valueMap[key]); }); console.log(realDesc);

这个方案能够解决大部分固定数据的情况,但是当你的展示数据是动态时,就有点不够用了。

方案三

方案二处理简单的展示方式没什么问题,直到我们遇到了这一张卡牌:

你会发现这张牌的描述需要依赖于其他牌的名字,假设这几张牌的数据如下:

[{ "id": "sal_dagger", "name": "萨儿的匕首", "type": "strategy", "desc": "手牌中加入{{reward_cards[0]}}或者{{reward_cards[1]}}", "reward_cards": ["big_hammer", "hold_knife"] }, { "id": "big_hammer", "name": "大锤" }, { "id": "hold_knife", "name": "握刀" }]

从数据设计的角度出来 reward_cards 一般只存放对应卡牌的 id,而 desc 里面则需要根据 id 读取技能的名字再展示,这种情况就需要我们的数据拥有执行表达式的能力了。

我们改造下 desc 的描述为:"手牌中加入:var{skills[current_skill.reward_cards[0]].name}或者:var{skills[current_skill.reward_cards[1]].name}",这里我们用一个特殊的关键字 :var 来标记这个数据来自外部,而 :var 括号里面则是表达式的内容,我们需要在外部传 一些变量例如 skillscurrent_skill 来让表达式里面的代码使用。

示例代码如下:

const skills = { "sal_dagger": { "id": "sal_dagger", "name": "萨儿的匕首", "type": "strategy", "desc": "手牌中加入:var{skills[currentSkill.reward_cards[0]].name}或者:var{skills[currentSkill.reward_cards[1]].name}", "reward_cards": ["big_hammer", "hold_knife"] }, "big_hammer": { "id": "big_hammer", "name": "大锤" }, "hold_knife": { "id": "hold_knife", "name": "握刀" } } ​ const currentSkill = skills["sal_dagger"] let realDesc = currentSkill.desc; realDesc.match(/:var{[^}]+}/g).forEach((exp) => { realDesc = realDesc.replace(exp, eval(exp.substring(5, exp.length - 1))); }); console.log(realDesc);

有了这样一个机制我们在填写描述时基本上是可以为所欲为了,任何动态的数据都可以通过外部传过来展示,同时可以自行进一步实现其他关键字例如 :visible 来判断展示时机。

总结

开发中没有银弹,所有的方案都有利有弊,大家会发现当方案越来越灵活时,对于填数据的人的要求也越来越高。方案一是有手就能填,方案二则需要了解一些占位数据的写法,方案三已经是直接在写代码了,方案的选择需要根据自身情况实际应用。

这篇文章旨在抛砖引玉,如果大家有什么更好的方案,欢迎在评论区提出!