This commit is contained in:
Tourswen
2025-11-22 01:41:05 +08:00
parent 34c93bb18d
commit d0b06cbc72
30 changed files with 1083 additions and 427 deletions

View File

@@ -8,10 +8,9 @@ namespace WulaFallenEmpire
public class Projectile_NorthArcTrail : Projectile_Explosive
{
// --- 弹道部分变量 ---
// 通过ModExtension配置的向北偏移高度
public float northOffsetDistance = 0f;
private Vector3 exactPositionInt; // 用于存储我们自己计算的位置
private Vector3 exactPositionInt;
private float curveSteepness = 1f;
private Vector3 originPos;
@@ -24,9 +23,12 @@ namespace WulaFallenEmpire
// --- 尾迹部分变量 ---
private TrackingBulletDef trackingDefInt;
private int Fleck_MakeFleckTick;
private Vector3 lastTickPosition; // 记录上一帧位置用于计算拖尾方向
private Vector3 lastTickPosition;
// 新增:绘制相关变量
private float currentArcHeight;
private const float DRAW_ALTITUDE_OFFSET = 15f; // 增加绘制高度偏移
// 获取 XML 中的扩展数据
public TrackingBulletDef TrackingDef
{
get
@@ -36,7 +38,6 @@ namespace WulaFallenEmpire
trackingDefInt = def.GetModExtension<TrackingBulletDef>();
if (trackingDefInt == null)
{
// 如果没配置,给一个空的默认值防止报错,或者只报错一次
trackingDefInt = new TrackingBulletDef();
}
}
@@ -44,13 +45,26 @@ namespace WulaFallenEmpire
}
}
public override Vector3 ExactPosition => exactPositionInt; // 重写属性,让游戏获取我们计算的位置
public override Quaternion ExactRotation => Quaternion.LookRotation(GetCurrentDirection()); // 弹头朝向当前移动方向
// 修改:重写绘制位置,确保在正确的高度
public override Vector3 ExactPosition
{
get
{
if (!initialized)
return base.ExactPosition;
// 返回计算的位置但保持Y轴为绘制高度
Vector3 pos = exactPositionInt;
pos.y = def.Altitude + currentArcHeight + DRAW_ALTITUDE_OFFSET;
return pos;
}
}
public override Quaternion ExactRotation => Quaternion.LookRotation(GetCurrentDirection());
public override void ExposeData()
{
base.ExposeData();
// 保存弹道数据
Scribe_Values.Look(ref originPos, "originPos");
Scribe_Values.Look(ref destinationPos, "destinationPos");
Scribe_Values.Look(ref bezierControlPoint, "bezierControlPoint");
@@ -60,8 +74,8 @@ namespace WulaFallenEmpire
Scribe_Values.Look(ref northOffsetDistance, "northOffsetDistance", 0f);
Scribe_Values.Look(ref exactPositionInt, "exactPositionInt", Vector3.zero);
Scribe_Values.Look(ref curveSteepness, "curveSteepness", 1f);
Scribe_Values.Look(ref currentArcHeight, "currentArcHeight", 0f);
// 保存尾迹数据
Scribe_Values.Look(ref Fleck_MakeFleckTick, "Fleck_MakeFleckTick", 0);
Scribe_Values.Look(ref lastTickPosition, "lastTickPosition", Vector3.zero);
}
@@ -79,8 +93,7 @@ namespace WulaFallenEmpire
}
else
{
// 如果没有配置,则使用默认值,或者从 projectile.arcHeightFactor 获取参考值
northOffsetDistance = def.projectile.arcHeightFactor * 3; // 将arcHeightFactor转换为北向偏移距离
northOffsetDistance = def.projectile.arcHeightFactor * 3;
}
// --- 初始化弹道 ---
@@ -90,36 +103,27 @@ namespace WulaFallenEmpire
float speed = def.projectile.speed;
if (speed <= 0) speed = 1f;
// 计算直线距离估算时间
float distance = (originPos - destinationPos).MagnitudeHorizontal();
totalTicks = Mathf.CeilToInt(distance / speed * 100f);
if (totalTicks < 1) totalTicks = 1;
ticksFlying = 0;
// 贝塞尔曲线计算
// 中点
// 贝塞尔曲线计算
Vector3 midPoint = (originPos + destinationPos) / 2f;
// 顶点 (中点向北偏移 X 格)
Vector3 apexPoint = midPoint + new Vector3(0, 0, northOffsetDistance);
// 控制点 P1 = 2 * 顶点 - 中点
bezierControlPoint = 2f * apexPoint - midPoint;
initialized = true;
// 初始化我们自己的位置
exactPositionInt = origin;
// --- 初始化尾迹 ---
lastTickPosition = origin;
currentArcHeight = 0f;
}
protected override void Tick()
{
// 首先调用base.Tick(),让它处理组件更新(比如拖尾特效)和ticksToImpact
base.Tick();
// 如果base.Tick()已经处理了撞击,我们就不再继续
if (this.Destroyed)
{
return;
@@ -133,61 +137,54 @@ namespace WulaFallenEmpire
ticksFlying++;
// 1. 计算当前帧的新位置 (贝塞尔曲线)
// 1. 计算当前帧的新位置
float t = (float)ticksFlying / (float)totalTicks;
if (t > 1f) t = 1f;
float u = 1 - t;
// 水平位移 (贝塞尔)
Vector3 nextPos = (u * u * originPos) + (2 * u * t * bezierControlPoint) + (t * t * destinationPos);
// 垂直高度 (抛物线)
float arcHeight = def.projectile.arcHeightFactor * GenMath.InverseParabola(t);
nextPos.y = arcHeight;
currentArcHeight = def.projectile.arcHeightFactor * GenMath.InverseParabola(t);
nextPos.y = 0f; // 水平位置不包含高度
// 检查边界
if (!nextPos.ToIntVec3().InBounds(base.Map))
{
this.Destroy();
return;
}
// 更新我们自己的位置
exactPositionInt = nextPos;
// 2. 处理拖尾特效
// 只有当这一帧移动了,且配置了 DefModExtension 时才生成
if (TrackingDef != null && TrackingDef.tailFleckDef != null)
{
Fleck_MakeFleckTick++;
// 检查生成间隔
if (Fleck_MakeFleckTick >= TrackingDef.fleckDelayTicks)
{
// 简单的循环计时重置逻辑
if (Fleck_MakeFleckTick >= (TrackingDef.fleckDelayTicks + TrackingDef.fleckMakeFleckTickMax))
{
Fleck_MakeFleckTick = TrackingDef.fleckDelayTicks;
}
Map map = base.Map;
// 只有当在地图内时才生成
if (map != null)
{
int count = TrackingDef.fleckMakeFleckNum.RandomInRange;
Vector3 currentPosition = this.ExactPosition;
Vector3 currentPosition = this.ExactPosition; // 使用重写后的ExactPosition
Vector3 previousPosition = lastTickPosition;
// 仅当有位移时才计算角度,防止原地鬼畜
if ((currentPosition - previousPosition).MagnitudeHorizontalSquared() > 0.0001f)
{
float moveAngle = (currentPosition - previousPosition).AngleFlat();
for (int i = 0; i < count; i++)
{
// 这里的逻辑完全照搬原来的 BulletWithTrail
float velocityAngle = TrackingDef.fleckAngle.RandomInRange + moveAngle;
FleckCreationData dataStatic = FleckMaker.GetDataStatic(currentPosition, map, TrackingDef.tailFleckDef, TrackingDef.fleckScale.RandomInRange);
dataStatic.rotation = moveAngle; // 粒子朝向跟随移动方向
dataStatic.rotation = moveAngle;
dataStatic.rotationRate = TrackingDef.fleckRotation.RandomInRange;
dataStatic.velocityAngle = velocityAngle;
dataStatic.velocitySpeed = TrackingDef.fleckSpeed.RandomInRange;
@@ -198,10 +195,8 @@ namespace WulaFallenEmpire
}
}
// 3. 更新上一帧位置
lastTickPosition = nextPos;
lastTickPosition = ExactPosition; // 使用重写后的ExactPosition
// 4. 判定到达目标或倒计时爆炸
if (ticksFlying >= totalTicks)
{
Impact(null);
@@ -209,6 +204,58 @@ namespace WulaFallenEmpire
}
}
// 修改:重写绘制方法,确保正确绘制
protected override void DrawAt(Vector3 drawLoc, bool flip = false)
{
if (!initialized)
{
base.DrawAt(drawLoc, flip);
return;
}
// 使用我们计算的位置进行绘制
Vector3 finalDrawPos = ExactPosition;
// 绘制阴影
if (def.projectile.shadowSize > 0f)
{
DrawShadow(finalDrawPos, currentArcHeight);
}
Quaternion rotation = ExactRotation;
if (def.projectile.spinRate != 0f)
{
float spinAngle = 60f / def.projectile.spinRate;
rotation = Quaternion.AngleAxis((float)Find.TickManager.TicksGame % spinAngle / spinAngle * 360f, Vector3.up);
}
// 使用正确的绘制方法
if (def.projectile.useGraphicClass)
{
Graphic.Draw(finalDrawPos, base.Rotation, this, rotation.eulerAngles.y);
}
else
{
Graphics.DrawMesh(MeshPool.GridPlane(def.graphicData.drawSize), finalDrawPos, rotation, DrawMat, 0);
}
Comps_PostDraw();
}
// 修改:重写阴影绘制,使用正确的高度
private void DrawShadow(Vector3 drawLoc, float height)
{
Material shadowMat = MaterialPool.MatFrom("Things/Skyfaller/SkyfallerShadowCircle", ShaderDatabase.Transparent);
if (shadowMat == null) return;
float shadowSize = def.projectile.shadowSize * Mathf.Lerp(1f, 0.6f, height / (def.projectile.arcHeightFactor + 1f));
Vector3 scale = new Vector3(shadowSize, 1f, shadowSize);
Vector3 shadowOffset = new Vector3(0f, -0.01f, 0f);
Matrix4x4 matrix = Matrix4x4.TRS(drawLoc + shadowOffset, Quaternion.identity, scale);
Graphics.DrawMesh(MeshPool.plane10, matrix, shadowMat, 0);
}
// 计算当前位置的切线方向
private Vector3 GetCurrentDirection()
{
@@ -220,13 +267,9 @@ namespace WulaFallenEmpire
float t = (float)ticksFlying / (float)totalTicks;
if (t > 1f) t = 1f;
// 计算贝塞尔曲线的导数(切线向量)
// 对于二次贝塞尔曲线 B(t) = (1-t)²P₀ + 2(1-t)tP₁ + t²P₂
// 导数 B'(t) = 2(1-t)(P₁-P₀) + 2t(P₂-P₁)
float u = 1 - t;
Vector3 tangent = 2 * u * (bezierControlPoint - originPos) + 2 * t * (destinationPos - bezierControlPoint);
// 如果切线向量为零,则使用默认方向
if (tangent.MagnitudeHorizontalSquared() < 0.0001f)
{
return (destinationPos - originPos).normalized;
@@ -240,4 +283,4 @@ namespace WulaFallenEmpire
base.Impact(hitThing, blockedByShield);
}
}
}
}