切换深浅主题
攻击、子弹与可达范围
当前实现没有“半径多少、最多几格”的固定射程。 每门炮从自己的真实炮口寻找颜色匹配、直线可达的目标;炮管转准后才发射。发射时扣弹并预占伤害,抵达时才真正扣血。
理解这套行为,要按顺序区分四件事:选谁 → 能否接触到 → 是否允许发射 → 发射后如何结算。
核对范围
本页依据 2026-10-30 工作区源码及共用配置整理,是当前 CubeLand 实现的说明。已有测试列在文末;本次文档整理未执行 Unity 运行测试。交互图解用于理解规则,不是运行中的 Unity 关卡。
先用图解理解遮挡
交互图解从炮口出发,看一条线先碰到谁。
可左右滑动图解,查看完整标签与射线。
后方红块被遮挡
蓝块虽然不匹配红炮颜色,仍是实体。所有采样线先碰到蓝块,红炮不能穿过它攻击后方红块。
示意图用于解释规则,不是 Unity 实测或完整模拟器。真实普通目标检查中心与六个近面点,共七条候选线;本二维图投影中,前后深度采样与中心方向重合,仅显示五个不同位置。绿色表示可达方向,实际代码找到第一条成功线就返回。
图解帮助区分“目标颜色匹配”和“通往目标的线没有被挡住”。实际游戏在三维空间内判定,并按下文检查多个采样方向。
一、从哪里开始判断
起点是炮台模型上的 FirePoint.position,即真实炮口。炮台底座、炮位中心和相机位置都不是正式攻击的起点。
每帧更新顺序为:模型转动和布局 → 炮台入槽运动 → 炮管瞄准 → 读取最新炮口 → 转换坐标 → 推进规则。
方块和机关范围保存在模型局部坐标中。炮口使用 ModelRoot.InverseTransformPoint 转入同一个空间;绘制子弹和命中特效时,再通过同一个 ModelRoot 转回世界坐标。
由此会出现这些合理结果:
- 两门炮位置不同,对同一个方块的可达结果也可能不同。
- 模型旋转后,原来被挡住的目标可能露出来。
- 相机看得见某个方块,不代表炮口到它之间没有障碍。
- 模型的位置、旋转和缩放会影响炮口与方块的空间关系。
因此,“可达范围”是每门炮在当前姿态下能直接接触到的目标集合,会随炮口和模型变化。
二、先选择哪些候选目标
BoardState.TryFindShot 先筛选同时满足以下条件的目标:
| 条件 | 含义 |
|---|---|
IsActive | 尚未消除 |
IsTargetable | 允许被普通炮弹直接攻击 |
AvailableHp > 0 | 还有未被在途子弹预占的血量 |
RequiredColor == 炮台颜色,或炮台颜色为 22 | 普通炮要求颜色匹配;彩虹炮可以通配颜色 |
候选按目标包围盒中心到炮口的局部空间三维距离从近到远排序;距离相同,目标 ID 较小者优先。
随后逐个检查可达性,返回第一个通过的目标。最近的同色方块完全被挡住时,会继续尝试更远的同色方块。
这里没有按屏幕上下、固定行列、颜色连通块或关卡 Depth 选目标。Depth 会保存在数据中,但普通射击的寻敌和遮挡判定不读取它。
三、“能打到”具体检查哪几条线
普通目标最多尝试 7 个瞄准点:先中心,再依次检查 X 负、X 正、Y 负、Y 正、Z 负、Z 正方向的六个近面中心点。
六个点的计算方式是:
text
瞄准点 = 目标中心 ± 对应坐标轴的半尺寸 × 0.98例如,中心在原点、边长为 1 的立方体,七个点为:
text
中心:(0, 0, 0)
左右:(-0.49, 0, 0)、(0.49, 0, 0)
上下:(0, -0.49, 0)、(0, 0.49, 0)
前后:(0, 0, -0.49)、(0, 0, 0.49)对每个点,从炮口连一条直线,检查这段线首先碰到的存活遮挡实体,是不是正在检查的目标本身。是,就成功;否则换下一个点。
普通目标的七条线都失败,才判定不可达。cover 在七点失败后,还会逐个尝试当前占用格的中心。
为什么中心被挡,还能打中
中心方向可能被一个小障碍挡住,但通向侧面采样点的方向仍然通畅。只要任意一条采样线先碰到目标,就能获得合法的射击方案。
为什么露出一个小角,仍可能打不到
这是有限采样,不是遍历目标的整张表面。如果露出的区域恰好没有被这些采样方向覆盖,仍会判定不可达。六个近面中心点也不是八个顶点。
子弹最后瞄向中心还是表面
用于试射的点通常在目标内部,但成功后保存的是射线进入实体的表面交点,即 ShotSolution.LocalHitPoint。可见子弹通常落在表面,不会一直钻到中心才算命中。
实现中射线长度为“炮口到本次瞄准点的距离 + 0.001”,后面的微小量用于数值容差,不是一个射程参数。起点与采样点距离小于 0.0001 的退化情况会直接接受;正常炮台布置不依赖这一分支。
四、什么东西会挡子弹
颜色负责筛选攻击目标;遮挡负责判断空间能否通过。 不匹配的颜色不会被当作透明物体。
| 实体 | 能被普通炮弹直接选中 | 是否遮挡 |
|---|---|---|
普通彩色方块 voxel | 能,需颜色匹配或彩虹 | 会,包括挡住同色后排 |
颜色 0 的墙 wall | 不能 | 会 |
大方块 large | 能,需颜色匹配或彩虹 | 按整个包围盒遮挡 |
覆盖物 cover | 能,需颜色匹配或彩虹 | 有占用格时按当前剩余格遮挡 |
围栏螺丝 screw | 能,需颜色匹配或彩虹 | 会;检查该螺丝时忽略它自己的围栏父体 |
doubleLarge 聚合父体 | 不能 | 父体本身不挡,由子实体决定 |
围栏 pen 父体 | 不能 | 会,自己的螺丝有上述例外 |
ice、colorBox、gift | 不能直接当普通炮弹目标 | 会,通过各自机制处理 |
| 尚未恢复行为的其他机关 | 不能 | 保留外壳遮挡,并记录未恢复规则 |
彩虹炮获得的是颜色通配能力,仍然遵守可攻击类型和遮挡限制。
几何范围与美术外观的区别
射线使用规则层的包围盒或占用格,不调用 Unity Physics.Raycast,也不沿美术网格的倒角或圆角检测。内部通过格子分桶和网格步进加速,再计算实体范围的交点。
射线是一条没有厚度的数学线。画面弹头的半径、拖尾宽度不参与可达性检测。大方块通常把整个包围盒视为实体;有占用格数据的 cover 则会保留真正的孔洞和已经释放的区域。
多个实体几乎同时被碰到时,代码使用微小容差比较,优先机关,再优先较小 ID;这种规则主要消除几何边界上的不确定性。
五、可达之后,为什么还不发射
几何可达只是前半段。真正发射还要求:
- 关卡可以推进,没有结束、暂停或交互冻结。
- 炮台已完成入槽或补位,正式炮口坐标已设置。
- 当前弹夹还有弹药,发射冷却已结束。
- 炮口已经转准。
- 准备发射的目标,与视觉层刚才瞄准的是同一个对象。
- 目标仍可预占伤害。
炮管以有限速度转动,不是瞬间完成瞄准。当前共用配置的转速为 540°/秒,俯仰目标角限制为 −10°~75°,水平没有额外扇形限制。
最终门槛是:炮口 forward 与弹道初始切线夹角不超过 2°。当前弧度为 0,静止起终点间的初始切线就是指向命中点的方向。
转动炮管会改变真实炮口位置,所以瞄准后会重新调用 CanReach。临发射时还会再核对当前目标与方向。
两门炮同时瞄准一块,会发生什么
第一门炮可能先把该目标的伤害额度预占满。规则层随后为另一门炮找到新目标时,必须确认它已经瞄准这个新目标,不能沿着旧方向直接开火。新目标通常要等下一次瞄准更新。
几何可见,但超出炮管俯仰能力
当前实现先选最近的几何可达目标,再检查瞄准门槛。首选目标没通过瞄准时,这一帧不会继续尝试另一块更远的目标。因此,排查“不发射”时要把几何遮挡与炮口姿态分开检查。
找不到可达目标时,已就位炮台保持上一次朝向,避免反复回正导致炮口位置变化,让边缘目标不断显隐。
六、发射、预占与伤害的时间线
普通射击的核心关系是:
text
AvailableHp = max(0, Hp − ReservedDamage)发射时先预占一点伤害,再扣除一发弹药、设置冷却并创建 ShotState。这一刻目标还没有因此扣血,也没有提前消失。
以一个一血方块为例:
| 时刻 | HP | 预占伤害 | 允许新子弹再选中 | 继续挡住后排 |
|---|---|---|---|---|
| 尚未发射 | 1 | 0 | 可以,需满足其他条件 | 是 |
| 已有一发在途中 | 1 | 1 | 不可以 | 是 |
| 子弹抵达并消除 | 0 | 0 | 不可以 | 否 |
两血目标则允许两发同时预占,第三发不会继续选它。这样既避免多炮向一血目标重复发射,又保留“外层未消除,后层仍然被挡”的关系。
每帧先推进已存在子弹,再处理本帧发射。抵达时释放预占并尝试 Board.Hit;只有目标仍存活、可攻击且颜色规则仍满足,才真正施加伤害。
彩虹炮的额外消耗
彩虹炮命中类型为 voxel、large 或 screw 的目标时,在发射阶段还会按目标颜色尝试消耗一发匹配的队列弹药,优先前排。它与发射炮台自身扣弹是两个动作,具体入口是 LevelSession.ConsumeQueuedBullet。
七、已经发出的子弹如何飞行
正式普通子弹使用规则层的生命周期和表现层的位置更新,没有在飞行中做物理碰撞检测。
发射后保留:
- 原目标对象。
- 发射瞬间的世界起点。
- 模型局部空间的命中点记录。
- 发射时确定的飞行时长。
每帧用模型当前变换把局部落点转回世界坐标。因此,模型旋转、平移或缩放时,子弹会继续追踪原目标。cover 在早一发子弹命中后缩短时,后续在途弹的当前落点会按新范围更新。
发射后不会重新检测沿途遮挡,也不会换成打中途出现的另一块方块。 这解释了为什么发射前需要无遮挡,但已发出的子弹仍能追着旋转后的目标命中。
如果目标被吸尘器等动作提前移除,在途弹会取消并释放预占,不再造成第二次伤害,也不自动改打别处;已经消耗的弹药不退回。重试、退出和显式清理在途弹时同样会释放预占。
八、射速、时长和弧线分别控制什么
以下为核对时 Art/Gameplay/Composition.asset 的共用值,运行时调参副本可以覆盖它们。
| 参数 | 当前共用值 | 含义 |
|---|---|---|
FireInterval | 0.035 秒 | 发射后设置的完整冷却 |
ProjectileFlightDuration | 0.7 秒 | 每颗正式新子弹的飞行生命周期 |
AimDegreesPerSecond | 540°/秒 | 瞄准转动速度 |
| 炮口容差 | 2° | 代码中的发射朝向门槛 |
MaximumElevation | 75° | 俯仰目标上限;下限为代码中的 −10° |
ProjectileArcHeight | 0 | 控制点的世界竖直抬升 |
ProjectileTrailDuration | 0.16 秒 | 视觉拖尾时长 |
ProjectileRadiusRange | 0.19 → 0.14 | 视觉弹体半径变化 |
0.035 秒不保证每秒 28.6 发
每门炮每帧最多发一发,发射后将冷却重置为完整间隔,不补发长帧积欠。稳定 60 FPS、普通时间倍率且其他门槛一直满足时,三帧才超过 0.035 秒,因此通常约 20 发/秒。目标可用性、瞄准和就位状态还会降低实际射速。
0.7 秒不按目标距离变化
正式表现层直接返回配置时长,没有按距离计算。远近目标使用相同时长时,远目标的视觉移动速度更快。
这是模拟时间:暂停或交互冻结时不推进,玩法时间倍率会改变现实耗时。规则层单独使用时存在 DefaultShotFlightDuration = 0.18 秒的默认值,但正式 CubeLandGame 安装了时长解析器,因此不能把这个规则层默认值当成正式游戏的 0.7 秒配置。
弧线不增加越障能力
普通弹控制点为:
text
控制点 = (世界起点 + 当前世界终点) / 2 + 世界向上方向 × 弧度当前弧度为 0,所以静止起终点之间的二次曲线退化成直线;沿线进度使用先加速后减速的经验曲线,并非匀速。模型在飞行期间移动时,终点持续改变。
调大弧度会改变可见轨迹和瞄准初始切线,但发射可达性仍然检查直线,没有变成曲线扫掠检测,不能据此认为子弹获得翻墙能力。
九、常见现象对照
| 看到的情况 | 当前判定 |
|---|---|
| 同色方块很远,但无遮挡且能瞄准 | 可以发射,没有独立距离上限 |
| 同色方块很近,但被异色方块完全挡住 | 不可达 |
| 前后两块同色,前块仍存在 | 前块仍可能挡住后块 |
| 中心方向受阻,某条侧面采样线通畅 | 可以找到表面命中点 |
| 只露出很小一个角,七条采样线均失败 | 仍不可达 |
| 相机看得见,炮口到它之间有障碍 | 不能据此认定可攻击 |
| 目标可达,但炮管仍在转向 | 暂不发射 |
| 一血目标已有一发在途 | 不追加攻击,目标继续挡住后排 |
| 发射后模型旋转 | 在途弹追踪原目标,不重新测遮挡 |
| 原目标提前被清除 | 在途弹取消,不自动转移伤害 |
十、按什么顺序排查“不发射”
- 检查关卡是否暂停、交互冻结、结束,炮台是否真的已入槽。
- 确认当前炮台颜色、弹量和冷却。
- 确认目标可攻击,且
Hp − ReservedDamage大于零。 - 用真实炮口检查七条采样方向的第一个遮挡实体。
- 确认炮口俯仰和方向能达到不超过 2° 的误差。
- 确认视觉瞄准目标与临发射候选仍一致。
- 区分“没有新弹发出”和“已有弹仍在飞行”;后者的伤害尚未到结算时刻。
停滞判负另有扫描流程,不能把“一段时间没开火”直接等同于失败,详见旋转、停滞与工具。
源码与验证入口
源码路径均相对项目根目录:
| 入口 | 负责内容 |
|---|---|
Assets/SGame/CubeLand/Runtime/Gameplay/BoardState.cs | TryFindShot、CanReach、TryAim、Raycast、预占与伤害 |
Assets/SGame/CubeLand/Runtime/Gameplay/TargetState.cs | HP、预占、可攻击与遮挡标记 |
Assets/SGame/CubeLand/Runtime/Gameplay/LevelSession.cs | 发射循环、冷却、在途推进和结算 |
Assets/SGame/CubeLand/Runtime/Gameplay/ShotState.cs | 发射快照和随 cover 更新的当前命中点 |
Assets/SGame/CubeLand/Runtime/Presentation/CubeLandGame.cs | 真实炮口、瞄准门控、坐标变换与飞行表现 |
Assets/SGame/CubeLand/Runtime/Presentation/CubeLandComposition.cs | 构图和弹道参数的使用方式 |
Assets/SGame/CubeLand/Art/Gameplay/Composition.asset | 上表中的正式共用配置 |
已有 Assets/SGame/CubeLand/Tests/Editor/GameplayContractsTests.cs 定义了中心遮挡但侧面可达、异色遮挡、发射预占但不提前伤害、瞄准门控、目标切换、cover 变形追踪、吸尘器取消在途弹及长帧不补发等契约测试。存在这些测试不等于本次已执行并通过。