炮塔和子弹
喜欢我石墨雷光吗?
Mindustry 包含自动化与塔防(Tower Defense)元素。炮塔是防御体系的核心组件,也是自动化系统的资源消耗节点,并作为科技树推进的重要环节。炮塔发射的子弹类型(BulletType)是内容系统的重要组成部分,本节将介绍炮塔与子弹的相关机制。
炮塔的基本构成包括子弹类型(BulletType)、射击方式(ShootPattern)、炮塔绘制器(Drawer)中的绘制部件(DrawPart)、冷却剂系统以及方块基础属性,下文将逐一说明。 为了方便理解以下内容,这里提供一个最小模板:
new ItemTurret("tutorial-item-turret"){{
requirements(Category.turret, with(Items.copper, 39));
ammo(Items.copper, new BasicBulletType(1.5f, 9));
shoot = new ShootPattern();
drawer = new DrawTurret(){{
parts.add(new RegionPart("-barrel"));
}};
consumePower(40f);
coolant = consumeCoolant(0.1f);
}};ItemTurret("tutorial-item-turret").apply {
requirements(Category.turret, with(Items.copper, 39))
ammo(Items.copper, BasicBulletType(1.5f, 9))
shoot = ShootPattern()
drawer = DrawTurret().apply{
parts.add(RegionPart("-barrel"))
}
consumePower(40f)
coolant = consumeCoolant(0.1f)
}子弹类型(BulletType)
子弹类型规定了子弹的类型、运动、大小、伤害、能力、目标、行为、功能、与其他子弹的交互、绘制和特效等。在此无法全部列举,如需完整了解可参阅源代码。子弹并不局限于传统意义的金属子弹,例如太空环境下的液体泡(SpaceLiquidBulletType),或没有贴图的 EmptyBulletType,以及激光、水珠、点激光等。非炮塔方块也能发射子弹,例如质量驱动器的 MassDriverBolt。整体而言,子弹是具有确定速度和寿命、由类炮塔方块发射的实体。
要新建一个子弹类型,只需要实例化一个 BulletType 的具体子类:
new BasicBulletType(1.5f, 9);BasicBulletType(1.5f, 9)其中构造方法的两个参数分别为子弹速度和伤害。在 v6 版本之前,子弹类型作为独立的内容类(mindustry.content.BulletType)进行加载。当前版本中,该类虽已不再承担原有的加载功能,但子弹类型仍属于内容体系,是 Content 的子类。
BulletType 是子弹类型的抽象基类,所有具体子弹类型都从它派生。这意味着不能直接实例化 BulletType 本身。从功能上看,它自身不具备绘制贴图的能力,仅负责绘制尾迹(Trail)和配合炮塔绘制器工作。以下简要介绍一些重要字段:
1. 核心属性 (Core Properties)
这些是定义一个子弹最基本行为的字段。
lifetime: 子弹的生命周期,单位是游戏刻(ticks)。超过这个时间,子弹会自然消失(触发despawn)。lifeScaleRandMin,lifeScaleRandMax: 子弹生成时,其生命周期会乘以一个在这两个值之间随机选取的系数,用于增加随机性。speed: 子弹的初始速度(单位/刻)。velocityScaleRandMin,velocityScaleRandMax: 子弹生成时,其速度会乘以一个在这两个值之间随机选取的系数。damage: 子弹的直接撞击伤害。hitSize: 子弹的碰撞箱(Hitbox)大小,用于检测与实体(单位、建筑)的碰撞。drawSize: 子弹的绘制大小和世界裁剪范围。如果子弹超出视口这个范围,就不会被绘制。angleOffset,randomAngleOffset:angleOffset是固定的角度偏移,randomAngleOffset是随机的角度偏移范围。两者都会在子弹生成时加到其初始角度上。drag: 子弹的拖拽/阻力系数。每帧速度会乘以(1 - drag),使其逐渐减慢。accel: 子弹的加速度(单位/刻²)。每帧速度会增加这个值。layer: 子弹绘制的Z层(层级),用于控制渲染顺序(例如在单位上方还是下方)。setDefaults: 如果为true,引擎会自动设置一些合理的默认值(例如,如果有闪电效果,则默认状态效果为“电击”)。
2. 穿透与碰撞 (Piercing & Collision)
控制子弹如何与场景中的其他实体交互。
pierce: 子弹是否可以穿透单位。pierceBuilding: 子弹是否可以穿透建筑。pierceCap: 子弹最多可以穿透多少个实体。-1表示无限穿透。pierceDamageFactor: 子弹每穿透一个实体后,其伤害减少的系数。伤害减少值为初始生命值 * pierceDamageFactor。removeAfterPierce: 如果为true,当子弹伤害因穿透降至0或以下时,子弹会被移除。如果为false,即使伤害为0也会继续飞行。maxDamageFraction: 对单个目标的伤害上限。伤害不会超过目标最大生命值 * maxDamageFraction。laserAbsorb: (通常用于穿透激光)是否可以被塑钢墙(Plastanium Wall)吸收。optimalLifeFract: 生命百分比(0到1之间),在此时间点子弹处于最佳状态(用于连续武器的计算)。collidesTiles: 是否与地形块(Tile)碰撞。collidesTeam: 是否与同队(相同队伍)的实体碰撞。collidesAir,collidesGround: 是否与空中或地面单位碰撞。collides: 总开关,是否与任何东西发生碰撞。如果为false,则忽略其他碰撞设置。collideFloor: 是否与非表面的地板(如深水)碰撞。collideTerrain: 是否与静态墙壁(如山脉)碰撞。hittable: 该子弹是否可以被防御塔(Point Defense)击中。reflectable: 该子弹是否可以被反射(例如被盾牌反射)。absorbable: 该子弹是否可以被护盾吸收。sticky: 子弹是否会“粘”在碰撞到的第一个实体上,并停止运动。stickyExtraLifetime: 子弹粘附后,其生命周期会增加的时间。
3. 效果与音效 (Effects & Sounds)
控制子弹创建、命中、消失时的视听效果。
hitEffect: 子弹击中某个目标时播放的效果(Effect)。despawnEffect: 子弹生命周期结束时播放的效果。shootEffect: 子弹被发射时播放的效果。chargeEffect: (通常用于单发武器)开始充能时播放的效果。smokeEffect: 发射时产生的额外烟雾效果。hitSound,despawnSound: 击中目标和自然消失时播放的音效。hitSoundPitch,hitSoundVolume: 击中音效的音调和音量。hitColor: 用于命中(hit)和消失(despawn)效果的颜色。hitShake,despawnShake: 击中目标和自然消失时造成的屏幕震动强度。
4. 发射器与武器属性 (Turret/Weapon Properties)
这些属性主要影响发射该子弹的武器(如炮塔)的行为。
inaccuracy: 发射时的额外不准确度(散射),单位是角度。ammoMultiplier: 每份弹药(物品/液体)能发射多少颗这种子弹。reloadMultiplier: 乘以炮塔的装填速度,得到最终的发射速率。buildingDamageMultiplier: 对建筑造成的伤害倍率(乘以damage)。shieldDamageMultiplier: 对护盾造成的伤害倍率。recoil: 发射时施加给发射者的后坐力。killShooter: 发射这颗子弹是否会杀死发射者(用于自杀式攻击)。targetBlocks,targetMissiles: (炮塔AI用)是否以区块和导弹为目标。keepVelocity: 子弹的初始速度是否继承发射者的速度。scaleLife: 是否根据到目标的距离缩放生命周期(用于 artillery 类武器)。ignoreSpawnAngle: 如果为true,创建子弹时传入的角度参数将被忽略(通常用于效果子弹,其角度由其他因素决定)。createChance: 子弹被成功创建的概率(0到1之间)。range: (由calculateRange()计算得出)子弹的理论最大射程。maxRange,rangeOverride,rangeChange,extraRangeMargin,minRangeChange: 一系列复杂参数,用于覆盖、调整和计算炮塔的实际攻击范围。
5. 特殊效果与行为 (Special Effects & Behaviors)
子弹除了直接撞击外可以触发的其他效果。
splashDamage: 范围溅射伤害。scaledSplashDamage: 溅射伤害是否根据单位碰撞箱大小进行“正确”的缩放。splashDamageRadius: 溅射伤害的半径。splashDamagePierce: 溅射伤害是否穿透地形。knockback: 击中单位时造成的击退力。impact: 击退方向是否遵循子弹的方向(true),而不是碰撞点与单位中心的方向(false)。status,statusDuration: 击中时施加的状态效果及其持续时间。healPercent,healAmount: 如果子弹是治疗弹,这两个参数决定治疗量(基于最大生命值的百分比和固定值)。healColor,healEffect: 治疗时效果的颜色和效果类型。lifesteal: 造成伤害后,治疗发射者的比例。makeFire: 是否在击中点生成火焰。instantDisappear: 子弹生成后是否立即消失(用于实现某些瞬时效果)。despawnHit: 子弹自然消失时是否也播放命中效果(hit)。如果子弹有碎片、溅射等效果,此值会自动设为true。
6. 分裂子弹 (Fragmentation)
子弹在命中或消失时产生其他子弹。
fragBullet: 分裂出的子弹类型。delayFrags: 是否将分裂子弹的创建延迟到下一帧(用于解决穿透子弹的复杂伤害计算问题)。fragOnHit: 是否在击中时产生分裂子弹。fragOnAbsorb: 是否在被护盾吸收时产生分裂子弹。fragRandomSpread: 分裂子弹的随机角度扩散范围。fragSpread: 分裂子弹之间的均匀角度间隔。fragAngle: 分裂子弹的基础角度偏移。fragBullets: 产生的分裂子弹数量。fragVelocityMin,fragVelocityMax: 分裂子弹速度的随机范围(乘以基础速度)。fragLifeMin,fragLifeMax: 分裂子弹生命周期的随机范围(乘以基础生命周期)。fragOffsetMin,fragOffsetMax: 分裂子弹生成位置距离父子弹的随机偏移量。pierceFragCap: 如果子弹可以穿透,它最多可以释放多少次分裂子弹。
7. 间隔子弹 (Interval Bullets)
子弹在飞行过程中定期发射其他子弹。
intervalBullet: 定期发射的子弹类型。bulletInterval: 发射间隔时间(刻)。intervalBullets: 每次间隔发射的子弹数量。intervalRandomSpread: 间隔子弹的随机角度扩散。intervalSpread: 多个间隔子弹之间的角度间隔。intervalAngle: 间隔子弹的角度偏移。intervalDelay: 开始发射间隔子弹前的初始延迟。
8. 生成单位 (Spawning Units)
子弹本身可以生成一个单位(如导弹),或在命中/消失时生成单位。
spawnUnit: 子弹本身被替换成的单位类型(例如,发射器射出的其实是一个导弹单位)。despawnUnit: 在命中或消失时生成的单位类型。despawnUnitChance: 生成该单位的概率。despawnUnitCount: 生成单位的数量。despawnUnitRadius: 生成单位位置相对于子弹的随机偏移半径。faceOutwards: 生成的单位是否面朝外(远离子弹中心),而不是面朝子弹的方向。
9. 视觉部件与轨迹 (Visual Parts & Trail)
控制子弹的自定义外观。
parts: 一个DrawPart序列,用于为子弹添加复杂的自定义绘制部件。trailColor: 轨迹的颜色。trailChance: 每帧产生轨迹效果的概率。trailInterval: 产生轨迹效果的固定间隔时间(刻)。如果 >0,则优先于trailChance。trailMinVelocity: 产生轨迹效果所需的最小速度。trailEffect: 产生的轨迹效果(通常是粒子效果)。trailSpread: 轨迹效果的随机位置偏移。trailParam: 传递给轨迹效果的参数(通常控制大小)。trailRotation:trailParam参数是否使用子弹的旋转角度。trailLength: 轨迹网格的长度(渲染为一条带)。如果 >0,会启用另一种连续的轨迹渲染方式。trailWidth: 轨迹网格的宽度。trailSinMag,trailSinScl: 轨迹宽度的正弦波波动幅度和 scale。trailInterp: 轨迹宽度随子弹生命周期变化的插值方式。
10. 运动模式 (Movement Patterns)
特殊的子弹运动行为。
circleShooter: 子弹是否尝试环绕发射者飞行。circleShooterRadius: 环绕的目标半径。circleShooterRadiusSmooth: 环绕时转向的平滑过渡系数circleShooterRotateSpeed: 环绕旋转的速度乘数。homingPower: 追踪目标的能力强度(转向速度)。homingRange: 追踪传感器的范围。homingDelay: 开始追踪前的延迟时间。followAimSpeed: 子弹跟随发射者准星的速度(用于玩家控制的单位)。weaveScale,weaveMag,weaveRandom: 控制子弹“蛇形”运动的参数(Scale,幅度,是否随机初始方向)。rotateSpeed: 子弹速度向量自身的旋转速度(度/刻)。
11. 其他效果 (Other Effects)
lightning: 击中时产生的闪电链数量。lightningColor: 闪电颜色。lightningLength,lightningLengthRand: 闪电基础长度和随机额外长度。lightningDamage: 闪电造成的伤害(如果为负数,则使用子弹的damage)。lightningCone,lightningAngle: 闪电的扩散锥形角度和基础角度偏移。lightningType: 在闪电端点创建的子弹类型(用于二次攻击)。incendAmount,incendSpread,incendChance: 生成火焰的数量、扩散范围、概率。puddles,puddleRange,puddleAmount,puddleLiquid: 生成液体坑的数量、位置范围、液体量、液体类型。suppressionRange,suppressionDuration,suppressionEffectChance,suppressColor: 抑制敌人方块回复的效果范围、持续时间、生效几率、效果颜色。lightRadius,lightOpacity,lightColor: 子弹发出的动态光照的半径、透明度、颜色。
12. 杂项与内部字段 (Misc & Internal)
underwater: (高度实验性) 是否在水下渲染。spawnBullets: 与此子弹同时创建的其他子弹(用于视觉效果,如枪口的多发子弹)。spawnBulletRandomSpread: 上述同时创建的子弹的随机角度扩散。displayAmmoMultiplier: 是否在游戏内统计信息中显示弹药倍率。statLiquidConsumed: 如果 >0,这个值(除以ammoMultiplier)会显示在统计信息中,用于液体弹药。cachedDps: 内部缓存字段,用于存储估计的每秒伤害值(DPS),避免重复计算。
BulletType类的字段之间存在特定的搭配规则和互斥关系,需要通过测试来验证其实际效果。
关于代码内部的距离单位,有的是格,有的是世界单位,它们之间的换算关系是1格=8世界单位=32方块贴图像素。
对于具有实体体积的子弹,BulletType的子类BasicBulletType通常已能满足基本需求,其他子类主要是在此基础上预设了不同的字段值。在原版中,这种不重写方法而仅通过预设字段来定义不同行为的方式称为模板(Template)。以下是一些常见的实体子弹类型:
BasicBulletType:常规子弹,例如“双管”使用的子弹;BombBulletType:轰炸弹,例如“天垠”使用的子弹,命中后停留并造成范围伤害;MissileBulletType:导弹,例如“蜂群”使用的子弹,会生成尾迹效果;ArtilleryBulletType:炮弹,例如“冰雹”使用的子弹,可提前结束生命周期并带有尾迹特效;FlakBulletType:高射炮弹,例如“分裂”使用的子弹,可在目标附近爆炸,无需直接命中;LaserBoltBulletType:激光弹,例如“新星”使用的子弹,在基础贴图上叠加绘制两条彩色光线;InterceptorBulletType:拦截弹,与PointDefenseBulletWeapon配合使用,当前版本中未实际应用;EmpBulletType:电磁脉冲弹,例如“龙王”使用的子弹,可降低敌方建筑速度并对范围内单位与建筑造成伤害;MassDriverBolt:质量驱动器抛射物,由质量驱动器发射,可造成伤害。其内容物会影响伤害值。在v149版本中,Anuke添加了使用质量驱动器击杀敌人的成就。
综上所述,BulletType系统虽然核心是为有实体、有弹道的抛射物(如炮弹、子弹)设计的,但其灵活的设计也允许它服务于另一类特殊的攻击形式。BulletType类的这种特殊用法称为“虚拟子弹”或“空子弹”模式,适用于需要子弹系统管理生命周期和触发逻辑,但不需要实际弹道运动的情况。通过将speed设为0、collide设为false,可以创建一个静止的子弹实体,然后通过其draw和update方法实现远程绘制和伤害效果,常用于激光武器、持续光束、区域效果等非弹道型攻击。
PointLaserBulletType:用于采矿激光,例如“光辉”,其视觉效果与钻头光束类似;PointBulletType:用于点防御激光,例如“裂解”,表现为绘制一条线段并消除命中的子弹;LightningBulletType:用于释放闪电链,例如“电弧”;ShrapnelBulletType:用于发射激光尖刺,例如“雷光”;LaserBulletType:用于发射激光束,例如“蓝瑟”;LiquidBulletType:用于发射液体球体,例如“波浪”;RailBulletType:用于模拟轨道炮攻击,例如“厄兆”,表现为瞬间命中目标;ExplosionBulletType:用于创建爆炸效果,例如“遏止”主炮的终结攻击;SapBulletType:用于施加抑制效果,例如“血蛭”,表现为绘制紫色线条并施加状态效果;ContinuousLaserBulletType:用于持续激光武器,例如“熔毁”,开火后可维持一段时间;ContinuousLiquidBulletType:用于持续液体喷射武器,例如“升华”,开火后可无限期维持;MultiBulletType:用于组合上述多种子弹类型的效果。
以上就是原版中的主要子弹类型了。未来你也可以自己定制子弹类型,并进一步理解子弹的生命周期与背后的 ECS(Entity-Component-System)思想。
射击方式(ShootPattern)
ShootPattern:定义了发数shots、首发延迟firstShotDelay和间发延迟shotDelay。该模式为默认射击方式,通常设置为单发,也存在如“天灾”设置为两发的实例;ShootBarrel:所有炮管依次发射,通过barrels设置炮管的x、y坐标和方向,例如“蜂群”。受炮塔结构限制,其炮管无法像单位的武器那样独立运作;ShootAlternate:多个间距相等、沿x轴排列的炮管依次发射,通过barrels设置炮管数量,spread设置炮管间距。例如“双管”,以及多管炮塔如四管的“发散”和五管的“天谴”;ShootSpread:多个间隔角度相等的子弹同时发射,通过spread设置间隔角度(角度制),例如“雷光”;ShootSine:多个发射角度呈正弦周期变化的子弹同时发射,原版内容中未使用此模式;ShootHelix:使发射出的子弹按正弦曲线运动,例如“天谴”;ShootSummon:在一定区域范围内发射角度随机的子弹,例如“魔灵”;ShootMulti:可组合上述多种射击模式。
上述“依次发射”是指,当shots设定的子弹数量大于炮管数量时,子弹会按炮管顺序循环发射。例如“蜂群”有三个炮管,若设置shots=4,则每次射击会依次使用三个炮管发射共四发子弹。
炮塔绘制器
炮塔和工厂一样拥有drawer字段,常与炮塔搭配的drawer是DrawTurret,用于将炮塔的完整贴图分解为多个可独立运动的DrawPart(绘制部件),通过控制每个部件的位移、旋转等参数,实现动态视觉效果,例如原版“魔灵”炮塔的分块动画。
首先介绍DrawTurret所需的贴图。DrawTurret使用的贴图均为可选,包括大小预览贴图-preview、液体层贴图-liquid、顶层贴图-top、热量贴图-heat和基座贴图-base。这些贴图仅在找到时被绘制,不会产生“ohno”贴图。对于流体层贴图,还需要指定liquidDraw来设置要绘制的流体。
DrawPart
mindustry.entities.part.DrawPart 是可独立运动的绘制部件抽象基类,DrawTurret 渲染时会为每个部件填充一组 PartParams(位置、旋转、热量、装填进度等)。你可以把它理解为“把炮塔贴图拆成多块,然后按开火过程驱动它们运动”的系统。
DrawPart 基础字段(所有部件通用)
| 字段 | 说明 |
|---|---|
params | 全局复用的 PartParams 实例,供渲染流程临时写入数据。 |
turretShading | 是否使用炮塔阴影/着色,通常由引擎自动设置。 |
under | 是否绘制在炮塔本体下层。 |
weaponIndex | 单位武器部件索引,影响进度来源。 |
recoilIndex | 使用哪一个后坐力计数器,<0 表示基础后坐。 |
PartParams(绘制参数)字段一览
| 字段 | 说明 |
|---|---|
warmup | 持续开火的升温进度(0~1)。 |
reload | 装填进度(刚开火为 1,装填完成为 0)。 |
smoothReload | 平滑后的装填进度。 |
heat | 发射后热量进度(1→0)。 |
recoil | 原始后坐力值。 |
life | 生命周期进度(仅部分弹体/单位部件使用)。 |
charge | 蓄能进度(0→1)。 |
x | 绘制坐标 X。 |
y | 绘制坐标 Y。 |
rotation | 绘制旋转角度。 |
sideOverride | 强制使用某一侧的渲染索引,-1 为默认。 |
sideMultiplier | 侧向渲染倍率(常用于镜像)。 |
PartMove(额外位移片段)字段一览
| 字段 | 说明 |
|---|---|
progress | 位移片段跟随的进度。 |
x | 额外平移 X。 |
y | 额外平移 Y。 |
gx | 额外缩放 X。 |
gy | 额外缩放 Y。 |
rot | 额外旋转。 |
RegionPart
用于绘制贴图区域,是最常用的 DrawPart,适合炮管、装甲、炮口等“真实贴图”部件。
贴图命名:默认拼接 炮塔名 + suffix,并自动读取 -outline、-heat、-light;若 mirror=true,则读取 -r/-l 与 -r-outline/-l-outline。
RegionPart 字段一览
| 字段 | 说明 |
|---|---|
suffix | 贴图名后缀(默认拼接在炮塔名后)。 |
name | 完整贴图名,设置后会覆盖默认拼接。 |
heat | 热量贴图区域(-heat)。 |
light | 发光贴图区域(-light)。 |
regions | 主贴图区域数组。 |
outlines | 描边贴图区域数组(-outline)。 |
mirror | 是否左右镜像(需 -l/-r 贴图)。 |
outline | 是否绘制描边。 |
replaceOutline | 是否用原贴图替换描边(原版用法)。 |
drawRegion | 是否绘制主贴图(可用于仅热量效果)。 |
heatLight | 热量贴图是否产生光照。 |
clampProgress | 是否把进度夹在 0~1。 |
progress | 位置/旋转跟随的进度。 |
growProgress | 缩放跟随的进度。 |
heatProgress | 热量透明度跟随的进度。 |
blending | 贴图混合模式。 |
layer | 绘制层级。 |
layerOffset | 层级偏移。 |
heatLayerOffset | 热量层级偏移。 |
turretHeatLayer | 使用炮塔热量层时的层级。 |
outlineLayerOffset | 描边层级偏移。 |
x | 基础位置 X。 |
y | 基础位置 Y。 |
xScl | 基础缩放 X。 |
yScl | 基础缩放 Y。 |
rotation | 基础旋转角度。 |
originX | 旋转原点 X 偏移。 |
originY | 旋转原点 Y 偏移。 |
moveX | 随进度平移 X。 |
moveY | 随进度平移 Y。 |
growX | 随进度缩放 X。 |
growY | 随进度缩放 Y。 |
moveRot | 随进度旋转角度。 |
heatLightOpacity | 热量光照强度。 |
color | 主贴图颜色。 |
colorTo | 主贴图渐变目标色。 |
mixColor | 混色颜色。 |
mixColorTo | 混色渐变目标色。 |
heatColor | 热量贴图颜色。 |
children | 子部件序列。 |
moves | 额外位移片段序列。 |
ShapePart
用于绘制几何图形(多边形/圆形/线框),常用于能量护环、魔法阵、简化装饰。
ShapePart 字段一览
| 字段 | 说明 |
|---|---|
circle | 是否绘制圆形。 |
hollow | 是否绘制空心(线框)。 |
sides | 多边形边数。 |
radius | 基础半径。 |
radiusTo | 目标半径(插值)。 |
stroke | 基础描边宽度。 |
strokeTo | 目标描边宽度(插值)。 |
x | 基础位置 X。 |
y | 基础位置 Y。 |
rotation | 基础旋转角度。 |
moveX | 随进度平移 X。 |
moveY | 随进度平移 Y。 |
moveRot | 随进度旋转角度。 |
rotateSpeed | 自转速度。 |
color | 基础颜色。 |
colorTo | 目标颜色(渐变)。 |
mirror | 是否镜像绘制。 |
clampProgress | 是否把进度夹在 0~1。 |
progress | 进度来源。 |
layer | 绘制层级。 |
layerOffset | 层级偏移。 |
HaloPart
用于绘制“环绕式”图形:多个形状围绕中心旋转,常见于仪式感或科技感炮塔。
HaloPart 字段一览
| 字段 | 说明 |
|---|---|
hollow | 是否空心。 |
tri | 是否使用三角光片模式。 |
shapes | 围绕形状数量。 |
sides | 单个形状边数。 |
radius | 单个形状半径。 |
radiusTo | 目标半径(插值)。 |
stroke | 描边宽度。 |
strokeTo | 目标描边宽度(插值)。 |
triLength | 三角光片长度。 |
triLengthTo | 目标三角长度(插值)。 |
haloRadius | 环半径。 |
haloRadiusTo | 目标环半径(插值)。 |
x | 基础位置 X。 |
y | 基础位置 Y。 |
shapeRotation | 单个形状基础旋转。 |
moveX | 随进度平移 X。 |
moveY | 随进度平移 Y。 |
shapeMoveRot | 随进度形状旋转。 |
haloRotateSpeed | 环自转速度。 |
haloRotation | 环基础旋转角度。 |
rotateSpeed | 单个形状自转速度。 |
color | 基础颜色。 |
colorTo | 目标颜色(渐变)。 |
mirror | 是否镜像绘制。 |
clampProgress | 是否把进度夹在 0~1。 |
progress | 进度来源。 |
layer | 绘制层级。 |
layerOffset | 层级偏移。 |
FlarePart
用于绘制“光芒/星芒”效果,由多组三角形叠加构成,适合充能、激发类视觉。
FlarePart 字段一览
| 字段 | 说明 |
|---|---|
sides | 光芒数量。 |
radius | 基础长度。 |
radiusTo | 目标长度(插值)。 |
stroke | 光芒宽度。 |
innerScl | 内层光芒缩放。 |
innerRadScl | 内层光芒长度缩放。 |
x | 基础位置 X。 |
y | 基础位置 Y。 |
rotation | 基础旋转。 |
rotMove | 随进度旋转。 |
spinSpeed | 自旋速度。 |
followRotation | 是否跟随炮塔旋转。 |
color1 | 外层颜色。 |
color2 | 内层颜色。 |
clampProgress | 是否把进度夹在 0~1。 |
progress | 进度来源。 |
layer | 绘制层级。 |
EffectSpawnerPart
用于在一个矩形范围内刷出粒子/特效,适合火花、能量溢散等效果。
EffectSpawnerPart 字段一览
| 字段 | 说明 |
|---|---|
x | 生成区域中心 X。 |
y | 生成区域中心 Y。 |
width | 生成区域宽度。 |
height | 生成区域高度。 |
rotation | 生成区域旋转角度。 |
mirror | 是否镜像生成。 |
effectChance | 生成概率。 |
effectRot | 固定旋转角度。 |
effectRandRot | 随机旋转幅度。 |
effect | 特效类型。 |
effectColor | 特效颜色。 |
useProgress | 是否让概率随进度变化。 |
progress | 进度来源。 |
debugDraw | 是否绘制调试红框。 |
HoverPart
用于绘制“悬浮环/扫描圈”一类的脉动线框效果,常用于支撑感与能量感装饰。
HoverPart 字段一览
| 字段 | 说明 |
|---|---|
radius | 环半径。 |
x | 基础位置 X。 |
y | 基础位置 Y。 |
rotation | 基础旋转角度。 |
phase | 脉动周期。 |
stroke | 最大线宽。 |
minStroke | 最小线宽。 |
circles | 环数量。 |
sides | 多边形边数。 |
color | 颜色。 |
mirror | 是否镜像绘制。 |
layer | 绘制层级。 |
layerOffset | 层级偏移。 |
PartProgress
PartProgress 是“进度驱动器”,决定部件随什么参数变化。下面表格列出全部可用项。
| 名称 | 类型 | 说明 |
|---|---|---|
reload | 内置进度 | 刚开火为 1,装填完成为 0。 |
smoothReload | 内置进度 | 平滑后的装填进度。 |
warmup | 内置进度 | 持续开火时升到 1,停火后回落。 |
charge | 内置进度 | 蓄能进度(0→1)。 |
recoil | 内置进度 | 原始后坐力值。 |
heat | 内置进度 | 发射后升温,再冷却回 0。 |
life | 内置进度 | 生命周期进度(仅部分弹体/单位部件使用)。 |
time | 内置进度 | 当前 Time.time 值。 |
constant(value) | 工具方法 | 固定进度值。 |
get(p) | 接口方法 | 取进度值,通常由引擎调用。 |
getClamp(p) | 工具方法 | 获取并夹到 0~1(默认夹取)。 |
getClamp(p, clamp) | 工具方法 | 可选择是否夹取。 |
inv() | 链式方法 | 取反(1 - 值)。 |
slope() | 链式方法 | 斜率形状(Mathf.slope)。 |
clamp() | 链式方法 | 强制夹到 0~1。 |
add(amount) | 链式方法 | 加上常数。 |
add(other) | 链式方法 | 与另一个进度相加。 |
delay(amount) | 链式方法 | 延迟开始。 |
curve(offset, duration) | 链式方法 | 指定区间映射。 |
sustain(offset, grow, sustain) | 链式方法 | 生长-维持-衰减曲线。 |
shorten(amount) | 链式方法 | 压缩时长。 |
compress(start, end) | 链式方法 | 在指定区间内压缩。 |
blend(other, amount) | 链式方法 | 与另一个进度线性混合。 |
mul(other) | 链式方法 | 与另一个进度相乘。 |
mul(amount) | 链式方法 | 乘以常数。 |
min(other) | 链式方法 | 取较小值。 |
sin(offset, scl, mag) | 链式方法 | 正弦叠加(带偏移)。 |
sin(scl, mag) | 链式方法 | 正弦叠加。 |
absin(scl, mag) | 链式方法 | 绝对正弦叠加。 |
mod(amount) | 链式方法 | 取模循环。 |
loop(time) | 链式方法 | 按时间循环到 0~1。 |
apply(other, func) | 链式方法 | 用 PartFunc 组合两个进度。 |
curve(interp) | 链式方法 | 应用插值函数。 |
示例:让炮管在开火后回缩,并在末段加一点呼吸抖动:
var part = new RegionPart("-barrel");
part.progress = PartProgress.recoil;
part.moveY = -3f;
part.moves.add(new PartMove(PartProgress.recoil.delay(0.6f), 0f, -0.6f, 0f));此处我们以魔灵为例,结合代码解析以上内容:
malign = new PowerTurret("malign"){{
requirements(Category.turret, with(Items.carbide, 200, Items.beryllium, 1000, Items.silicon, 500, Items.graphite, 500, Items.phaseFabric, 200));
var haloProgress = PartProgress.warmup;
Color haloColor = Color.valueOf("d370d3"), heatCol = Color.purple;
float haloY = -15f, haloRotSpeed = 1.5f;
var circleProgress = PartProgress.warmup.delay(0.9f);
var circleColor = haloColor;
float circleY = 25f, circleRad = 11f, circleRotSpeed = 3.5f, circleStroke = 1.6f;
shootSound = Sounds.shootMalign;
loopSound = Sounds.loopMalign;
loopSoundVolume = 1.3f;
shootType = new FlakBulletType(8f, 70f){{
sprite = "missile-large";
lifetime = 40f;
width = 12f;
height = 22f;
hitSize = 7f;
shootEffect = Fx.shootSmokeSquareBig;
smokeEffect = Fx.shootSmokeDisperse;
ammoMultiplier = 1;
hitColor = backColor = trailColor = lightningColor = circleColor;
frontColor = Color.white;
trailWidth = 3f;
trailLength = 12;
hitEffect = despawnEffect = Fx.hitBulletColor;
buildingDamageMultiplier = 0.3f;
trailEffect = Fx.colorSpark;
trailRotation = true;
trailInterval = 3f;
homingPower = 0.17f;
homingDelay = 19f;
homingRange = 160f;
explodeRange = 100f;
explodeDelay = 0f;
flakInterval = 20f;
despawnShake = 3f;
intervalBullet = new LightningBulletType() {{
lightningColor = circleColor;
lightningCone = 15f;
lightningLength = 35;
lightningLengthRand = 5;
damage = 18f;
}};
fragBullet = new LaserBulletType(65f){{
colors = new Color[]{haloColor.cpy().a(0.4f), haloColor, Color.white};
buildingDamageMultiplier = 0.25f;
width = 19f;
hitEffect = Fx.hitLancer;
sideAngle = 175f;
sideWidth = 1f;
sideLength = 40f;
lifetime = 22f;
drawSize = 400f;
length = 120f;
pierceCap = 2;
optimalLifeFract = 1f;
}};
intervalBullets = 1;
fragSpread = fragRandomSpread = intervalRandomSpread = 0f;
bulletInterval = 20f;
splashDamage = 0f;
hitEffect = Fx.hitSquaresColor;
collidesGround = true;
}};
size = 5;
drawer = new DrawTurret("reinforced-"){{
parts.addAll(
//summoning circle
new ShapePart(){{
progress = circleProgress;
color = circleColor;
circle = true;
hollow = true;
stroke = 0f;
strokeTo = circleStroke;
radius = circleRad;
layer = Layer.effect;
y = circleY;
}},
new ShapePart(){{
progress = circleProgress;
rotateSpeed = -circleRotSpeed;
color = circleColor;
sides = 4;
hollow = true;
stroke = 0f;
strokeTo = circleStroke;
radius = circleRad - 1f;
layer = Layer.effect;
y = circleY;
}},
//outer squares
new ShapePart(){{
progress = circleProgress;
rotateSpeed = -circleRotSpeed;
color = circleColor;
sides = 4;
hollow = true;
stroke = 0f;
strokeTo = circleStroke;
radius = circleRad - 1f;
layer = Layer.effect;
y = circleY;
}},
//inner square
new ShapePart(){{
progress = circleProgress;
rotateSpeed = -circleRotSpeed/2f;
color = circleColor;
sides = 4;
hollow = true;
stroke = 0f;
strokeTo = 2f;
radius = 3f;
layer = Layer.effect;
y = circleY;
}},
//spikes on circle
new HaloPart(){{
progress = circleProgress;
color = circleColor;
tri = true;
shapes = 3;
triLength = 0f;
triLengthTo = 5f;
radius = 6f;
haloRadius = circleRad;
haloRotateSpeed = haloRotSpeed / 2f;
shapeRotation = 180f;
haloRotation = 180f;
layer = Layer.effect;
y = circleY;
}},
//actual turret
new RegionPart("-mouth"){{
heatColor = heatCol;
heatProgress = PartProgress.warmup;
moveY = -8f;
}},
new RegionPart("-end"){{
moveY = 0f;
}},
new RegionPart("-front"){{
heatColor = heatCol;
heatProgress = PartProgress.warmup;
mirror = true;
moveRot = 33f;
moveY = -4f;
moveX = 10f;
}},
new RegionPart("-back"){{
heatColor = heatCol;
heatProgress = PartProgress.warmup;
mirror = true;
moveRot = 10f;
moveX = 2f;
moveY = 5f;
}},
new RegionPart("-mid"){{
heatColor = heatCol;
heatProgress = PartProgress.recoil;
moveY = -9.5f;
}},
new ShapePart(){{
progress = haloProgress;
color = haloColor;
circle = true;
hollow = true;
stroke = 0f;
strokeTo = 2f;
radius = 10f;
layer = Layer.effect;
y = haloY;
}},
new ShapePart(){{
progress = haloProgress;
color = haloColor;
sides = 3;
rotation = 90f;
hollow = true;
stroke = 0f;
strokeTo = 2f;
radius = 4f;
layer = Layer.effect;
y = haloY;
}},
new HaloPart(){{
progress = haloProgress;
color = haloColor;
sides = 3;
shapes = 3;
hollow = true;
stroke = 0f;
strokeTo = 2f;
radius = 3f;
haloRadius = 10f + radius/2f;
haloRotateSpeed = haloRotSpeed;
layer = Layer.effect;
y = haloY;
}},
new HaloPart(){{
progress = haloProgress;
color = haloColor;
tri = true;
shapes = 3;
triLength = 0f;
triLengthTo = 10f;
radius = 6f;
haloRadius = 16f;
haloRotation = 180f;
layer = Layer.effect;
y = haloY;
}},
new HaloPart(){{
progress = haloProgress;
color = haloColor;
tri = true;
shapes = 3;
triLength = 0f;
triLengthTo = 3f;
radius = 6f;
haloRadius = 16f;
shapeRotation = 180f;
haloRotation = 180f;
layer = Layer.effect;
y = haloY;
}},
new HaloPart(){{
progress = haloProgress;
color = haloColor;
sides = 3;
tri = true;
shapes = 3;
triLength = 0f;
triLengthTo = 10f;
shapeRotation = 180f;
radius = 6f;
haloRadius = 16f;
haloRotateSpeed = -haloRotSpeed;
haloRotation = 180f / 3f;
layer = Layer.effect;
y = haloY;
}},
new HaloPart(){{
progress = haloProgress;
color = haloColor;
sides = 3;
tri = true;
shapes = 3;
triLength = 0f;
triLengthTo = 4f;
radius = 6f;
haloRadius = 16f;
haloRotateSpeed = -haloRotSpeed;
haloRotation = 180f / 3f;
layer = Layer.effect;
y = haloY;
}}
);
Color heatCol2 = heatCol.cpy().add(0.1f, 0.1f, 0.1f).mul(1.2f);
for(int i = 1; i < 4; i++){
int fi = i;
parts.add(new RegionPart("-spine"){{
outline = false;
progress = PartProgress.warmup.delay(fi / 5f);
heatProgress = PartProgress.warmup.add(p -> (Mathf.absin(3f, 0.2f) - 0.2f) * p.warmup);
mirror = true;
under = true;
layerOffset = -0.3f;
turretHeatLayer = Layer.turret - 0.2f;
moveY = 9f;
moveX = 1f + fi * 4f;
moveRot = fi * 60f - 130f;
color = Color.valueOf("bb68c3");
heatColor = heatCol2;
moves.add(new PartMove(PartProgress.recoil.delay(fi / 5f), 1f, 0f, 3f));
}});
}
}};
velocityRnd = 0.15f;
heatRequirement = 144f;
maxHeatEfficiency = 1f;
warmupMaintainTime = 120f;
consumePower(40f);
unitSort = UnitSorts.strongest;
shoot = new ShootSummon(0f, 0f, circleRad, 20f);
minWarmup = 0.96f;
shootWarmupSpeed = 0.08f;
shootY = circleY - 5f;
outlineColor = Pal.darkOutline;
envEnabled |= Env.space;
reload = 3.5f;
range = 410;
trackingRange = range * 1.4f;
shootCone = 100f;
scaledHealth = 370;
rotateSpeed = 2.6f;
recoil = 0.5f;
recoilTime = 30f;
shake = 3f;
}};创建一个Turret
有了这些东西,是时候创建一个炮塔了。
new ItemTurret("tutorial-item-turret"){{
requirements(Category.turret, with(Items.copper, 39));
ammo(Items.copper, new BasicBulletType(1.5f, 9));
shoot = new ShootPattern();
drawer = new DrawTurret(){{
parts.add(new RegionPart("-barrel"));
}};
consumePower(40f);
coolant = consumeCoolant(0.1f);
}};ItemTurret("tutorial-item-turret").apply {
requirements(Category.turret, with(Items.copper, 39))
ammo(Items.copper, BasicBulletType(1.5f, 9))
shoot = ShootPattern()
drawer = DrawTurret().apply{
parts.add(RegionPart("-barrel"))
}
consumePower(40f)
coolant = consumeCoolant(0.1f)
}炮塔类型具有明确的专用性,这主要源于不同子弹类型在功能实现上的显著差异。 炮塔的基类包括BaseTurret、ReloadTurret和Turret,其主要功能涵盖射程range(单位为像素,1格=8像素)、冷却时间reload、子弹容量maxAmmo的设置,以及上文在BulletType中已提及的部分字段。
常见的炮塔类型包括:
ItemTurret:物品炮塔,使用ammo(...)方法设置弹药与子弹类型;LiquidTurret:流体炮塔,使用ammo(...)方法设置弹药与子弹类型;PowerTurret:电力炮塔,使用consumePower方法设置电力消耗,通过shootType字段设置子弹类型;LaserTurret:激光炮塔(例如“熔毁”而非“蓝瑟”),通常建议使用ContinuousLaserBulletType作为子弹类型,并通过shootDuration字段设置持续时间;ContinuousTurret:连续炮塔(例如“光辉”),开火后可持续射击,需配合具有连续效果的子弹类型;ContinuousLiquidTurret:连续流体炮塔(例如“升华”),使用ammo(...)方法设置弹药与子弹类型,需配合具有连续效果的子弹类型。
部分方块虽归类于炮塔建造栏,但其功能与常规炮塔存在差异:
TractorBeamTurret:牵引光束类PointDefenseTurret:点防御类BuildTurret:建造塔类
使用 ammo 方法声明子弹类型的示例如下:
ammo(Items.copper, new BasicBulletType(3.5f, 18),
Items.lead, new FlakBulletType(4.2f, 3))在炮塔建筑中,当物品进入炮塔的一瞬间就会变成弹药。
炮塔类的部分字段如下:
1. 核心逻辑与计时器 (Core Logic & Timers)
这些字段控制炮塔的基础运行逻辑和内部计时。
logicControlCooldown(static final): 一个静态常量。当炮塔被逻辑模块控制后,需要经过这个时间(2秒 * 60帧/秒 = 120帧)才会恢复正常AI。timerTarget: 一个内部计时器ID,用于“寻找目标”这个动作的冷却。timers++表示它从父类继承了一个计时器数组,并分配了一个新的索引。targetInterval: 尝试寻找新目标的间隔时间(帧)。即使没有目标,也会定期执行搜索。newTargetInterval: 当炮塔已有有效目标时,尝试寻找新目标的间隔时间。如果为-1,则使用targetInterval。
2. 弹药系统 (Ammunition System)
控制炮塔如何消耗和存储弹药。
maxAmmo: 炮塔内最大可储存的弹药单位数。ammoPerShot: 每次射击消耗的弹药单位数。consumeAmmoOnce: 如果为true,无论一次射击发射多少发子弹(例如散射),都只消耗ammoPerShot份弹药。如果为false,则每发子弹都会消耗弹药。heatRequirement: (对于需要热量的炮塔)开火所需的最低热量值。-1表示不需要热量。maxHeatEfficiency: (对于需要热量的炮塔)最大热量效率乘数。热量越高,效率(通常是伤害或射速)越高,直到这个上限。
3. 射击精度与弹道 (Shooting Accuracy & Ballistics)
控制子弹发射时的随机性和行为。
inaccuracy: 子弹的角度随机性(散射),单位是度。velocityRnd: 子弹速度的随机 fraction。例如 0.1 表示速度会有 ±10% 的随机波动。scaleLifetimeOffset: 一个 fraction,会乘以某个值然后加到子弹的生命周期上。(不确定:具体乘以的是子弹的原始生命周期还是另一个值?代码中需确认lifeScale的计算)shootCone: 炮塔开火的容忍角度。如果炮塔当前旋转角度与目标角度之差小于此值,即使未完全对准也会开火。shootX,shootY: 子弹生成的相对坐标(相对于炮塔中心)。shootY = Float.NEGATIVE_INFINITY是一个常见的默认值,通常意味着如果未设置,则会使用炮塔本身的高度或其他逻辑来计算。xRand: 在X轴(水平轴)上的随机偏移量,用于给子弹生成位置增加随机性。
4. 目标选择与范围 (Targeting & Range)
控制炮塔如何寻找和锁定目标。
drawMinRange: 如果为true,在显示炮塔范围时也会绘制最小范围圈。trackingRange: 跟踪范围。在此范围内的目标会被炮塔发现并跟踪(旋转炮身),但不会开火。必须小于或等于射程。minRange: 最小射程。在此范围内的目标不会被攻击(主要用于 Artillery- artillery 类武器)。targetAir,targetGround: 是否以空中或地面单位为目标。targetBlocks: 是否以敌方建筑为目标。targetHealing: 如果为true,此炮塔会以友方(需要治疗的)建筑为目标(例如用于治疗炮塔)。targetUnderBlocks: 如果为false,则不会以“下层”方块(如下方的传送带)为目标。predictTarget: 是否预测移动中目标的位置(提前量计算)。unitSort: 一个排序函数(Sortf),用于在多个可用目标中选择优先攻击哪个。UnitSorts.closest是默认的“最近优先”。unitFilter: 一个过滤函数(Boolf<Unit>),用于判断哪些单位可以被攻击。u -> true表示默认所有单位都可以。buildingFilter: 一个过滤函数(Boolf<Building>),用于判断哪些建筑可以被攻击。默认逻辑是:如果targetUnderBlocks为false且建筑是“下层子弹”类型,则过滤掉。
5. 射击控制与冷却 (Firing Control & Cooldown)
控制炮塔的射击节奏、预热和冷却。
minWarmup: 最低预热值。只有当炮塔的warmup值(从0到1)达到或超过此值时,才能开火。accurateDelay: 如果为true,炮塔在具有firstShotDelay的情况下,会精确计算延迟以命中移动目标。moveWhileCharging: (firstShotDelay> 0 时)如果为false,炮塔在充能/准备射击时无法旋转。reloadWhileCharging: (firstShotDelay> 0 时)如果为false,炮塔在充能/准备射击时无法装填弹药。warmupMaintainTime: 停止射击后,预热值保持不衰减的时间。shoot: 一个ShootPattern对象,定义了射击模式(例如单发、散射、脉冲等)。这是控制多子弹发射的核心。alwaysShooting: 如果为true,只要炮塔有弹药,它就会一直射击,无视范围内是否有目标或任何控制信号。常用于装饰性或特殊效果的炮塔。cooldownTime: 视觉上的“热量区域”冷却所需的时间(帧)。
6. 玩家控制与显示 (Player Control & Display)
与玩家交互和UI显示相关的设置。
playerControllable: 玩家是否可以直接手动控制这个炮塔(例如点击并攻击特定目标)。displayAmmoMultiplier: 是否在炮塔的UI状态中显示弹药效率乘数(对于某些不直接使用弹药的炮塔可能不相关)。
7. 效果与音效 (Effects & Sounds)
控制炮塔射击时的视听反馈。
heatColor: 炮塔过热时绘制的热量区域的颜色。shootEffect,smokeEffect: 射击效果和烟雾效果的覆盖。如果为null,则使用子弹类型中定义的效果。ammoUseEffect: 必定播放的效果,在消耗弹药时触发。shootSound: 发射单发子弹时的声音。chargeSound: 当shoot.firstShotDelay> 0 时,开始充能/准备时播放的声音。loopSound: 炮塔处于活动状态时(例如预热值 > 0)循环播放的声音。应谨慎使用,避免性能问题。loopSoundVolume: 活动循环音效的基础音量。soundPitchMin,soundPitchMax: 射击音效音高的随机范围,用于增加变化。ammoEjectBack: 弹药弹出效果在Y轴(向下)方向的偏移量,模拟弹壳向后抛出。
8. 视觉与动画 (Visuals & Animation)
控制炮塔的视觉表现,如后坐力、震动等。
shootWarmupSpeed: 预热值增加或减少的插值速度。linearWarmup: 预热值的增长是线性的(true)还是遵循某种曲线(false)。recoil: 每次射击时,炮身向后移动的视觉距离。recoils: 额外的后坐力计数器数量。-1通常表示使用默认值(可能是1)。(不确定:具体如何影响视觉效果?可能是为多个炮管独立设置后坐力)recoilTime: 后坐力动画恢复原状所需的时间(帧)。如果为-1,则使用装填时间(reload)。recoilPow: 应用于后坐力动画的幂曲线,控制运动的效果(例如先快后慢)。elevation: 炮塔阴影的视觉高度(Elevation)。-1表示使用默认值。shake: 每次射击时造成的屏幕震动强度。drawer: 一个DrawBlock对象,负责处理这个炮塔的所有绘制逻辑。DrawTurret()是标准的炮塔绘制器。