This commit is contained in:
2026-02-24 12:02:38 +08:00
parent 1af5f0c1d8
commit 96bc1d4c5a
57 changed files with 6595 additions and 1170 deletions

View File

@@ -0,0 +1,359 @@
// CompMechMovementSound_Fixed.cs
using RimWorld;
using UnityEngine;
using Verse;
using Verse.Sound;
using System.Collections.Generic;
namespace WulaFallenEmpire
{
public class CompMechMovementSound : ThingComp
{
public CompProperties_MechMovementSound Props => (CompProperties_MechMovementSound)props;
// 核心状态
private Sustainer soundSustainer;
private bool isPlaying = false;
private Vector3 lastPosition = Vector3.zero;
private float currentSpeed = 0f;
private int ticksSinceLastMovement = 0;
private bool wasMovingLastTick = false;
// 缓存引用
private Pawn mechPawn;
private CompPowerTrader powerComp;
private CompMechPilotHolder pilotComp;
// 状态平滑
private const int MOVEMENT_CHECK_INTERVAL = 3; // 每10ticks检查一次移动
private const int STOP_DELAY_TICKS = 1; // 停止后延迟30ticks再停止音效
private const float SPEED_SMOOTHING = 0.2f; // 速度平滑系数
public override void Initialize(CompProperties props)
{
base.Initialize(props);
mechPawn = parent as Pawn;
}
public override void PostSpawnSetup(bool respawningAfterLoad)
{
base.PostSpawnSetup(respawningAfterLoad);
powerComp = parent.TryGetComp<CompPowerTrader>();
pilotComp = parent.TryGetComp<CompMechPilotHolder>();
if (mechPawn != null && mechPawn.Spawned)
{
lastPosition = GetCurrentPositionSafe();
}
}
public override void CompTick()
{
base.CompTick();
// 每帧都更新位置,但减少移动状态检查频率
if (mechPawn != null && mechPawn.Spawned)
{
UpdatePosition();
}
// 每10ticks检查一次移动状态
if (Find.TickManager.TicksGame % MOVEMENT_CHECK_INTERVAL == 0)
{
UpdateMovementState();
}
// 每帧维持音效
MaintainSound();
}
// 安全的获取当前位置
private Vector3 GetCurrentPositionSafe()
{
try
{
if (mechPawn == null || !mechPawn.Spawned)
return mechPawn?.Position.ToVector3Shifted() ?? Vector3.zero;
// 优先使用DrawPos如果不可用则使用网格位置
if (mechPawn.Drawer != null)
{
return mechPawn.DrawPos;
}
return mechPawn.Position.ToVector3Shifted();
}
catch
{
return mechPawn?.Position.ToVector3Shifted() ?? Vector3.zero;
}
}
// 更新位置(每帧)
private void UpdatePosition()
{
Vector3 currentPos = GetCurrentPositionSafe();
// 计算当前帧的速度(使用真实时间)
float deltaTime = Time.deltaTime;
if (deltaTime > 0)
{
float distance = Vector3.Distance(currentPos, lastPosition);
float rawSpeed = distance / deltaTime;
// 应用平滑过滤
currentSpeed = Mathf.Lerp(currentSpeed, rawSpeed, SPEED_SMOOTHING);
}
lastPosition = currentPos;
}
// 更新移动状态(低频检查)
private void UpdateMovementState()
{
if (!ShouldProcess())
{
ticksSinceLastMovement++;
if (isPlaying && ticksSinceLastMovement > STOP_DELAY_TICKS)
{
StopSound();
}
return;
}
// 检查是否在移动
bool isMoving = CheckIfMoving();
// 更新移动状态
if (isMoving)
{
ticksSinceLastMovement = 0;
if (!isPlaying)
{
StartSound();
}
}
else
{
ticksSinceLastMovement++;
// 延迟停止,避免频繁启停
if (isPlaying && ticksSinceLastMovement > STOP_DELAY_TICKS)
{
StopSound();
}
}
wasMovingLastTick = isMoving;
}
// 检查是否应该处理音效
private bool ShouldProcess()
{
if (mechPawn == null || Props.movementSound == null)
return false;
// 基础状态检查
if (!mechPawn.Spawned || mechPawn.Dead || mechPawn.Downed || mechPawn.InMentalState)
return false;
// 条件检查
if (Props.requirePower && powerComp != null && !powerComp.PowerOn)
return false;
if (Props.requirePilot && pilotComp != null && !pilotComp.HasPilots)
return false;
return true;
}
// 综合判断是否在移动
private bool CheckIfMoving()
{
// 方法1速度阈值
if (currentSpeed > Props.minMovementSpeed)
return true;
// 方法2检查寻路器
if (mechPawn.pather?.Moving ?? false)
return true;
// 方法3检查当前任务
var job = mechPawn.CurJob;
if (job != null)
{
if (job.def == JobDefOf.Goto ||
job.def == JobDefOf.GotoWander ||
job.def == JobDefOf.Flee ||
job.def == JobDefOf.Follow)
return true;
}
return false;
}
// 维持音效
private void MaintainSound()
{
if (soundSustainer != null && isPlaying)
{
try
{
// 更新音效位置
if (mechPawn != null && mechPawn.Spawned)
{
var map = mechPawn.Map;
if (map != null)
{
// 创建一个新的SoundInfo来更新位置
SoundInfo soundInfo = SoundInfo.InMap(mechPawn, MaintenanceType.PerTick);
soundSustainer?.SustainerUpdate();
}
}
// 维持音效
soundSustainer.Maintain();
}
catch
{
// 如果sustainer失效重置状态
soundSustainer = null;
isPlaying = false;
}
}
}
// 开始音效
private void StartSound()
{
if (Props.movementSound == null || soundSustainer != null)
return;
try
{
// 创建音效信息
SoundInfo soundInfo = SoundInfo.InMap(mechPawn, MaintenanceType.PerTick);
// 使用TrySpawnSustainer
soundSustainer = Props.movementSound.TrySpawnSustainer(soundInfo);
if (soundSustainer != null)
{
isPlaying = true;
}
else
{
Log.Warning($"[DD] Failed to create sustainer for {Props.movementSound.defName}");
isPlaying = false;
}
}
catch
{
soundSustainer = null;
isPlaying = false;
}
}
// 停止音效
private void StopSound()
{
if (soundSustainer != null)
{
try
{
// 先检查sustainer是否有效
if (!soundSustainer.Ended)
{
soundSustainer.End();
}
}
finally
{
soundSustainer = null;
isPlaying = false;
}
}
}
// 事件处理
public override void PostDestroy(DestroyMode mode, Map previousMap)
{
base.PostDestroy(mode, previousMap);
StopSound();
}
public void PostDeSpawn(Map map)
{
base.PostDeSpawn(map);
StopSound();
}
public override void Notify_Downed()
{
base.Notify_Downed();
StopSound();
}
// 序列化
public override void PostExposeData()
{
base.PostExposeData();
Scribe_Values.Look(ref lastPosition, "lastPosition", Vector3.zero);
Scribe_Values.Look(ref currentSpeed, "currentSpeed", 0f);
Scribe_Values.Look(ref ticksSinceLastMovement, "ticksSinceLastMovement", 0);
Scribe_Values.Look(ref wasMovingLastTick, "wasMovingLastTick", false);
// 加载后重新初始化
if (Scribe.mode == LoadSaveMode.PostLoadInit)
{
soundSustainer = null;
isPlaying = false;
}
}
// 调试信息
public override IEnumerable<Gizmo> CompGetGizmosExtra()
{
foreach (Gizmo gizmo in base.CompGetGizmosExtra())
{
yield return gizmo;
}
if (DebugSettings.ShowDevGizmos && mechPawn != null && mechPawn.Faction == Faction.OfPlayer)
{
yield return new Command_Action
{
defaultLabel = "DEV: Sound Debug",
defaultDesc = GetDebugInfo(),
action = () =>
{
// 切换声音状态
if (soundSustainer == null)
{
StartSound();
Messages.Message("Sound started", mechPawn, MessageTypeDefOf.NeutralEvent);
}
else
{
StopSound();
Messages.Message("Sound stopped", mechPawn, MessageTypeDefOf.NeutralEvent);
}
}
};
}
}
private string GetDebugInfo()
{
return $"Movement Sound Debug:\n" +
$" Playing: {isPlaying}\n" +
$" Speed: {currentSpeed:F2} (min: {Props.minMovementSpeed})\n" +
$" Ticks since move: {ticksSinceLastMovement}\n" +
$" Sustainer: {soundSustainer != null}\n" +
$" Was moving: {wasMovingLastTick}\n" +
$" Pawn pathing: {mechPawn.pather?.Moving ?? false}";
}
}
}

View File

@@ -0,0 +1,66 @@
// CompProperties_MechMovementSound_Enhanced.cs
using RimWorld;
using Verse;
using System.Collections.Generic;
namespace WulaFallenEmpire
{
public class CompProperties_MechMovementSound : CompProperties
{
// 基础音效
public SoundDef movementSound;
// 控制参数
public bool requirePilot = false;
public bool requirePower = false;
public float minMovementSpeed = 0.1f;
// 新增:平滑控制
public int movementCheckInterval = 10; // 移动检查间隔
public int stopDelayTicks = 30; // 停止延迟
public float speedSmoothing = 0.2f; // 速度平滑系数
// 新增:声音参数
public bool loopSound = true; // 是否循环播放
public float volumeMultiplier = 1.0f; // 音量乘数
public CompProperties_MechMovementSound()
{
this.compClass = typeof(CompMechMovementSound);
}
public override IEnumerable<string> ConfigErrors(ThingDef parentDef)
{
foreach (string error in base.ConfigErrors(parentDef))
{
yield return error;
}
if (movementSound == null)
{
yield return $"movementSound is not defined for {parentDef.defName}";
}
if (minMovementSpeed < 0f)
{
yield return $"minMovementSpeed cannot be negative for {parentDef.defName}";
}
if (movementCheckInterval < 1)
{
yield return $"movementCheckInterval must be at least 1 for {parentDef.defName}";
}
if (stopDelayTicks < 0)
{
yield return $"stopDelayTicks cannot be negative for {parentDef.defName}";
}
// 如果需要驾驶员,检查是否配置了驾驶员容器
if (requirePilot && parentDef.GetCompProperties<CompProperties_MechPilotHolder>() == null)
{
Log.Warning($"[DD] requirePilot is true but no CompProperties_MechPilotHolder found for {parentDef.defName}");
}
}
}
}