云风的 BLOG - RSS Feed

思绪来得快去得也快,偶尔会在这里停留

Latest articles

球面地图网格的设计

最近几年有很多基于球体地图的游戏,包括今年颇有好评的戴森球计划。 大多数在球面打格子分割地图的游戏,会采用六边形网格。但是,只靠正六边形网格无法铺满整个球面。必然会留下 12 个五边形。因为用正六边形铺满球面时,其实是在尝试用正 20 面体逼近球体。如果我们将正 20 面体做平面展开,每个三角形面中划分多个正三角形,每 6 个三角形合成一个正六边形,但中间的 12 个交接点必然是 5 个三角形缝合。只要处理好这个五边形,尝试用六边形网格做游戏是个合适的选择。 据戴森球计划的开发日志所述,开发团队也曾考虑过六边形网格方案,后来觉得不合适又回归四边形。我猜想多少和戴森球计划参照的游戏原型异星工厂是四边形网格有点关系。我也觉得在四边形网格上设计这种传送带游戏更舒适。不过,相比目前戴森球计划中用的方案:从赤道到两极,沿着纬线圈一圈圈减少网格数量;我更喜欢网格能完全对齐的方案。...

Paradox 脚本语言的一点研究

因为一直给群星维护汉化 Mod 的缘故,我花了不少时间去理解 Paradox 的配置脚本语言。 我认为它用了类 lisp 的语法来描述数据。用数据去描述游戏逻辑。P 社的游戏都有玩家共建的 Wiki 提供了丰富的资源来帮助玩家创作 Mod 。学习它的脚本语言是非常容易的。 最近一段时间,我仔细阅读了 Wiki 中所有关于 Modding 的文章,相比之前零星的了解,算是做了一次系统的学习。 这套脚本语言还是以配置数据为主,但也提供了很多逻辑控制手段,很值得学习。 我以一个游戏程序员的角度去看,在此记录一些有启发的东西。当然,我自己并未用它开发完整的 Mod ,可能理解会有所偏差。 从游戏逻辑控制(官方称为 Dynamic modding )的角度,它定义了两个重要的概念:Effect...

带娃玩桌游的一些记录

云豆目前 7 岁,可可 4 岁半。我从三年前就不断尝试带娃玩桌游,实际能玩的下去的并不多。最近一段时间是云豆接受桌游的爆发期,很多游戏都玩得津津有味,非常值得记录一下。 首先是我认为 4~6 岁可以玩进去的游戏。这类游戏不多,具体和娃的天性相关很大。我在网上参考别家的娃爱玩的游戏尝试了一堆,大多是玩不下去的。核心问题是:孩子太小的话,很难理解 “规则” 这件事。许多游戏即使能玩,也是破坏了规则,玩个热闹而已。 在我家能重复玩的这类游戏有这么几个。 企鹅敲冰块:一些蓝色和白色的六边形塑料块,靠摩擦力相互卡住拜满棋盘,轮流敲掉一些,防止棋盘中心的企鹅掉下去。本来游戏还配有轮盘转的规则,对每次的敲法做出一些限制的,但娃太小的话很难遵循这些细则玩下去。 北极熊钓鱼...

内存对齐问题和编译器优化

昨天在公司内部的“不作不死”(程序员)群里,有同学贴了个知乎上的帖子 。表示这个问题居然关闭 gcc 的 builtin-memset 就解决了,感觉很玄学。 我说,这个感觉才是对的。关于文章中表达的 “添加编译选项-no-builtin-memset后,一切就正常了。然后大家都如释重负,不但解决了问题,又学到的新知识。” ,我认为这“如释重负”对于程序员来说才是种不正常的感觉,正常应该是“更加困扰”了才对呀。 到底是怎么回事,文章线索不全,无法判断。不过我直觉上感觉和我前几个月在我们这个“不作不死”群里讨论过的另一个问题非常相似。 我怀疑,问题是内存不对齐造成的。 当时有同事发现了我给 skynet 写的序列化库在 arm 32 下有机会引起崩溃 。由于...

预制件和对象集的管理

最近在用自研引擎开发项目时,发现了一些问题。在解决问题的同时,也逐步对之前的设计做了一些调整。一开始只是一些小修复,慢慢的发展成了大规模的代码重构。 最开始源于我重新设计了 ECS 框架。在新设计下,可以用 C/Lua 混合组织数据。为未来优化热点做好准备。我们借此机会重新思考了 ECS 框架下应该如何组织代码的问题。发现一个关键点就是,要尽量去掉系统中对象之间的引用关系。每类对象最好是成组分批的处理业务,每个模块都只做最简单的事情。但同一件事情尽量处理更多的数据、对象。 比如对象的构建和销毁,通常会随着对象构成的复杂度上升而演变为一件越来越复杂的事务。我们之间在设计 ECS 框架时,就设计了大量的机制来正确执行 Entity 的构建流程。一个 Entity 可以由若干 Component...

Tag set 的数据结构优化

在最近实现的 ECS 库中,Tag 是一种非常重要的数据结构。它是一类特殊的 Component ,不携带数据,但会关联到同一 Entity ,最重要的用途是用于筛选。我在设计 Comonent 的数据结构时,采用了一种简单的数据结构 。它采用连续内存储存的数组,按 Entity id 有序排列。并在查询算法上做了一些优化,可以使得大部分查询时间小于 Log(N),接近常量时间。 但是,这样做的代价是插入和删除操作都是 O(n) 的。为了避免大量的插入删除操作堆积在一起时,整体的时间复杂度变成 O(n^2) ,我禁止 Component 的删除,而删除 Entity 改为标记,待一帧结束后一起删除。这样,可以让批量删除保持在 O(n) 的复杂度。 标记操作实质上就是打...

ECS 模型下的处理模式

最近在公司内做了一次两小时的分享,介绍了一下我最近几年对 ECS 模型的一些想法以及最近在项目中的应用心得。 我分享的主题不叫 ECS ,而用了一个更宽泛的名字 Data oriented design 。因为我不想局限在 Entity Component System 这些具体名词上。从 wikipedia 上看 ,DOD 的提出是源于游戏软件对性能的追求, 它主要围绕的都是其数据在内存中的组织形式不同。和 C++ 这类可以直接控制对象内存布局的 OOP 的语言的默认布局相比,DOD/ECS 的数据布局对 CPU Cache 更好友一些,从而可以获得更好的性能。 如果从这个角度看,如果不是用 C/C++ 这些可以直接控制数据内存布局的语言,采用 ECS 的意义很小。 但是,我觉得...

带猜测的二分查找算法

我想用 C 实现一个内存紧凑的 ECS 框架,希望数据结构足够的简单,且能管理海量的对象。所以我让每个 component 就是一个不包含任何引用的 struct ,并带有一个 32bit 的 id 。并把这样的一个数据结构放在一块连续内存中。 这个 id 没有对外暴露的 API (不是 entity id ),可以在运行过程中调整。如果两个不同类型的 component 有相同的 id ,即认为它们同属一个 entity 。id 的作用是管理 component 的生命期,以及在遍历 component 时,可以找到同个 entity 上其它的组件。 对于我的应用场合来说,最多的操作是遍历单类 component 的集合。这种操作在这个数据结构下特别简单,就是 for 循环遍历一个简单的数组。但有些系统会需要在处理一个...

缓存在 Lua 中的配置表

最近在尝试做一个类似异星工厂的游戏原型。由于最终希望在内存有限的手机上运行,所以不得不考虑内存如何有效利用的问题。 这是因为,我在玩异星工厂(加上一些 mod )时,发现 PC 内存能占到 10G 以上,且这些内存大部分都不是图像资源,是实打实的逻辑数据。我稍微估算了一下,在一些内容丰富的 mod 中,游戏内的对象能够达到数十万,甚至上百万之多。用传统方法使用的内存势必以 G 计算。 如果希望同样的逻辑跑在手机上,我希望把逻辑数据控制在 500M 之下。除了从游戏玩法做做一些限制外,还需要对逻辑占用的内存精打细算。 在 C 中实现一个 ECS 模型是一个不错的选择。 每个 Entity 仅包括必须持久保存的数据。很多数据都只在同一个 tick 计算中间使用,并不需要一直保存在对象中。...

ANSI escape code 及 Lua 封装

这两天想给一个想法做个简单的原型,因为涉及人机交互,需要在屏幕上绘制一些简单的交互元素。当然,现在有很多工具可供利用。过去遇到这种事情,我会尝试用已有的各种开源游戏引擎(我尤其推荐 PICO-8),或是直接在浏览器中用 css/javascript 写等等。 最近几年我玩了大量 RogueLike ,想尝试一下在 console 下用 ascii 字符来拼凑画面。这很有趣,能让我回忆起小时候 Apple ][ 上花掉的大把时光。同时我想用 Lua 来做开发,却不想引入 ncurses 这样的第三方库。最好能用几分钟就可以从零搭建起开发环境。 最好的选择当然是用 ANSI escape code 通过标准输出在 console 界面上作画了。 需要用到的是 CSI...

Discover, share and read the best on the web

Follow RSS Feeds, Blogs, Podcasts, Twitter searches, Facebook pages, even Email Newsletters! Get unfiltered news feeds or filter them to your liking.

Get Inoreader
Inoreader - Follow RSS Feeds, Blogs, Podcasts, Twitter searches, Facebook pages, even Email Newsletters!