大家。 首先,我是一名业余程序员。我正在尝试使用C ++和SFML制作一个简单的城市建筑应用程序进行学习。它并不是真正的游戏,因为它只会建造城市街区和建筑物,并向用户展示。 现在,我能够创建城市街区。我的问题是如何将街区细分为建筑物。我对如何做到没有真正的想法。 可能的解决方案是(我没有足够的声誉来发布图像,但是有链接): https://i.postimg.cc/630GKGW7/bitmap.png
唯一的规则是: (1)每座建筑物必须符合已知的最小和最大尺寸; (2)每栋建筑物必须至少有一个面接触任何块体边缘; (3)不得保留任何空白空间。
我已经为此困扰了好几天。谁能给我一个想法呢?伪代码也很棒。
提前谢谢!
答案 0 :(得分:1)
请注意,我将使用OOP语法简化此操作,但这不是有效的代码。首先,我们创建一个接口来定义所需的行为:
class CityBlock {
Building[] buildings // should initially contain one building taking up the whole CityBlock
double width
double height
double maxBuildingSize
double minBuildingSize
splitBuilding(horizontal/vertical, coord) // This will split a building horizontally/vertically
createRandomBuildings() // this is what we want to create!
}
class Building {
Point position // position of top-left corner
Building[] subBuildings // buildings created by subdivision
double width
double height
double size() { return width * height }
}
现在好玩了!让我们尝试制作createRandomBuildings()
方法。我将采用的方法是重复细分建筑物,直到它们介于2 * minBuildingSize
(小于等于没有细分可以创建两个有效建筑物)和maxBuildingSize
之间。
重要提示::即使可以进行有效的细分,此方法也仅在maxBuildingSize >= 2 * minBuildingSize
时保证有效的建筑物。考虑到您的用例,我认为大小约束不会造成任何问题,并且相对于确定性更高的解决方案,更“随机”的解决方案会更好。
让我们开始吧!我们将创建一个称为subdivide
的递归函数来完成繁重的工作。
Building[] subdivide(Building b, horizontal/vertical) {} // Subdivides b into a random number of other buildings
我将每座建筑物细分的方法是将其分成任意数量的水平/垂直段。例如。
从此
对此
注意::为简化起见,我将像上图那样将细分视作垂直。对于水平细分,只需交换宽度/高度。
当然,我们不能使用任何个细分。太多了,所有生成的建筑物都会太小。因此,我们应该首先定义仍可创建有效建筑物的最大细分数量。
minSubdivisionWidth = minSize / b.height // ensures that subdivisionWidth * b.height >= minSize
maxSubdivisions = floor(b.width / minSubdivisionWidth)
subdivisions = randomInt(2, maxSubdivisions)
现在我们有了有效的细分数量,我们需要在确保建筑物不太小的同时随机分配它们的空间。为此,我们将可用空间分成两部分:最小空间和 free 空间。每个细分区域都需要有最小的空间,但是也有等于b.size() - minBuildingSize * subdivisions
的空闲(或剩余)空间。这个自由空间是我们想要在细分的矩形之间随机分配的空间。
蓝色是最小空间,粉红色是自由空间
让我们分配这个空间:
widths[] // This will be the widths of our subdivided buildings
freeWidth = b.width - minSubdivisionWidth * subdivisions
weights[] // randomly assigned weight for free space
sumWeight
for i = 1 to subdivisions {
randWeight = random()
weights[i] = randWeight
sumWeight += randWeight
}
for i = 1 to subdivisions {
widths[i] = minSubdivisionWidth + (weights[i] / sumWeight) * freeWidth
}
现在我们可以进行实际的细分了:
// transform individual widths into coordinates for building split
cumulativeWidth = 0
for i = 1 to (subdivisions - 1) {
cumulativeWidth += widths[i]
splitBuilding(vertical, cumulativeWidth)
}
我们快到了!现在,如果建筑物低于最大值,我们只需要一个片段即可随机不细分:
probToNotSubdivide = .3 // obviously change this to whatever
if b.size() < maxBuildingSize and randomDouble(0, 1) <= probToNotSubdivide { return }
如果建筑物太小,一个不能细分的地方:
if b.size() < minBuildingSize * 2 { return }
如果要从街区的边缘切下建筑物,则不能细分:
/*
If the building is touching a horizontal edge, vertical subdivisions
will not cut anything off. If the building is touching both
vertical edges, one subdivision can be made.
*/
if not (b.position.y == 0 or (b.position.y + b.height) == cityBlock.height) {
if b.width == cityBlock.width {
// do one subdivision and recurse
splitBuilding(vertical, randomDouble(minSubdivisionWidth, cityBlock.width - minSubdivisionWidth)
for subBuilding in b.subBuildings {
subdivide(horizontal, subBuilding)
}
return
} else { return }
}
在末尾添加一点递归,然后...
Building[] subdivide(Building b, horizontal/vertical) {
// exit conditions
if b.size() < maxBuildingSize and randomDouble(0, 1) <= probToNotSubdivide { return }
if b.size() < minBuildingSize * 2 { return }
/*
If the building is touching a horizontal edge, vertical subdivisions
will not cut anything off. If the building is touching both
vertical edges, one subdivision can be made.
*/
if not (b.position.y == 0 or (b.position.y + b.height) == cityBlock.height) {
if b.width == cityBlock.width {
// do one subdivision and recurse
splitBuilding(vertical, randomDouble(minSubdivisionWidth, cityBlock.width - minSubdivisionWidth)
for subBuilding in b.subBuildings {
subdivide(horizontal, subBuilding)
}
return
} else { return }
}
// get # subdivisions
minSubdivisionWidth = minSize / b.height // ensures that subdivisionWidth * b.height <= minSize
maxSubdivisions = floor(b.width / minSubdivisionWidth)
subdivisions = randomInt(2, maxSubdivisions)
// get subdivision widths
widths[] // This will be the widths of our subdivided buildings
freeWidth = b.width - minSubdivisionWidth * subdivisions
weights[] // randomly assigned weight for free space
sumWeight
for i = 1 to subdivisions {
randWeight = random()
weights[i] = randWeight
sumWeight += randWeight
}
for i = 1 to subdivisions {
widths[i] = minSubdivisionWidth + (weights[i] / sumWeight) * freeWidth
}
// transform individual widths into coordinates for building split
cumulativeWidth = 0
for i = 1 to (subdivisions - 1) {
cumulativeWidth += widths[i]
splitBuilding(vertical, cumulativeWidth)
}
// recurse
for subBuilding in b.subBuildings {
subdivide(horizontal, subBuilding)
}
}
就是这样!现在我们有了createRandomBuildings() { subdivide(vertical, initialBuilding) }
,并且我们细分了城市街区。
P.S。。再次,此代码并不意味着有效,这也是一篇很长的文章。如果此处的某些内容无法正常工作,请对此答案进行编辑/评论。我希望这对您可以采用的方法有所了解。
编辑:为清楚起见,您应该在递归的每个级别上在水平和垂直细分之间切换。