简介一套面向Cityengine用户的城市规划规则库适合城乡规划、数字城市、三维建模方向的设计师与学习者用于解决从零搭建复杂城市模型费时费力的问题。压缩包内共8个文件以.cga和.cgb规则脚本为核心配合.xml项目配置、工程文件及.txt说明文档完整展现了规则库从地形、道路到建筑生成的组织逻辑整体仅36KB体积小巧便于快速解析与复用。用户可将规则库直接导入Cityengine通过调整建筑高度、街道走向、地块密度等参数实时预览不同方案也可参考CGA脚本自行修改或扩展规则实现个性化城市设计。已有1108人学习该资源对希望系统理解CGA规则语法、掌握批量建模流程的读者而言是一份轻量而实用的参考工具。1. Cityengine 城市规划02规则库为什么我劝你别再手动画城市模型做了几年城市更新项目我慢慢发现一个规律凡是靠手工一点点挤出体块来推敲方案的日子基本都浪费在改版上了。 Cityengine 城市规划规则库正是把地块、道路、建筑、退线这些规划要素封装成一套可复用的规则资产。它的价值不是你点一个按钮就生成一座城而是让你在改容积率、换业态、调控高的时候像改参数表一样改模型。这篇文章不是规则库说明书而是从文件结构、CGA 规则写法到参数陷阱的一线拆解适合正在做城市设计和国土空间方案的规划师、建筑师也适合刚接触 CityEngine 规则库的开发者。我把最值得投入的部分讲透把最容易翻车的坑提前摆出来。2. 拆开规则库的“黑匣子”从CGA规则到城市生成的完整链路很多新手拿到一个“城市规划02规则库”第一件事就是把整个文件夹拖进 CityEngine然后等着看效果。结果通常是一堆建筑从地块里长出来有的叠在一起有的干脆消失。这不是规则库错了而是你没有理解它的运行方式。规则库本质是一组带输入输出的规则程序不是贴图素材包。要让它听话先得看清它装了什么、按什么顺序执行。2.1 规则库到底装了什么文件、图层与坐标系一个面向城市规划的规则库文件结构大致是这样rules/ 00_base.cga # 常量、纹理路径、通用材质 01_zoning.cga # 区划分类、地块属性入口 02_building.cga # 建筑体量、立面生成 streets/ street.cga # 道路和步行道规则 assets/ textures/ # 立面、屋面、地面纹理 models/ # 树、路灯、公交站 data/ parcels.shp # 地块矢量面 roads.shp # 道路中心线 zoning.csv # 区划代码和指标表不要只看文件名先用命令行把规则库完整列一遍find . -type f | sort | head -50head -50只是限制输出量。真正该看的是.cga文件之间有没有依赖顺序比如00_base.cga里定义了BASE_COLOR后面的02_building.cga引用了它。如果你只把02_building.cga单独拖进场景引擎会直接报“属性未定义”。这时不是规则坏了而是文件加载顺序不对。CityEngine 的规则编辑器按文件夹整体加载所以规则库的目录层级就是在定义依赖关系。比文件顺序更隐蔽的是坐标系。GIS 数据经常混用投影有的地块面来自 Web Mercator有的来自 CGCS2000如果你把不同投影的数据丢进同一个规则库生成的地块会偏出几公里。我拿到一个规则库的第一件事是看场景的 World 坐标系和data/parcels.shp的源投影是否一致。不一致时先转坐标准系再跑生成否则后面对容积率的统计全是错的。2.2 城市生成的核心流程地块、递归与属性CityEngine 生成模型的逻辑一句话概括从一个矢量面开始把面一步步切割、拉伸、细分最终生成带材质的模型。CGA 规则是一个递归系统不是一次性画完就结束的脚本。下面是最小的城市体量规则链Lot -- setback(3) { all: Building | border: Garden } Building -- extrude(world.y, groundFloorHeight) { all: Footprint } Footprint -- split(y) { floorHeight: Floor }* Floor -- comp(f) { front: Facade | top: Roof }这四个规则做了四件事Lot是初始形状setback(3)表示向内退 3 米内圈交给Building外圈变成GardenBuilding沿世界坐标 Y 轴拉伸groundFloorHeight的高度得到FootprintFootprint使用split(y)沿 Y 轴按层高切分每一层进入FloorFloor用comp(f)把前面和顶部分离。setback对应规划里的退线extrude对应建筑高度split对应楼层划分。很多新手只写了extrude没有继续细分于是所有建筑都是方盒子。那不是城市是纪念碑。规则库里的执行顺序一般固定为Block - Lot - Building - Floor - Wall/Window。在严格的城市设计规则库里还会追加Roof、Balcony、Shading等分支保证生成立面风格一致。理解这个递归链你才能知道生成出错时该去查哪一层。2.3 为什么“02”不只是版本号分层的工程习惯“城市规划02规则库”里的“02”我通常读作“第二层规则建筑体量”而不是第二个版本。一个可维护的规则库不会把所有逻辑写进一个巨型文件。常见做法是分层编号00_base.cga定义通用材质、颜色、常量01_zoning.cga把地块按区划分成住宅、商业、工业02_building.cga针对不同区划生成不同体量和立面03_civic.cga学校、医院等公共建筑。这样分层的好处很直接规划师想改商业地块的容积率只需打开02_building.cga把commercialFAR从 4.5 改成 5.0不用碰道路规则更不会影响住宅立面。我还会在每个规则文件头部花一分钟写块注释记录参数来源和修改日期/* commercial building rule updated: 2026-02-14 by planner group parameters: commercialFAR, maxBuildingHeight */ version 2026.02 Group(招商参数, 地块) Parameter(容积率上限) commercialFAR 4.5 Group(招商参数, 地块) Parameter(建筑控高) maxBuildingHeight 60这里的version字符串不是给 CityEngine 读的是给人读的。当规划师和程序员来回调整时没有版本注释你根本不知道4.5是哪次会议的决策。规则库越到后期注释越比代码值钱。3. 从0搭一套城市规划规则库核心CGA代码与参数设置这一章我会从零搭一套能用于实际规划推演的规则库不追求花哨把入口、区划、建筑、街道四层逻辑串起来。每个代码块你都可以直接复制到自己的工程里跑。3.1 先建工程导入地块并绑定入口规则准备工作新建 CityEngine 工程把parcels.shp和roads.shp放到项目的data目录。在场景中选中地块图层准备绑规则文件。入口规则我一般固定命名为Lot因为 CityEngine 里默认的初始 shape 就叫 Lot。先写一个最外层分流Hidden StartRule Lot -- case zoneType COM: CommercialBlock() case zoneType RES: ResidentialBlock() else: PublicSpace()StartRule标记入口。zoneType是地块属性从parcels.shp里的字段映射而来。这里用了case分支等于把地块按规划用途分流。这样后期你想加一种“商住混合”类型只需要再加一个case分支不会影响其他类型。绑规则的步骤是选中地块图层在 Inspector 窗口的 Rule File 栏目选择这个.cga文件然后点 Generate。如果看不到生成结果先检查地块属性面板里有没有zoneType这个字段没有的话基础数据就没接上。3.2 定义区划属性把规划指标写进 CGA为了让规则库脱离 GIS 图层也能演示我会在规则文件里直接定义一套指标参数。这一步很关键因为规则库的价值就是把指标变成可变参数。Group(区划, 用地属性) Parameter(用地代码) zoneType COM Group(区划, 用地属性) Parameter(容积率上限) FAR 3.5 Group(区划, 用地属性) Parameter(建筑控高) maxHeight 60 Group(区划, 用地属性) Parameter(首层高度) groundFloorHeight 6这些参数会出现在 CityEngine 的属性面板里可以直接用滑杆调整。规划师不需要懂代码拖动滑块就能看方案变化。接下来写商业地块的体量规则CommercialBlock -- setback(5) { all: BuildingFootprint | border: StreetLine } BuildingFootprint -- extrude(world.y, groundFloorHeight) { all: MainVolume } MainVolume -- split(y) { floorHeight: Floor }* Floor -- color(#aab8c4) comp(f) { front: Facade | top: Roof }逻辑说明setback(5)先向内退 5 米退出来的边界做成StreetLine用于之后铺路BuildingFootprint用extrude(world.y, groundFloorHeight)拉出首层体量MainVolume再按floorHeight沿 Y 轴重复切出楼层每层Floor上色、分离立面与屋面。你可以把floorHeight设为 4 米、FAR设为 3.5 试试。这时建筑总高度等于groundFloorHeight floorHeight * 楼层数但这里的楼层数并没有和容积率挂钩下一章我会讲怎么用面积参数倒推层数。现在先让模型“长起来”。3.3 街道与公共空间用道路中心线生成路网街道不要混在建筑规则里生成。我习惯单独写一个street.cga作用在道路中心线图层上。StartRule Street -- s(roadWidth, 0, roadWidth) t(0, 0, 0) i(assets/road_texture.jpg)三行规则的含义s(roadWidth, 0, roadWidth)把当前 scope 的宽度设为roadWidthY 方向为 0t(0,0,0)把 scope 平移回原点避免偏位i()实例化一张路面纹理。这里必须注意s()只改变当前 scope 的尺寸不改变道路长度长度由道路中心线本身的 extent 决定。所以用一个roadWidth参数控制机动车道宽度是很常见的做法。如果你想做步行道和路缘石就在Street里再切分Street -- split(x) { 0.5: Sidewalk | roadWidth: Roadway | 0.5: Sidewalk } Sidewalk -- color(#c8c8c8) Roadway -- color(#404040)split(x)沿当前 scope 的 X 轴切分两侧各 0.5 米是步行道中间是机动车道。这种写法的好处是路网总宽度变化时只要你改roadWidth步行道宽度保持不变。4. 规则库里的关键参数容积率、退线、楼层与朝向的数学关系参数是规则库的命门。很多项目最后卡在“生成结果和规划指标对不上”问题都出在这几组数学关系上。4.1 容积率和建筑面积为什么不能直接写整数容积率是地上总建筑面积除以用地面积。就算你在规则里写了FAR 3.5CityEngine 不会自己去算层数。你需要把地块面积和标准层面积的关系写出来。常见做法是在Lot入口处报告面积参数Lot -- report(plotArea, geometry.area) ...然后在体量规则里计算楼层数BuildingFootprint -- report(footprintArea, geometry.area) floors FAR * geometry.area / floorPlateArea extrude(world.y, floors * floorHeight)可这里有个陷阱geometry.area是当前形状的二维面积不是用地面积。如果你在已经setback之后的形状上算面积得到的是建筑投影面积用它算容积率就会偏小。正确做法是在Lot阶段就把用地面积存成一个属性Lot -- plotArea geometry.area report(plotArea, plotArea) setback(5) { all: BuildingFootprint | border: StreetLine } BuildingFootprint -- report(footprintArea, geometry.area) floors FAR * plotArea / geometry.area extrude(world.y, floors * floorHeight)参数说明plotArea在Lot阶段记录不随 setback 改变floors FAR * plotArea / geometry.area用容积率倒推层数如果地块形状不规则geometry.area是投影面积不考虑退台和阳台。这种写法下容积率变成硬约束。改FAR建筑层数会自动跟着变改地块面积建筑高度也会被重新计算。4.2 退线与日照距离函数不只是美化退线除了满足消防间距还会影响建筑实际可建范围。CGA 里用setback处理但它可以带多个参数控制不同方向Lot -- setback(3, 10, 6, 10) { left: FrontYard | front: BackYard | all: MainZone }四个数字分别对应四边顺序是 south, west, north, east。注意这里没有自动判断日照方向你需要根据项目地理位置确定南向在哪边。如果不做方向区分只写setback(5)所有退线距离一样遇到东北向道路就会和规划条件冲突。日照模拟不是 CGA 的主业但你可以用太阳角度做一个简单的阴影范围判断Building -- shadow tan(sunAngle) * buildingHeight setback(shadow) { all: ShadowArea }sunAngle需要手动输入不能从 CityEngine 场景直接读取。所以这个公式更适合做方案对比不适合做精确日照计算精确日照我建议导出到专业软件里跑。4.3 随机种子与城市肌理让结果可复现规则库里最怕的是“这次生成一个样下次生成另一个样”。这不是城市有性格是你没有控制随机种子。CGA 里任何随机函数都依赖当前形状的 seed只有用srand()手动固定Lot -- srand(20260214) ...固定 seed 后同一地块每次生成结果完全一致。如果你希望不同地块有不同形态但整体可复现可以根据地块 ID 生成种子Lot -- srand(20260214 objectId) ...objectId是每个图层要素自带的主键。这样每个地块有不同的随机形态但整条街道的生成结果可以稳定复现。对规划方案来说这一点特别重要不然汇报时动一下鼠标所有高层位置都变了甲方会以为你在变魔术。5. 避坑规则库运行翻车的典型场景规则库用得多了必然会翻车。我把最常见的几个坑按现象、原因、解决三层写出来方便你在现场照着排查。5.1 现象建筑之间横七竖八互相穿插甚至出现立体交叉原因多半是extrude用了局部坐标系而不是世界坐标系。局部坐标会随地块旋转导致建筑斜着长。解决统一用world.y不要用裸的yBuilding -- extrude(world.y, height)同时检查入口形状的 scope 方向如果地块朝向混乱在入口规则前加一句Lot -- alignScopeToGeometry(x, y, z) // 这里只是示意实际用 alignScopeToGeometry 需要先 geometry5.2 现象纹理整体发黑或者被拉成马赛克原因UV 坐标没有设置CityEngine 使用默认投影时纹理把一个 shape 整体铺满物体形状稍微复杂就拉伸。解决在立面规则前手动设置贴图坐标Facade -- setupProjection(0, scope.xy, 1, 1) set(material.colormap, assets/facade.jpg) projectUV(0)setupProjection(0, scope.xy, 1, 1)把纹理坐标按当前立面 scope 的 XY 方向对齐第二个参数换成scope.xy意思是忽略厚度projectUV(0)让纹理从 scope 的原点开始。调整后面的1,1可以控制纹理重复次数立面高度大的话建议改大这个数否则图案会被拉伸。5.3 现象导出 FBX 后材质全部丢失原因规则库里的纹理用了绝对路径模型导入其他软件后找不到原路径。解决把所有资产引用改为相对路径并在工程里统一放到assets/下set(material.colormap, /assets/facade.jpg)注意斜杠开头指的是工程根目录不是系统盘。移动规则库文件夹时保持assets和rules的相对位置不变导出就大概率正常。5.4 现象容积率统计总差一截原因很多规则分支最后没有汇总到同一个report字段或者split(y)切出的楼层没有被计算到面积里。解决只用一个字段累计并且确认所有分支都执行了 reportFootprint -- totalArea 0 split(y) { floorHeight: AddFloor }* AddFloor -- totalArea totalArea geometry.area report(grossFloorArea, totalArea)这里要特别注意CGA 的变量在递归分支间不是全局可写的。上面这种写法在实际运行中并不严谨因为每层递归都会产生新作用域。更稳妥的做法是在最终楼层属性里报告单层面积再在外部汇总或者直接用reports里的累加工具。最保险的是每个Floor都单独report(grossFloorArea, geometry.area)最后导出报表时再相加。5.5 现象换了一台电脑同样的规则生成结果完全不同原因默认 seed 基于形状的创建顺序不同的导入顺序会产生不同随机序列。解决在入口规则第一行固定 seed保证跨机器可复现StartRule Lot -- srand(20260214) ...如果你希望每个地块不同形态用objectId计算 seed而不是完全依赖运行环境。6. 验证与进阶用 reports 把规则库变成“规划计算器”规则库不是生成完模型就结束了真正的价值是把模型和指标连起来。我在日常项目中把reports当成了最常用的验证工具。在每个地块规则里写上报指标生成完点开报表容积率、建筑密度、建筑面积一目了然。Lot -- report(landArea, geometry.area) report(plotArea, geometry.area)在体量规则里补上建筑面积BuildingFootprint -- report(footprintArea, geometry.area) report(grossFloorArea, FAR * geometry.area)这样生成后选择所有地块导出表格直接就能和规划条件比对。不需要重新建模只需要调整参数再跑一轮。进阶用法是分层控制细节。我习惯在预览阶段关闭复杂纹理和模型用lod简化远处体量Floor -- case lod 0: Massing() else: DetailedFacade()lod是 CityEngine 的细节等级参数你可以在场景属性里设置。方案推敲阶段用lod0渲染表现阶段切回lod1生成速度和视觉质量两条路都走通。我现在的习惯是每天下班前把当天所有生成结果用reports导出一份指标明细和规划条件表逐条比对第二天再针对偏差调参。这样规则库才真正变成了方案计算器而不是一个“看起来像城市”的黑盒子。希望帮到你。本文还有配套的精品资源点击获取
