城市与国家
This content is not available in your language yet.
世界地图上的 7 座城市分属 4 个国家,用 Voronoi 最近城市划分领海;每座非自由港城市拥有海上炮台防御与巡逻舰队,玩家靠港可交易、修船、招募、外交,摧毁全部炮台即可占领城市。
城市与国家系统是外循环(航海)的骨架:玩家驾船在海上航行时,脚下的每一片海都归属于某座城市(进而归属某个国家),或者是公海。城市是玩家补给的落脚点(靠港触发港口面板:交易 / 修船 / 招募 / 外交),也是军事目标(打掉全部海上炮台即可占领,改变领土归属并推动胜利进度)。
- 国家与城市的静态数据来自两张 CSV:
countries.csv、cities.csv(位于Assets/StreamingAssets/CitySystem/),策划可直接修改。 - 场景启动时由
CitySystemInitializer.cs按 CSV 自动生成所有国家与城市对象,并依次挂上交易、外交、防御、巡逻、显示等组件,最后构建 Voronoi 领地索引。 - 城市运行时注册到
CityManager.cs(全局单例),它同时对外提供“这个坐标属于哪个国家”的领地查询(TerritoryRegistry),供怪物、遭遇等系统消费。
世界缩放(必须先理解)
Section titled “世界缩放(必须先理解)”CSV 中的坐标与半径是“真实尺度”(real)。实际进游戏时所有位置与半径会按场景预设(preset)的 world 轴缩放(ScenarioPreset.cs 的 WorldConfig):
| world 预设 | positionScale(坐标缩放) | cityRadiusScale(城市半径缩放) | 使用场景 |
|---|---|---|---|
real.json |
1.0 | 1.0 | “正常游玩”预设、PlayMode 测试 |
compact.json |
0.1 | 0.2 | 大多数开发/AI 测试预设 |
来源:Assets/StreamingAssets/DataSets/world/real.json / compact.json(CSV 同级 JSON 配置,策划可改)。本页正文数值除特别注明外,均按 real(×1.0)描述。
公式(CitySystemInitializer.cs):
- 城市世界坐标 = CSV(position_x, position_z) × positionScale + positionOffset(positionOffset = 初始化器自身位置 + world 预设 offset,默认均为 0)
- 城市触发半径 = CSV radius × cityRadiusScale
国家与城市全表
Section titled “国家与城市全表”国家表(countries.csv,全量 4 行)
Section titled “国家表(countries.csv,全量 4 行)”| country_id | 国家名称 | 区域颜色 | 中心X | 中心Z | 领海半径 |
|---|---|---|---|---|---|
| country_001 | 西班牙 | #4169E1(宝蓝) | 0 | 0 | 0 |
| country_002 | 英格兰 | #DC143C(绯红) | 0 | 0 | 0 |
| country_003 | 法兰西 | #FFD700(金黄) | 0 | 0 | 0 |
| country_free | 自由港联合 | #C0C0C0(银灰) | 500 | 2500 | 0 |
说明:center_x/center_z/territory_radius 列当前不参与领土计算(领土完全由 Voronoi 决定,见下节);区域颜色用于小地图标记染色与战略地图填色(城市中心标记 Cube 恒为黄色,不随国家变色——CityColorMarker.cs 的 Renderer 染色引用未接线)。
城市表(cities.csv,全量 7 行)
Section titled “城市表(cities.csv,全量 7 行)”| city_id | 城市名称 | 所属国家 | 位置 (X, Z) | 半径 | 城市类型 | 海域类型 |
|---|---|---|---|---|---|---|
| city_001 | 马德里 | country_001 西班牙 | (200, 150) | 60 | Capital 首都 | Safe |
| city_002 | 塞维利亚 | country_001 西班牙 | (-1500, 1000) | 50 | IndustryCity 工业城 | Safe |
| city_003 | 普利茅斯 | country_002 英格兰 | (2000, 500) | 45 | FishingTown 渔村 | Safe |
| city_004 | 朴次茅斯 | country_002 英格兰 | (2500, 2000) | 55 | Fortress 要塞 | Moderate |
| city_005 | 自由港 | country_free 自由港联合 | (500, 2500) | 60 | TradePort 贸易港 | Safe |
| city_006 | 勒阿弗尔 | country_003 法兰西 | (-1000, 5000) | 55 | TradePort 贸易港 | Dangerous |
| city_007 | 马赛 | country_003 法兰西 | (2000, 5500) | 55 | TradePort 贸易港 | Dangerous |
- 半径单位:米(世界单位,real 尺度)。半径既是靠港触发距离也是编辑器 Gizmo 圈。
- use_random_offset / random_seed 列全部为 false/0,当前等同未启用(且生成路径会覆盖随机偏移结果,见“已知限制”)。
- sea_region_type 列(Safe/Moderate/Dangerous)目前没有任何运行时系统读取(见“已知限制”)。
- 城市类型(city_type)决定海上炮台防御规模,见“城市防御”。
自由港特殊规则
Section titled “自由港特殊规则”country_id 为 country_free 的城市(当前只有 city_005 自由港)在生成时跳过三个组件(CitySystemInitializer.cs 硬编码判断):
- 不挂
CityDefense.cs—— 不可被占领; - 不挂
CityDiplomacyTrigger.cs—— 无外交操作(永远中立); - 不挂
PatrolShipSpawner.cs—— 不产生巡逻舰; - 也不生成海上炮台。
自由港照常可交易/修船/招募。另外它不出售奢侈品梯度商品(CityTrader.cs 中产地距离衰减算法对 city_005 返回不上架)。
领土划分:Voronoi 最近城市
Section titled “领土划分:Voronoi 最近城市”实现:VoronoiTerritoryGrid.cs,由 CityManager.BuildTerritoryGrid 在所有城市生成完毕后构建。
规则(触发条件 → 规则 → 结果):
- 归属判定:任意海面点属于“离它最近的城市”,该城市属于谁,这片海就是谁的领海。没有按国家画圈的概念——国土 = 该国所有城市的 Voronoi 胞元之并。
- 公海判定:若该点到最近城市的距离 > 公海阈值,则视为公海(无归属国,CountryId 返回空)。
- 公海阈值 = 1500 米 × positionScale(
TerritoryConstants.cs代码常量DemoPublicSeaThresholdRealScale = 1500;compact 下为 150 米)。
- 公海阈值 = 1500 米 × positionScale(
- 世界边界:世界外接矩形 = 全部城市位置的包围盒向外扩 1500 米 × positionScale(
TerritoryConstants.cs常量WorldBoundsPaddingRealScale = 1500)。该矩形同时注册为全局 IWorldBounds,全屏地图等“显示整个世界”的界面统一使用。 - 栅格索引图:构建时烘焙一张栅格(列数 = 世界宽度 ÷ 50 米,截断到 [64, 256];行数按宽高比缩放,截断到 [64, 512]),每格存最近城市索引或公海标记 -1。栅格用于提取边界线;实时点查询不走栅格而是直接暴力遍历 7 个种子点取最近(更精确)。
- 边界三分类(
BoundarySegment.cs):相邻两格归属不同时产生一段边界,按两侧关系分为:- InterCountry(两侧不同国家)—— 国界,粗线渲染;
- IntraCountry(两侧同国不同城)—— 同国城界,细线/虚线渲染;
- PublicSeaEdge(一侧是公海)—— 领海外缘,银白色渲染。
- 城市易手:城市被占领后只改种子点的国别(位置不变),边界分类懒重算;不需要整体重建。城市位置变化才需要整体重建(
RebuildTerritoryGrid)。 - 对外查询:
TerritoryRegistry(全局注册点)提供GetCountryAtPosition/GetTerritoryAt,返回(归属国、最近城市、是否公海、到最近城市距离)。Boss 怪物等系统用它判断海域归属,见 遭遇与刷怪。
城市防御(CityDefense)
Section titled “城市防御(CityDefense)”旧的圆柱防御塔已被“海上炮台平台”(harbor platform)替代:每座非自由港城市在港口外海生成若干小型驻防船平台,平台上的机枪炮是城市防御的实体。
生成规则(CitySystemInitializer.cs):
- 触发时机:陆地(岛屿/港口)注册完成、且玩家船甲板铺设完成之后,按城市逐艘串行生成(防止与玩家船初始化竞争)。PlayMode 测试场景会整体抑制生成。
- 锚点:港口泊位(dock)位置;没有岛屿配置时退回城市中心。
- 布置:以泊位朝海方向为中轴,在 ±80° 扇形上等分布置,每座平台距锚点 50 米(代码常量
orbitRadius = 50f,朝向公式:方向 = forward × cos(角度) + right × sin(角度),角度在 -80° 到 +80° 间按序号线性插值)。 - 平台行为:生成后立即切入驻守状态(
GarrisonState.cs)——永不移动、进入最高战备(BattleStations)、每 1 秒扫描一次敌人(代码常量ScanInterval = 1f)、炮塔自动索敌开火;被打也不逃跑不追击。
城市类型 → 防御配置(CitySystemInitializer.GetGarrisonConfig,代码硬编码;平台本体数据在 Assets/StreamingAssets/DataSets/fleet/harbor_*.json,策划可改):
| 城市类型 | 平台组成 | 总机枪数 | 适用城市 |
|---|---|---|---|
| FishingTown 渔村 | 1 × harbor_picket | 1 | 普利茅斯 |
| TradePort 贸易港 | 2 × harbor_battery | 4 | 勒阿弗尔、马赛 |
| IndustryCity 工业城 | 2 × harbor_battery | 4 | 塞维利亚 |
| Fortress 要塞 | 2 × harbor_battery + 1 × harbor_bastion | 8 | 朴次茅斯 |
| Capital 首都 | 1 × harbor_picket + 2 × harbor_bastion + 1 × harbor_citadel | 15 | 马德里 |
平台规格表(来源:harbor_picket/battery/bastion/citadel.json,JSON 配置,策划可改):
| 平台 | 甲板尺寸 | 机枪(MachineGun_Prod) | 船员 | 子弹储备 | 食物/水 |
|---|---|---|---|---|---|
| harbor_picket 哨戒台 | 6×6 格 | 1 门 | 1 炮手 | 500 发 | 各 5 |
| harbor_battery 炮组台 | 8×8 格 | 2 门 | 2 炮手 + 1 乘务 | 1200 发 | 各 12 |
| harbor_bastion 棱堡台 | 10×10 格 | 4 门 | 4 炮手 + 1 乘务 | 2400 发 | 各 24 |
| harbor_citadel 卫城台 | 14×14 格 | 6 门 | 6 炮手 + 1 乘务 + 1 军医 | 5000 发 | 各 50 |
机枪本体的血量/伤害不在本系统配置——它就是普通的船载武器建筑,数值见 武器与弹药 与 建筑。
占领判定(CityDefense.cs):
- 每座平台生成完毕后,扫描其上所有有人武器的可受伤组件,逐门注册到该城的
CityDefense(计入“防御塔总数”)。 - 每门炮被摧毁时存活计数 -1,并记录最后攻击者阵营。
- 存活计数归零 → 城市被占领(一次性,不可逆):
- 占领者阵营解析:攻击者是 “Player” → 取玩家当前国籍;攻击者是 AI 国家 → 直接用其国家 ID。
- 若解析出有效国家:城市改挂到新国家(颜色、领地立即变更)。玩家无国籍(海盗)时城市归属不变,但防御已永久清零、不再重建。
- 同步广播占领消息:刷新城市颜色标记(
CityColorMarker.cs)、更新 Voronoi 种子国别(CityManager.cs)、推进胜利进度(VictoryManager)、结算阵营声望(FactionEventHandler,见 阵营与外交)。
驻防冬眠(GarrisonHibernator.cs,Inspector 序列化字段,运行时动态挂载故实际取代码默认值):
| 参数 | 值 | 说明 |
|---|---|---|
| 唤醒半径 | 150 米 | 玩家船距平台小于此值 → 启用全部组件与渲染 |
| 休眠半径 | 200 米 | 大于此值 → 禁用全部组件与渲染(滞回防抖) |
| 检查间隔 | 0.5 秒 |
冬眠只切组件开关、不销毁实体,因此 CityDefense 持有的炮塔受伤引用始终有效——玩家远程炮击休眠中的平台照样能打掉并触发占领。
关于 CityDefenseConfig.csv(Assets/StreamingAssets/FactionSystem/,全量 5 行):
| 城市类型 | tower_count | tower_hp | city_hp |
|---|---|---|---|
| FishingTown | 1 | 500 | 1000 |
| TradePort | 2 | 800 | 2000 |
| IndustryCity | 2 | 1000 | 2500 |
| Fortress | 4 | 1500 | 5000 |
| Capital | 6 | 2000 | 8000 |
注意:这张表目前没有任何运行时代码消费(只有自动生成的读表类 CityDefenseConfig.cs,无调用方)。它对应旧的“圆柱防御塔”设计,已被上述海上炮台方案取代。改这张表不会改变游戏行为,详见“已知限制”。
巡逻舰:生成与虚拟巡逻模拟
Section titled “巡逻舰:生成与虚拟巡逻模拟”每座非自由港城市挂一个 PatrolShipSpawner.cs,目标是让本国海域常驻 2-4 艘巡逻舰。为了性能,巡逻舰平时只是数据层“虚拟船”(一条记录),由全局 PatrolSimulationManager.cs 低频推演位置与战斗;玩家靠近时才实例化为真实船(激活),远离即销毁回数据层(去激活)。
生成参数(PatrolShipSpawner.cs,Inspector 序列化字段,组件为运行时动态挂载故实际取代码默认值):
| 参数 | 值 | 单位 | 说明 |
|---|---|---|---|
| 每城目标数量 | 2 / 3 / 4 随机 | 艘 | 城市初始化时随机一次(均匀分布) |
| 补充检查间隔 | 15 | 秒 | 存活数 < 目标数 → 注册 1 艘新虚拟船 |
| 开局错峰延迟 | 0 - 5 随机 | 秒 | 防止全部城市同帧涌入 |
| 生成最小间距 | 30 | 米 | 新船出生点与已有巡逻船距离下限(最多重抽 16 次) |
| 激活半径 | 150 | 米 | 玩家到虚拟船距离小于此值 → 实例化 |
| 去激活半径 | 200 | 米 | 大于此值 → 销毁实体回虚拟态(1.33 倍滞回) |
| 巡逻半径 | 80 | 米 | 虚拟巡逻航点采样基准半径 |
| 巡逻海域倍率 | 2.5 | 倍 | PatrolZone 半径 = 80 × 2.5 = 200 米 |
| 舰队配置 | pirate_sloop | — | 代码常量,所有城市巡逻舰同款 |
巡逻海域(PatrolZone)(PatrolZone.cs,全部代码常量):以泊位为圆心、半径 200 米的圆,按 8 米 × 8 米网格离散化为海面格列表;剔除“在岛上或距任一岛岸线不足 12 米安全裕量”的格子,再从泊位侧做四连通洪泛填充,只保留与泊位连通的海面(岛屿背面的飞地海域被排除)。出生点与巡逻航点全部从这些格子里随机采样,保证永不穿陆。
虚拟模拟(PatrolSimulationManager.cs):
| 参数 | 值 | 单位 | 来源 |
|---|---|---|---|
| 模拟 tick 间隔 | 1 | 秒 | Inspector 序列化字段(动态挂载,实际取代码默认值) |
| 虚拟航速 | 5 | 米/秒 | 代码常量 VirtualMoveSpeed |
| 航点到达判定 | < 1 | 米 | 代码常量,到达后换下一个随机航点 |
| 虚拟战斗距离 | 50 | 米 | Inspector 序列化字段 |
| 激活检查间隔 | 2 | 秒 | Inspector 序列化字段 |
| 激活最小距离 | 80 | 米 | Inspector 序列化字段,玩家太近不刷脸(等虚拟船漂远再激活) |
| 远巡概率 | 30% | — | Inspector 序列化字段(仅在 PatrolZone 构建失败的回退路径生效) |
| 远巡半径倍率 | 2.5 | 倍 | 同上 |
虚拟战斗:每个模拟 tick,任意两艘未激活、相距 ≤ 50 米且阵营互相敌对(按阵营关系表,见 阵营与外交)的虚拟船互相结算一次伤害:
单 tick 伤害 = round(10 × 攻方战力 ÷ max(1, 守方战力))(双向同时结算)
战力(CombatRating)与血量在注册时从舰队 JSON 推算(PatrolShipSpawner.cs):
战力 = max(1, 名字含 “Gun”/“Cannon” 的建筑数 + 船员数) 血量 = 100 + 甲板边长² + 建筑数 × 5
代入 pirate_sloop.json(6×6 甲板、8 个建筑、其中 1 门机枪、4 名船员):战力 = 5,血量 = 100 + 36 + 40 = 176。两艘同级巡逻舰对打,每 tick 互扣 10 点,约 18 秒分出同归于尽的结果。
战损掉落(虚拟船血量归零时,PatrolSimulationManager.cs 代码常量):在沉没点 ±10 米圆内随机生成 2-4 堆漂浮物,每堆从下表随机抽一种,存活 600 秒后消失(走战利品冬眠系统,见 战利品与掉落):
| 物品 | 每堆数量 |
|---|---|
| Wood 木材 | 3 - 6 |
| CannonShell 炮弹 | 2 - 4 |
| Bullet 子弹 | 5 - 10 |
激活 / 去激活规则:
- 激活:玩家到虚拟船距离落在 [80, 150) 米区间 → 在虚拟船当前位置实例化真实船(行为 = 巡逻,索敌距离 50 米),漫游锚点固定在泊位、绳长(LeashRadius)= 80 × 2.5 × 1.2 = 240 米,航点继续从 PatrolZone 采样。同一时刻只允许一艘船在激活流程中(互斥锁)。
- 去激活:距离 > 200 米 → 记录实体当前位置回写虚拟记录,销毁实体。血量不回写——下次激活仍是满血(MVP 简化)。
- 激活船被击沉:实体销毁被管理器检测到 → 虚拟记录血量置 0 → 走战损掉落与移除。
- 城市被占领后:旧国别的虚拟船不再计入新主人的配额,生成器会按新国别补满 2-4 艘;旧船保留旧阵营继续游荡直到被消灭。
开局护航:若某城泊位距玩家出生点 < 50 米,该城固定立刻生成并激活 1 艘巡逻舰作为开局护航(绕过 80 米最小激活距离),叙事上是“护航船”。
港口交互(靠港触发什么)
Section titled “港口交互(靠港触发什么)”CityTradingTrigger.cs 每帧检测玩家船与城市的距离。触发参考点优先使用岛屿系统注册的泊位(dock)位置,无岛屿时退回城市中心(见 岛屿与陆地);触发距离 = 城市半径(CSV radius × cityRadiusScale,real 下 45-60 米)。
进入半径时(一次性触发,直到离开):
- 记录停靠港(仅非敌对城):写入“最后停靠港口”,用于死亡复活点与船员压力的“靠岸破除”(见 船员);敌对城不算靠岸。
- 打开港口面板:右侧主面板切换为 Harbor 面板(
Core/UI/MainRight/Harbor/HarborPanel.cs,UI Toolkit,见 界面总览)。 - 招募进港:通知招募系统玩家进入该城(敌对城跳过)。
- 发现登记:标记该城为“已发现”,自动连带发现所属国家(first-meet 雾战机制,见 探索与情报)。
- 港口简报:收集自上次简报以来的世界新闻 + 为该城生成传闻,广播简报消息(见 探索与情报)。
离开半径时:清除停靠状态、关闭 Harbor 面板、若正在与该城交易则强制结束交易、关闭招募面板。
Harbor 面板四按钮:
| 按钮 | 行为 | 敌对城 |
|---|---|---|
| 交易 | 打开交易会话(隐藏 Harbor 面板,交易结束且仍在港内时自动切回) | 禁用 |
| 修船 | 一键修复全部损毁地板 + 排空积水,按损毁数量扣金币(费用公式见 船只),金币不足弹红色飘字;支付成功播放金币从船飞向城市的特效 | 禁用 |
| 招募 | 打开该城招募面板(见 船员) | 禁用 |
| 外交 | 打开外交面板 | 始终可用(允许在敌对国港口求和) |
敌对判定 = 玩家与城市所属国家的阵营关系为 Hostile(实时刷新:停火成功立即解锁按钮,不必离港重进)。
外交操作(CityDiplomacyTrigger.cs,代码常量;机制详情见 阵营与外交):
| 操作 | 前置条件 | 花费(金币) |
|---|---|---|
| 贸易协定 | 关系非敌对 | 5000 |
| 停火协议 | 关系为敌对 | 10000 |
| 申请国籍 | 由阵营系统校验 | 0 |
交易价格的关系修正(CityTrader.cs 代码常量;交易系统全貌见 交易):
| 玩家与城市所属国关系 | 买入价倍率 | 卖出价倍率(NPC 付给玩家) |
|---|---|---|
| 中立 Neutral | ×1.2 | ×(1/1.2) ≈ ×0.83 |
| 友好 Friendly | ×1.0 | ×1.0 |
| 友好 + 同国籍 | ×0.9 | ×(1/0.9) ≈ ×1.11 |
| 敌对 Hostile | ×1.5(保底,正常流程到不了交易) | ×(1/1.5) ≈ ×0.67 |
城市在售商品来自 CityInventory.csv(运行时默认读 Assets/StreamingAssets/DataSets/trading/standard/CityInventory.csv,41 条数据行,列:city_id / item_id / stock 库存 / buy_price 买入价 / sell_price 卖出价;预设 trading 轴填 rich 时改读 DataSets/trading/rich/ 同名表),并叠加奢侈品产地距离梯度商品。每条商品换算规则(CityTrader.cs):库存上限 = stock × 2,初始库存 = stock,每日补货 = stock ÷ 5。完整商品经济见 交易 与 配置表总览。
城市 LOD 与显示
Section titled “城市 LOD 与显示”城市标记(CitySystemInitializer.cs):每城生成一个 10 米见方的黄色 Cube 作为中心标记(Inspector 序列化字段 _cityMarkerSize = 10、颜色黄色),并挂小地图标记(颜色取国家区域色,尺寸 = 触发半径)。
发现显隐(CityDiscoveryVisibility.cs):未发现的城市隐藏标记 Cube、禁用颜色染色组件;首次进入其触发半径被“发现”后永久显示。战略地图上未发现城市的领海填透明色、相关边界隐藏、点击不响应。
名字标签(运行时动态创建的世界空间文字,参数为代码默认值):
| 标签 | 显示高度阈值 | 字号 | 高度缩放系数 | 位置 |
|---|---|---|---|---|
城市名(CityNameLabel.cs) |
相机高度 ≥ 1000 米 | 50 | 0.005(标签随高度线性放大,保证屏幕大小恒定) | 城市上方 5 米,平铺水面 |
国家名(CountryNameLabel.cs) |
相机高度 ≥ 1000 米 | 90 | 0.012 | 该国全部城市位置的几何中心,高 8 米 |
两者都受发现状态约束:未发现的城市/国家即使高度达标也不显示。
领地渲染双模式(TerritoryRenderer.cs,相机高度驱动,阈值为 Inspector 序列化字段、运行时自动创建故取代码默认值 1000 米):
-
航行模式(高度 < 1000 米,
SailingBorderRenderer.cs):只渲染玩家周围 200 米内的边界段为发光条带(宽 6 米、贴水面 0.2 米、每 0.25 秒刷新一次)。条带颜色按“边界另一侧国家与玩家的关系”决定:状况 颜色 另一侧友好 绿 另一侧中立 黄 另一侧敌对 红 公海边缘 银白 一侧国家未发现 灰 两侧都未发现 / 公海边缘的陆侧未发现 不绘制 -
战略模式(高度 ≥ 1000 米,
StrategicMapRenderer.cs):整海面铺一张政治填色图(宽 256 像素的烘焙贴图、整体透明度 0.4)+ 边界线(国界宽 12 米、同国城界宽 4 米)。各国领海填国家区域色;公海填深蓝;敌对国领海向红色混合 40%;未发现国家领海透明(雾战)。 -
战略点击(
StrategicMapInteractor.cs):设计为仅战略模式下左键点选城市(射线 5000 米)触发城市点击事件,由外交面板订阅打开,未发现的城市点击无效。但该组件从未被挂载(见“已知限制”),事件实际不会触发。
关系/声望变化会即时通知两个渲染器重算颜色。
公式与数值(集中表格)
Section titled “公式与数值(集中表格)”| 项目 | 公式 / 数值 | 单位 | 来源 |
|---|---|---|---|
| 城市世界坐标 | CSV 坐标 × positionScale + positionOffset | 米 | cities.csv + world/*.json |
| 城市触发半径 | CSV radius × cityRadiusScale | 米 | 同上 |
| 公海阈值 | 1500 × positionScale | 米 | 代码常量 TerritoryConstants.cs |
| 世界边界外扩 | 1500 × positionScale | 米 | 同上 |
| Voronoi 栅格列数 | clamp(世界宽 ÷ 50, 64, 256) | 格 | 代码 VoronoiTerritoryGrid.cs |
| 炮台距泊位 | 50 | 米 | 代码常量 CitySystemInitializer.cs |
| 炮台扇形 | ±80°,按序号等分 | 度 | 同上 |
| 驻守扫描间隔 | 1 | 秒 | 代码常量 GarrisonState.cs |
| 驻防冬眠唤醒/休眠/检查 | 150 / 200 / 0.5 | 米/米/秒 | 代码默认值 GarrisonHibernator.cs |
| 占领条件 | 该城注册的全部炮塔摧毁(存活数 ≤ 0) | — | CityDefense.cs |
| 每城巡逻舰目标数 | 随机 2 / 3 / 4 | 艘 | 代码 PatrolShipSpawner.cs |
| 巡逻补充间隔 | 15 | 秒 | 代码默认值 PatrolShipSpawner.cs |
| 巡逻激活 / 去激活 | [80, 150) 激活;> 200 去激活 | 米(玩家到虚拟船距离) | 代码默认值 PatrolSimulationManager.cs / PatrolShipSpawner.cs |
| 巡逻海域半径 | 80 × 2.5 = 200 | 米 | 代码默认值 PatrolShipSpawner.cs |
| 巡逻海域网格 / 离岸裕量 | 8 / 12 | 米 | 代码常量 PatrolZone.cs |
| 虚拟航速 | 5 | 米/秒 | 代码常量 PatrolSimulationManager.cs |
| 虚拟模拟 tick / 激活检查 | 1 / 2 | 秒 | 代码默认值 PatrolSimulationManager.cs |
| 虚拟战斗距离 | 50 | 米 | 同上 |
| 虚拟战斗伤害 | round(10 × 攻方战力 ÷ max(1, 守方战力)) / tick,双向 | 点 | PatrolSimulationManager.ComputeDamage(有单元测试佐证) |
| 巡逻舰战力 | max(1, 含 Gun/Cannon 建筑数 + 船员数);pirate_sloop = 5 | 点 | 代码 PatrolShipSpawner.cs |
| 巡逻舰血量 | 100 + 甲板边长² + 建筑数 × 5;pirate_sloop = 176 | 点 | 同上 |
| 激活后绳长 | 巡逻半径 × 2.5 × 1.2 = 240 | 米 | 代码 PatrolSimulationManager.cs |
| 战损掉落 | 2-4 堆,±10 米散布,600 秒消失 | — | 代码常量 PatrolSimulationManager.cs |
| 开局护航判定 | 泊位距玩家出生点 < 50 米 → 1 艘立即激活 | 米 | 代码常量 PatrolShipSpawner.cs |
| 贸易协定 / 停火花费 | 5000 / 10000 | 金币 | 代码常量 CityDiplomacyTrigger.cs |
| 关系价格倍率 | 中立 ×1.2;友好同籍 ×0.9;卖出取倒数 | — | 代码常量 CityTrader.cs |
| 商品库存换算 | 上限 = stock × 2;日补 = stock ÷ 5 | 件 | 代码 CityTrader.cs(stock 来自 CityInventory.csv) |
| 名字标签显示阈值 | 城市 / 国家均为相机高度 ≥ 1000 | 米 | 代码默认值 CityNameLabel.cs / CountryNameLabel.cs |
| 领地渲染模式切换 | 相机高度 ≥ 1000 → 战略填色 | 米 | 代码默认值 TerritoryRenderer.cs |
| 航行边界条带 | 半径 200 内、宽 6、0.25 秒刷新 | 米/米/秒 | 代码默认值 SailingBorderRenderer.cs |
| 战略边界线宽 | 国界 12 / 同国城界 4 | 米 | 代码默认值 StrategicMapRenderer.cs |
| 修船金币飞行特效 | 3-15 枚硬币(按金额 ÷ 500 上限插值)、飞行 1.2 秒、弧高 8 米 | — | 代码默认值 WorldGoldFlyEffect.cs |
与其他系统的交互
Section titled “与其他系统的交互”- 阵营与外交:城市的国家归属决定靠港待遇(敌对禁交易/修船/招募)、巡逻舰敌我、边界条带颜色;占领城市会结算声望并可改变领土;外交三操作(贸易协定/停火/国籍)入口在港口面板。见 阵营与外交。
- 交易:城市是主要交易对手,商品表
CityInventory.csv+ 奢侈品产地梯度 + 关系价格修正。见 交易。 - 探索与情报:first-meet 发现机制控制城市/国家的标签、领海填色、点击交互可见性;靠港触发新闻简报与传闻。见 探索与情报。
- 岛屿与陆地:泊位(dock)由岛屿系统注册,决定靠港触发点、炮台锚点、巡逻海域中心;巡逻海域剔除陆地依赖岛屿多边形。见 岛屿与陆地。
- 遭遇与刷怪:领地查询(公海/归属国)供 Boss 怪物与遭遇系统判断海域;城市的海域类型列预留给该系统但未接通。见 遭遇与刷怪。
- 船只 / 船员:靠港记录是死亡复活点与船员压力“靠岸破除”的依据;修船走船体浮力系统的修复费用。见 船只、船员。
- 武器与弹药 / 伤害与护甲:炮台平台上的机枪是普通船载武器,受伤/死亡走通用伤害管线,城市占领由武器的可受伤组件死亡事件驱动。见 武器与弹药、伤害与护甲。
- 战利品与掉落:虚拟巡逻舰战损掉落注册到战利品冬眠管理器。见 战利品与掉落。
- 游戏循环与胜负:城市占领消息推进胜利进度(VictoryManager / 胜利 HUD)。见 游戏循环与胜负。
- 界面总览:Harbor 面板挂 MainRight 槽位;战略地图点击打开外交 Modal。见 界面总览。
配置与调参指南
Section titled “配置与调参指南”| 想调整的体验 | 改哪里 | 类型 |
|---|---|---|
| 增删城市、改位置/归属/半径/类型 | Assets/StreamingAssets/CitySystem/cities.csv,改后需跑 CSV 代码生成(Tools → CSV Code Generator → Generate All) |
CSV |
| 增删国家、改国家颜色 | Assets/StreamingAssets/CitySystem/countries.csv(颜色为十六进制) |
CSV |
| 城市在售商品与基础价 | Assets/StreamingAssets/DataSets/trading/standard/CityInventory.csv(默认)/ trading/rich/(预设覆盖) |
CSV |
| 某类城市的防御强度(平台组成) | CitySystemInitializer.cs 的 GetGarrisonConfig(城市类型 → 平台清单) |
代码 |
| 单种平台的火力/船员/补给 | Assets/StreamingAssets/DataSets/fleet/harbor_*.json |
JSON 配置 |
| 巡逻舰船型与装备 | Assets/StreamingAssets/DataSets/fleet/pirate_sloop.json(船型名本身是 PatrolShipSpawner.cs 代码常量) |
JSON / 代码 |
| 每城巡逻数量、激活距离、巡逻半径 | PatrolShipSpawner.cs 序列化字段默认值(组件运行时动态挂载,prefab 上改不到,需改代码默认值) |
代码 |
| 虚拟战斗强度 / 掉落 | PatrolSimulationManager.cs(伤害公式、掉落表、TTL 均在此) |
代码 |
| 公海范围大小 | TerritoryConstants.cs 的 DemoPublicSeaThresholdRealScale |
代码 |
| 世界整体缩放(测试用小地图) | Assets/StreamingAssets/DataSets/world/compact.json 或新建 world 预设并在 Assets/StreamingAssets/Presets/*.json 引用 |
JSON 配置 |
| 外交花费 | CityDiplomacyTrigger.cs 代码常量(5000 / 10000) |
代码 |
| 关系价格倍率 | CityTrader.cs 的 GetRelationPriceModifier |
代码 |
| 边界条带 / 战略填色样式 | SailingBorderRenderer.cs / StrategicMapRenderer.cs 颜色与宽度常量 |
代码 |
| 名字标签出现高度与字号 | CityNameLabel.cs / CountryNameLabel.cs 序列化字段默认值(动态挂载) |
代码 |
已知限制与注意事项
Section titled “已知限制与注意事项”CityDefenseConfig.csv是死配置:tower_count / tower_hp / city_hp 没有任何运行时消费方,对应已被替代的“圆柱防御塔”旧设计。实际防御强度由GetGarrisonConfig(代码)+harbor_*.json(舰队配置)决定。改这张 CSV 无效。cities.csv的 sea_region_type 列未接通:Safe/Moderate/Dangerous 当前没有系统读取。cities.csv的随机偏移列实际失效:use_random_offset / random_seed 虽有完整的位置生成逻辑(CityPositionGenerator.cs),但CitySystemInitializer.GenerateCity随后会用“CSV 原始坐标 × 缩放 + 偏移”直接覆盖城市位置,随机偏移结果被丢弃。当前 CSV 全部为 false/0,无实际影响。CityLODSwitcher.cs(3D 模型 / 2D 图标 LOD 切换,阈值 800 米)从未被挂载:初始化器不添加它,场景与 prefab 中也无引用,属于未接线的预置组件。当前城市视觉只是标记 Cube,没有 3D 模型 LOD。- 战略层显示阈值与相机上限存在矛盾:主相机(
ZoomCamera.cs)距离上限 500 米、俯角上限 80°,可达最大高度约 500 × sin(80°) ≈ 492 米,达不到名字标签 / 战略填色的 1000 米阈值。全图视角实际由按 M 打开的全屏小地图(UI Toolkit)承担,世界空间的战略填色、城市/国家名标签、战略点击在默认相机参数下不可见。TerritoryRenderer.cs注释中的“Strategic 2500m”为过时描述。 - 代码注释中的炮台规格已过时:
CitySystemInitializer.cs注释写“Picket 4×4·1 炮…首都 17 炮”,实际 JSON 为 6×6/8×8/10×10/14×14,首都合计 15 门机枪。以 JSON 为准。 - 玩家无国籍(海盗)占领城市不变更归属:防御被永久清零(
IsConquered = true,不会重建防御),但城市仍挂在原国家名下,颜色与领地不变。先入籍再攻城才能“插旗”。 - 巡逻舰激活时血量重置满血:虚拟态战损与实体战损互不延续(MVP 简化);玩家把激活的巡逻舰打残后走远再回来,它又是满血。
- 巡逻激活/驻防冬眠半径是绝对值,不随 world 缩放:compact(×0.1)下 150 米激活半径相对世界明显偏大(注释承认 compact 城间距仅 150-600 米),数值是按 compact 体验调的,real 模式下未单独校准。
Service/CitySystem/UI/HarborPanel.cs是自毁桩:旧 UGUI 港口面板已废弃,运行时 Awake 即销毁自身;真正的港口面板在Core/UI/MainRight/Harbor/。- 城市防御注册不去重:同一门炮如果被重复注册会被计数两次(调用方保证唯一性,当前生成路径不会重复)。
countries.csv的 center_x / center_z / territory_radius 列不参与任何领土计算(Voronoi 完全由城市位置驱动),仅自由港联合填了非零值,无实际作用。Assets/StreamingAssets/CitySystem/CityInventory.csv是无人读取的陈旧副本:运行时商品表实际从DataSets/trading/standard/(默认)或DataSets/trading/rich/(预设 trading 轴覆盖)加载。改CitySystem/目录下那份不生效。