(游戏实现)(翻译)Dungeon Generation in Binding of Isaac 以撒的结合(原版)房间生成规则

Dungeon Generation in Binding of Isaac

BorisTheBrave.Com

 https://www.boristhebrave.com/2020/09/12/dungeon-generation-in-binding-of-isaac/

The Binding of Isaac, and its remake, Binding Of Isaac: Rebirth are one of my favourite games of all time. It’s a roguelite twin stick shooter, much like Enter the Gungeon.

The dungeons it generates are particularly iconic. I’ve seen countless tutorials online offering how to do Isaac-like generation, but I was interested in how the original did it. To my suprise, most tutorials get it wrong. In this article I go over how the generation works, including a Javascript demo.

Though I did work through a decompilation, and brush up on my rusty knowledge of Flash (I wrote my own actionscript decompiler back in the day), I was also very fortunate that Florian Himsl, the developer of Isaac, and Simon Parzer, one of the key developers of Rebirth, were both happy to answer my questions. In fact, Florian has recently made a video describing the algorithm. Check out his whole channel for development details on his latest game, Squid Invaders.

Given his write up, this article is somewhat redundant, but if you want the gory details, press on.

本文中,我将介绍生成的工作原理,包括一个Javascript演示。

以撒的作者Florian最近做了一个视频来描述这个算法。

 

The Core Algorithm

Isaac is heavily inspired by the 2d zelda games, and generates maps similar to their dungeons.

It’s a bunch of square rooms that connect adjacently to each other. Several rooms are special – there’s always a shop , reward room  and boss on each floor, and a few other special rooms are randomly picked too. With the exception of the secret room , there are no loops in the dungeon.

The game itself is a linear progression of such levels, typically two per “chapter”. The maps get slightly larger for later levels, and the contents of rooms change, but the layout algorithm is essentially the same every time.

The first version of Isaac was developed in under 3 months, so Himsl had to be incredibly efficient with his time. The key design is elegantly simple. First, a floorplan is generated. Then some rooms are designated as special rooms. Then the interior of each room is picked from an appropriate pool.

 

1.房间都是以方块为单位相连的

2.有几类特殊房间是必定有的, 有几个特殊房间是随机选的

3.隐藏房间比较特殊,没有走主loop生成,是特殊处理的

4.设计上 有效、直观、简单。

5.首先生成一个布局图floorplan;然后一些房间被指定为特殊房,然后有个合适的“房间池”去选择普通战斗房。

 

Floorplan 布局图

Isaac is generated on a 9×8 grid. For convenience, the cells are numbered numerically, with the units digit indicating the x position and the tens digit indicating the y postion. This means you can move up, down, left, and right just by adding +10, -10, +1 and -1. The cells with an x position of 0 are unused (always empty), which means that most of the code doesn’t need worry about the boundaries of the map. So the top left cell of the map is 01 and the bottom right cell is 79.

1.9*8的格子,  up:+10  down-10  左+1 右-1

2.个位数为x ,十位为y, 左上角为01  右下角为79  【这里翻译后我对细节保持怀疑。不过不是重点】

 

First, the number of rooms is determined by formula random(2) + 5 + level * 2.6. I.e. levels start with 7 or 8 rooms, and increase by 2 or 3 each time.

首先,房间个数公式为:   random(2) + 5 + level * 2.6 。 例如关卡开始为7或8个房间,然后每次增长2或3个房间。

The game then places the starting room, cell 35, on a queue. It then loops over the queue.

游戏将开始房间(单元35)放置在一个队列中。然后循环遍历队列。

For each cell in the queue, it loops over the 4 cardinal directions and does the following:

对于每个格子,在4个方向上进行循环以下操作:

  • Determine the neighbour cell by adding +10/-10/+1/-1 to the currency cell. (上下左右找相邻cell)
  • If the neighbour cell is already occupied, give up  (如果相邻cell已有实体,放弃)
  • If the neighbour cell itself has more than one filled neighbour, give up.(如果相邻cell的邻居有2个以及2个以上的实体,放弃)
  • If we already have enough rooms, give up(如果我们房间数量够了,放弃)
  • Random 50% chance, give up  (50%的概率,放弃)
  • Otherwise, mark the neighbour cell as having a room in it, and add it to the queue.  (否则,标记这个cell为实体房,并放入队列中去)

 

If a cell doesn’t add a room to any of its neighbours, it’s a dead end, and it can be added to a list of end rooms for use later.

如果一个cell不能将一个房间添加给它的任何邻居(没看懂 马上再说   -> 解释:文章内一个cell就是一个room),那么它就是一个死胡同,可以将它添加到一个终端房间列表中以供以后使用。

In the case of maps needing more than 16 rooms, the starting room is reseeded into the queue periodically to encourage more growth.

如果需要超过16个房间,起始房间会周期性重新生成,用于达到多房间的目的。

As the above starts with a single room, and repeatedly expands outwards, it’s essentially a breadth first exploration. The restriction that it won’t add a room which already has 2 neighbours keeps the rooms in separate corridors that never loop around.

本质上是个广度优先搜索,不能有2个实体邻居的限制,确保不会出现回形房间。

The floorplan is then checked for consistency. It must have the right number of rooms, and the boss room cannot be adjacent to starting room. Otherwise, we retry from the start.

然后进行一致性检查:正确的房间数量,boss房不能和起始房间相邻。否则重新生成。

Special Rooms

Boss rooms are placed by reading the last item from the end rooms list. Because of the outward growth, this is always one of the rooms furthest from the start area.

Boss放从endRoom队列中选取最后一个。这是离起始房间最远的房间之一。

Then, the Secret Room is placed. These are added to the floorplan, and one one of the few exceptions to rooms not being placed adjacent to multiple existing rooms.

In fact, it prefers it. The generator randomly searches for an empty cell that is next to at least three rooms, and not next to any end rooms

If doesn’t find one after 300 attempts, it loosens the criteria a bit, and after 600 attempts it loosens it even futher. This procedure ensures that the secret room will always be placed, but generally they are wedged near intersections so they are next to a lot of rooms.

隐藏房:最大限度保证:楔在十字路口附近,靠近许多房间。

首先随机找出邻接3个实体的空cell,并且不和endRoom相邻。如果300次尝试没有找到 则放宽条件(猜测放宽条件的意思是相邻2个实体)。600次找不到则再放宽条件。

Nearly all other special rooms are placed at a random end room. Some rooms are guaranteed to spawn, while others have a small chance or criteria. For example, Sacrifice rooms appear 1 in 7 times, unless you are at full health, in which case they appear about 1 in 3 times.

其他特殊房间也是随机到endRoom内。一些房间确保生成,但是有些只有很小概率。比如献祭房只有1/7的概率创建,但是如果你HP是满的,则有1/3概率创建。(这么说有些特殊房间还是runtime决定的 ?存疑)

 

Normal Rooms 标准战斗房间

Adjacent rooms always have a door (or destructible wall) in the exact center and every room is designed to always be accessible from a four directions. Thus, there’s no special considerations required when choosing rooms – they will always work.

相邻房间总是联通的(有门或者可破坏的墙)。这样的话在选择NormalRoom时候不需要特殊考虑

Rooms are randomly picked from a pool. Rooms include both the layout (pits, fires, boulders etc) and the monsters. Both are subject to random variations like champion monsters, tinted rocks, and red fireplaces.

房间是从随机池拿出并创建的。房间内的怪物、障碍物等也都是随机创建的。

For normal rooms, there are 3 pools of rooms – easy, medium and hard. The first stage in a given chapter draws easy and medium rooms, and the second stage from medium and hard. The first chapter, the Basement, has 174 normals rooms in the pools. The “alternative chapters” such as the Cellar which can replace the Basement randomly, have a slightly different set of rooms.

针对标准房,有三种大类型的pool:简单中等和困难。章节刚开始的阶段是简单和中等房间,第二阶段是中等和困难房间。第一章地下室有174的房间在pool里(174个房间什么意思?一共就不超过20个类型的房间啊?存疑,大概率是同种形状的房间  enemy模版等不一样。

 

Curse of the Labyrinth  迷宫的诅咒

One of the most interesting extras in the code is double sized maps. These occur randomly, and also for several of the games challenge modes. Aside from the obvious duplication of special rooms and two adjacent boss rooms, it has lots of small details too:

双倍大小地图。随机触发的,有些挑战模式也是随机触发的。除了明显的 重复特殊房和2个相邻的boss房外,还有以下细节:

  • 80% more normal rooms (max 45)   多了80%的标准房间(上限45)
  • Only use the 6 furthest away end rooms for special rooms  特殊房间指定为最远端的6个房间
  • It pulls from easy, medium, and hard room pools.  不分阶段,直接从3种房间池中提取。 
  • Randomly adds extra normal rooms to the floorplan with similar placement logic to Secret rooms.  随机添加额外的普通房间,布局逻辑与秘密房间相似。

 

Demo

I’ve put together a simplified example of the generator in javascript for you to play with. The full code can be found here.

 

Rebirth   以撒 重生

Though Rebirth has a lot of fun new features, the main contribution to the level generation is adding larger, irregularly shaped rooms.

重置版内有:更大且形状不规则的房间。

This was implemented by Simon Parzer as a careful modification of Himsl’s original code.

作者重改了源码实现的

Instead of looping over the cardinal directions, it loops over all exits of a room instead. There can be up to 8, on a 2×2 room.

不再使用可选方向进行循环。使用所有可能存在的Room方向进行循环。比如2X2的大方形房间,有8种类型的可能。

When it comes to inserting a room, it randomly tries to use a big room instead. The neighbour check still only applies to the first cell, the one by the door, but it does check that there is empty space for the rest of the room.

That means that big rooms can cause some loops in the level. Commonly two large rooms are generated side by side, complete with a pair of doors leading between them.

没看懂

If there’s no space for the room, another candidate is tried. When large rooms are successfully inserted, there’s a 95% they are removed from the pool.

Even more code was needed to handle large boss rooms. Recall that boss rooms are always placed as far as possible from the starting room. If a large room is desired, the generator replaces the designated single room. As boss rooms are always dead ends, the replacement is checked that it not adjacent to any extra rooms. Sometimes the replacement is still not possible, so all the endpoints at max distance from the start room are tried before giving up entirely.

When it comes to selection rooms, the adjacent rooms on the floorplan are now considered, and some rooms are only picked if it determines that no doors are needed.

没看懂

 

本文详细说明了以撒原版的实现规则。不过以撒重生(多种形状的Room)的实现规则比较模糊,待我再找找。

文章作者的js代码:

https://www.boristhebrave.com/permanent/20/09/isaac_gen/gen.js

参考上文链接实现了一个unity版本的实现:

https://github.com/fechen2/TestRoomGenerator.git

基本ok。 多形状Room的isaac实现我没找到,不过我想到了一种方式,实现并测试。

下图是实现的ShapeRoom的随机生成:

 

posted @ 2023-05-30 23:50  sun_dust_shadow  阅读(36)  评论(0编辑  收藏  举报