cumulative update

This commit is contained in:
Valera 2025-12-01 00:02:14 +07:00
parent e12b75be45
commit 66fcc6a2bb
12 changed files with 131 additions and 151 deletions

View File

@ -12,6 +12,7 @@
"NetworkMode": 2,
"NetworkInterpolation": true,
"NetworkOrphaned": 0,
"NetworkTransmit": true,
"OwnerTransfer": 1,
"Components": [],
"Children": [
@ -28,6 +29,7 @@
"NetworkMode": 2,
"NetworkInterpolation": true,
"NetworkOrphaned": 0,
"NetworkTransmit": true,
"OwnerTransfer": 1,
"Components": [
{
@ -197,7 +199,7 @@
"FaceVelocity": false,
"FogStrength": 1,
"LeadingTrail": true,
"Lighting": false,
"Lighting": true,
"MotionBlur": false,
"OnComponentDestroy": null,
"OnComponentDisabled": null,
@ -206,6 +208,7 @@
"OnComponentStart": null,
"OnComponentUpdate": null,
"Opaque": false,
"PlaybackSpeed": 1,
"RenderOptions": {
"GameLayer": true,
"OverlayLayer": false,
@ -215,7 +218,7 @@
"RotationOffset": 0,
"Scale": 1,
"Shadows": true,
"SortMode": "Unsorted",
"SortMode": "ByDistance",
"Sprite": {
"$compiler": "embed",
"$source": null,
@ -228,7 +231,8 @@
"LoopMode": "Loop",
"Frames": [
{
"Texture": "textures/smoketexturesheet.vtex"
"Texture": "textures/smoketexturesheet.vtex",
"BroadcastMessages": []
}
]
}

View File

@ -4,10 +4,10 @@ namespace VeloX;
public abstract partial class VeloXBase
{
[Feature( "Input" )] internal InputResolver Input { get; set; } = new();
[Feature( "Input" )] public Connection Driver { get => Input.Driver; set => Input.Driver = value; }
internal InputResolver Input { get; set; } = new();
public Connection Driver { get => Input.Driver; set => Input.Driver = value; }
public bool IsDriver => Connection.Local == Driver;
private bool IsDriverActive => Driver is not null;
public Vector2 MouseDelta => IsDriverActive ? Input.MouseDelta : default;

View File

@ -52,12 +52,8 @@ public abstract partial class VeloXBase
v.BrakeTorque = HandbrakeForce;
}
v.Update( this, in dt );
v.DoPhysics( in dt );
}
Body.Velocity = vehVel;
Body.AngularVelocity = vehAngVel;
}

View File

@ -4,7 +4,7 @@ using System;
namespace VeloX;
public abstract partial class VeloXBase : Component
public abstract partial class VeloXBase : Component, IGameObjectNetworkEvents
{
[Sync, Change( nameof( OnEngineIgnitionChange ) )]
public bool EngineIgnition { get; set; }
@ -26,18 +26,24 @@ public abstract partial class VeloXBase : Component
[Sync( SyncFlags.Interpolate )] public Angles SteerAngle { get; set; }
public Vector3 LocalVelocity;
[Sync( SyncFlags.Interpolate )] public Vector3 LocalVelocity { get; set; }
[Sync( SyncFlags.Interpolate )] public Vector3 Velocity { get; set; }
public float ForwardSpeed;
public float TotalSpeed;
protected override void OnFixedUpdate()
{
if ( !IsProxy )
{
LocalVelocity = WorldTransform.PointToLocal( WorldPosition + Body.Velocity );
Velocity = Body.Velocity;
}
ForwardSpeed = LocalVelocity.x;
TotalSpeed = LocalVelocity.Length;
if ( IsProxy )
return;
LocalVelocity = WorldTransform.PointToLocal( WorldPosition + Body.Velocity );
ForwardSpeed = LocalVelocity.x;
TotalSpeed = LocalVelocity.Length;
Body.PhysicsBody.Mass = Mass;
FixedUpdate();
@ -50,4 +56,10 @@ public abstract partial class VeloXBase : Component
PhysicsSimulate();
}
//void IGameObjectNetworkEvents.NetworkOwnerChanged( Connection newOwner, Connection previousOwner )
//{
// Driver = newOwner;
// EngineIgnition = Driver is not null;
//}
}

View File

@ -4,9 +4,6 @@ namespace VeloX;
public struct Friction
{
public float SlipCoef { get; set; }
public float ForceCoef { get; set; }
public float Force { get; set; }
public float Slip { get; set; }
public float Speed { get; set; }

View File

@ -35,8 +35,6 @@ public partial class VeloXWheel
public float CounterTorque { get; private set; }
//[Property, Range( 0, 2 )] public float BrakeMult { get; set; } = 1f;
public Friction ForwardFriction = new();
public Friction SidewayFriction = new();
public Vector3 FrictionForce;
@ -47,20 +45,32 @@ public partial class VeloXWheel
public Vector3 ContactRight => hitSidewaysDirection;
public Vector3 ContactForward => hitForwardDirection;
public float LongitudinalSlip => ForwardFriction.Slip;
public float LongitudinalSpeed => ForwardFriction.Speed;
public float LongitudinalSlip => sx;
public float LongitudinalSpeed => vx;
public bool IsSkiddingLongitudinally => NormalizedLongitudinalSlip > 0.35f;
public float NormalizedLongitudinalSlip => Math.Clamp( Math.Abs( LongitudinalSlip ), 0, 1 );
public float LateralSlip => SidewayFriction.Slip;
public float LateralSpeed => SidewayFriction.Speed;
public float LateralSlip => sy;
public float LateralSpeed => vy;
public bool IsSkiddingLaterally => NormalizedLateralSlip > 0.35f;
public float NormalizedLateralSlip => Math.Clamp( Math.Abs( LateralSlip ), 0, 1 );
public bool IsSkidding => IsSkiddingLaterally || IsSkiddingLongitudinally;
public float NormalizedSlip => (NormalizedLateralSlip + NormalizedLongitudinalSlip) / 2f;
// speed
[Sync] private float vx { get; set; }
[Sync] private float vy { get; set; }
// force
[Sync] private float fx { get; set; }
[Sync] private float fy { get; set; }
// slip
[Sync] private float sx { get; set; }
[Sync] private float sy { get; set; }
private void UpdateHitVariables()
{
if ( IsOnGround )
@ -70,13 +80,13 @@ public partial class VeloXWheel
hitForwardDirection = ContactNormal.Cross( TransformRotationSteer.Right ).Normal;
hitSidewaysDirection = Rotation.FromAxis( ContactNormal, 90f ) * hitForwardDirection;
ForwardFriction.Speed = hitContactVelocity.Dot( hitForwardDirection ).InchToMeter();
SidewayFriction.Speed = hitContactVelocity.Dot( hitSidewaysDirection ).InchToMeter();
vx = hitContactVelocity.Dot( hitForwardDirection ).InchToMeter();
vy = hitContactVelocity.Dot( hitSidewaysDirection ).InchToMeter();
}
else
{
ForwardFriction = new();
SidewayFriction = new();
vx = 0;
vy = 0;
}
}
@ -112,9 +122,9 @@ public partial class VeloXWheel
float slipLoadModifier = 1f - loadPercent * 0.4f;
float mass = Vehicle.Body.Mass;
float absForwardSpeed = Math.Abs( ForwardFriction.Speed );
float absForwardSpeed = Math.Abs( vx );
float forwardForceClamp = mass * LoadContribution * absForwardSpeed * invDt;
float absSideSpeed = Math.Abs( SidewayFriction.Speed );
float absSideSpeed = Math.Abs( vy );
float sideForceClamp = mass * LoadContribution * absSideSpeed * invDt;
float forwardSpeedClamp = 1.5f * (dt / 0.005f);
@ -124,7 +134,7 @@ public partial class VeloXWheel
float peakForwardFrictionForce = 11000 * (1 - MathF.Exp( -0.00014f * forwardLoadFactor ));
float absCombinedBrakeTorque = Math.Max( 0, brakeTorque + RollingResistanceTorque );
float signedCombinedBrakeTorque = absCombinedBrakeTorque * -Math.Sign( ForwardFriction.Speed );
float signedCombinedBrakeTorque = absCombinedBrakeTorque * (vx > 0 ? -1 : 1);
float signedCombinedBrakeForce = signedCombinedBrakeTorque * invRadius;
float motorForce = motorTorque * invRadius;
float forwardInputForce = motorForce + signedCombinedBrakeForce;
@ -134,7 +144,7 @@ public partial class VeloXWheel
float maxForwardForce = Math.Min( peakForwardFrictionForce, forwardForceClamp );
maxForwardForce = absMotorTorque < absBrakeTorque ? maxForwardForce : peakForwardFrictionForce;
ForwardFriction.Force = forwardInputForce > maxForwardForce ? maxForwardForce
fx = forwardInputForce > maxForwardForce ? maxForwardForce
: forwardInputForce < -maxForwardForce ? -maxForwardForce : forwardInputForce;
wheelIsBlocked = false;
@ -153,14 +163,14 @@ public partial class VeloXWheel
AngularVelocity += combinedWheelForce * mRadius * invInertia * dt;
// Surface (corrective) force
float noSlipAngularVelocity = ForwardFriction.Speed * invRadius;
float noSlipAngularVelocity = vx * invRadius;
float angularVelocityError = AngularVelocity - noSlipAngularVelocity;
float angularVelocityCorrectionForce = Math.Clamp( -angularVelocityError * inertia * invRadius * invDt, -maxForwardForce, maxForwardForce );
if ( absMotorTorque < absBrakeTorque && Math.Abs( wheelForceClampOverflow ) > Math.Abs( angularVelocityCorrectionForce ) )
{
wheelIsBlocked = true;
AngularVelocity += ForwardFriction.Speed > 0 ? 1e-10f : -1e-10f;
AngularVelocity += vx > 0 ? 1e-10f : -1e-10f;
}
else
{
@ -178,24 +188,24 @@ public partial class VeloXWheel
float absAngularVelocity = AngularVelocity < 0 ? -AngularVelocity : AngularVelocity;
float maxCounterTorque = inertia * absAngularVelocity;
CounterTorque = Math.Clamp( (signedCombinedBrakeForce - ForwardFriction.Force) * mRadius, -maxCounterTorque, maxCounterTorque );
CounterTorque = Math.Clamp( (signedCombinedBrakeForce - fx) * mRadius, -maxCounterTorque, maxCounterTorque );
ForwardFriction.Slip = (ForwardFriction.Speed - AngularVelocity * mRadius) / clampedAbsForwardSpeed;
ForwardFriction.Slip *= slipLoadModifier;
sx = (vx - AngularVelocity * mRadius) / clampedAbsForwardSpeed;
sx *= slipLoadModifier;
SidewayFriction.Slip = MathF.Atan2( SidewayFriction.Speed, clampedAbsForwardSpeed );
SidewayFriction.Slip *= slipLoadModifier;
sy = MathF.Atan2( vy, clampedAbsForwardSpeed );
sy *= slipLoadModifier;
float sideSlipSign = SidewayFriction.Slip > 0 ? 1 : -1;
float absSideSlip = Math.Abs( SidewayFriction.Slip );
float sideSlipSign = sy > 0 ? 1 : -1;
float absSideSlip = Math.Abs( sy );
float peakSideFrictionForce = 18000 * (1 - MathF.Exp( -0.0001f * sideLoadFactor ));
float sideForce = -sideSlipSign * Tire.Evaluate( absSideSlip ) * peakSideFrictionForce;
SidewayFriction.Force = Math.Clamp( sideForce, -sideForceClamp, sideForceClamp );
fy = Math.Clamp( sideForce, -sideForceClamp, sideForceClamp );
// Calculate effect of camber on friction
float camberFrictionCoeff = Math.Max( 0, Vehicle.WorldRotation.Up.Dot( ContactNormal ) );
SidewayFriction.Force *= camberFrictionCoeff;
fy *= camberFrictionCoeff;
if ( IsOnGround && absForwardSpeed < 0.12f && absSideSpeed < 0.12f )
{
@ -225,9 +235,9 @@ public partial class VeloXWheel
if ( wheelIsBlocked && absAngularVelocity < 0.5f )
{
ForwardFriction.Force += correctiveForce.Dot( hitForwardDirection );
fx += correctiveForce.Dot( hitForwardDirection );
}
SidewayFriction.Force += correctiveForce.Dot( hitSidewaysDirection );
fy += correctiveForce.Dot( hitSidewaysDirection );
}
}
else
@ -235,52 +245,21 @@ public partial class VeloXWheel
lowSpeedReferenceIsSet = false;
}
fx = Math.Clamp( fx, -peakForwardFrictionForce, peakForwardFrictionForce );
fy = Math.Clamp( fy, -peakSideFrictionForce, peakSideFrictionForce );
//float forwardSlipPercent = -ForwardFriction.Slip / Tire.GetPeakSlip();
//float sideSlipPercent = SidewayFriction.Slip / Tire.GetPeakSlip();
//float slipCircleLimit = MathF.Sqrt( forwardSlipPercent * forwardSlipPercent + sideSlipPercent * sideSlipPercent );
//if ( slipCircleLimit > 1f )
//{
// float beta = MathF.Atan2( sideSlipPercent, forwardSlipPercent * 1.05f );
// float sinBeta = MathF.Sin( beta );
// float cosBeta = MathF.Cos( beta );
// float absForwardForce = ForwardFriction.Force < 0 ? -ForwardFriction.Force : ForwardFriction.Force;
// float absSideForce = SidewayFriction.Force < 0 ? -SidewayFriction.Force : SidewayFriction.Force;
// float f = absForwardForce * cosBeta * cosBeta + absSideForce * sinBeta * sinBeta;
// ForwardFriction.Force = 0.5f * ForwardFriction.Force - f * cosBeta;
// SidewayFriction.Force = 0.5f * SidewayFriction.Force - f * sinBeta;
//}
//var slipVector = new Vector2(
// ForwardFriction.Slip,
// SidewayFriction.Slip
//);
//if ( slipVector.Length > 0.001f )
//{
// var frictionDirection = slipVector.Normal;
// float frictionMagnitude = MathF.Sqrt(
// ForwardFriction.Force * ForwardFriction.Force +
// SidewayFriction.Force * SidewayFriction.Force
// );
// ForwardFriction.Force = Math.Abs( frictionDirection.x ) * frictionMagnitude * Math.Sign( ForwardFriction.Force );
// SidewayFriction.Force = Math.Abs( frictionDirection.y ) * frictionMagnitude * Math.Sign( SidewayFriction.Force );
//}
var f = MathF.Sqrt( ForwardFriction.Force * ForwardFriction.Force + SidewayFriction.Force * SidewayFriction.Force );
var d = Math.Abs( new Vector2( ForwardFriction.Slip, SidewayFriction.Slip ).Normal.y );
SidewayFriction.Force = f * d * Math.Sign( SidewayFriction.Force );
if ( absForwardSpeed > 0.01f || absAngularVelocity > 0.01f )
{
var f = MathF.Sqrt( fx * fx + fy * fy );
var d = Math.Abs( new Vector2( sx, sy ).Normal.y );
fy = f * d * Math.Sign( fy );
}
if ( IsOnGround )
{
FrictionForce.x = (hitSidewaysDirection.x * SidewayFriction.Force + hitForwardDirection.x * ForwardFriction.Force).MeterToInch();
FrictionForce.y = (hitSidewaysDirection.y * SidewayFriction.Force + hitForwardDirection.y * ForwardFriction.Force).MeterToInch();
FrictionForce.z = (hitSidewaysDirection.z * SidewayFriction.Force + hitForwardDirection.z * ForwardFriction.Force).MeterToInch();
FrictionForce.x = (hitSidewaysDirection.x * fy + hitForwardDirection.x * fx).MeterToInch();
FrictionForce.y = (hitSidewaysDirection.y * fy + hitForwardDirection.y * fx).MeterToInch();
FrictionForce.z = (hitSidewaysDirection.z * fy + hitForwardDirection.z * fx).MeterToInch();
//DebugOverlay.Normal( WorldPosition, hitSidewaysDirection * 10, overlay: true, color: Color.Red );
//DebugOverlay.Normal( WorldPosition, hitForwardDirection * 10, overlay: true, color: Color.Green );

View File

@ -34,7 +34,7 @@ public partial class VeloXWheel
{
GameObject go = new()
{
WorldPosition = ContactPosition + Vehicle.WorldRotation.Down * (Radius.MeterToInch() - 0.01f),
WorldPosition = ContactPosition + Vehicle.WorldRotation.Down * (Radius.MeterToInch() - 1f),
WorldRotation = Rotation.LookAt( hitSidewaysDirection )
};
_skidMark = go.AddComponent<LineRenderer>();
@ -53,8 +53,7 @@ public partial class VeloXWheel
protected void UpdateSkid()
{
if ( IsProxy )
return;
while ( SkidMarks.Count > MaxSkid )
{
SkidMarks.Dequeue()?.DestroyGameObject();
@ -81,7 +80,7 @@ public partial class VeloXWheel
GameObject go = new()
{
Parent = _skidMark.GameObject,
WorldPosition = ContactPosition + Vehicle.WorldRotation.Down * Radius.MeterToInch(),
WorldPosition = ContactPosition + Vehicle.WorldRotation.Down * (Radius.MeterToInch() - 1f),
WorldRotation = Rotation.LookAt( ContactNormal.RotateAround( Vector3.Zero, Rotation.FromRoll( 90 ) ) )
};
go.Flags = go.Flags.WithFlag( GameObjectFlags.Hidden, true );

View File

@ -8,9 +8,8 @@ namespace VeloX;
public partial class VeloXWheel
{
private static readonly GameObject SmokePrefab = GameObject.GetPrefab( "prefabs/particles/tire_smoke.prefab" );
private GameObject SmokeObject { get; set; }
private GameObject SmokeObject;
public const float MIN_DRIFT_ANGLE = 10f;
public const float MIN_DRIFT_SPEED = 30f;
public const float MAX_DRIFT_ANGLE = 110f;
@ -19,11 +18,13 @@ public partial class VeloXWheel
protected override void OnStart()
{
base.OnStart();
if ( IsProxy )
return;
if ( !SmokeObject.IsValid() )
SmokeObject = GameObject.GetPrefab( "prefabs/particles/tire_smoke.prefab" ).Clone( new CloneConfig() { Parent = GameObject, StartEnabled = true } );
SmokeObject = GameObject.GetPrefab( "prefabs/particles/tire_smoke.prefab" ).Clone( new CloneConfig() { Parent = GameObject, StartEnabled = true } );
SmokeObject.Flags = SmokeObject.Flags.WithFlag( GameObjectFlags.NotNetworked, true );
var emitter = SmokeObject.Components.Get<ParticleSphereEmitter>( FindMode.EverythingInSelfAndDescendants );
emitter.Enabled = false;
}
public float GetSlip()
{
@ -36,12 +37,8 @@ public partial class VeloXWheel
// return val;
return val * 5;
}
protected override void OnUpdate()
private void UpdateSmoke()
{
base.OnUpdate();
if ( IsProxy )
return;
UpdateSkid();
SmokeObject.WorldPosition = ContactPosition + Vehicle.WorldRotation.Down * Radius.MeterToInch();
smokeMul = Math.Max( 0, GetSlip() - 3 );

View File

@ -29,7 +29,7 @@ public partial class VeloXWheel : Component
public float RPM { get => AngularVelocity * 60f / MathF.Tau; set => AngularVelocity = value / (60 / MathF.Tau); }
private Vector3 StartPos { get; set; }
[Sync] private Vector3 StartPos { get; set; }
private static Rotation CylinderOffset = Rotation.FromRoll( 90 );
[Sync] public bool IsOnGround { get; private set; }
@ -39,13 +39,13 @@ public partial class VeloXWheel : Component
[Property] public float BrakeTorque { get; set; }
public float Compression { get; protected set; } // meters
public float LastLength { get; protected set; } // meters
[Sync( SyncFlags.Interpolate )] public float LastLength { get; protected set; } // meters
public float Fz { get; protected set; } // N
public float AngularVelocity { get; protected set; } // rad/s
public float RollAngle { get; protected set; } // degrees
[Sync( SyncFlags.Interpolate )] public float RollAngle { get; protected set; }
private VeloXBase Vehicle;
public VeloXBase Vehicle { get; private set; }
[Sync] public Vector3 ContactNormal { get; protected set; }
[Sync] public Vector3 ContactPosition { get; protected set; }
Rotation TransformRotationSteer => Vehicle.WorldTransform.RotationToWorld( Vehicle.SteerAngle * SteerMultiplier );
@ -59,15 +59,9 @@ public partial class VeloXWheel : Component
Inertia = BaseInertia;
}
internal void Update( VeloXBase vehicle, in float dt )
private void UpdateVisuals()
{
UpdateVisuals( vehicle, dt );
}
private void UpdateVisuals( VeloXBase vehicle, in float dt )
{
WorldRotation = vehicle.WorldTransform.RotationToWorld( vehicle.SteerAngle * SteerMultiplier ).RotateAroundAxis( Vector3.Right, -RollAngle );
WorldRotation = Vehicle.WorldTransform.RotationToWorld( Vehicle.SteerAngle * SteerMultiplier ).RotateAroundAxis( Vector3.Right, -RollAngle );
LocalPosition = StartPos + Vector3.Down * LastLength.MeterToInch();
}
@ -213,4 +207,15 @@ public partial class VeloXWheel : Component
RollAngle += MathX.RadianToDegree( AngularVelocity ) * dt;
RollAngle = (RollAngle % 360f + 360f) % 360f;
}
protected override void OnFixedUpdate()
{
UpdateSmoke();
}
protected override void OnUpdate()
{
UpdateVisuals();
UpdateSkid();
}
}

View File

@ -1,6 +1,5 @@
using Sandbox;
using System.Linq;
using static Sandbox.Services.Inventory;
namespace VeloX;
internal sealed class WheelManager : GameObjectSystem
@ -23,17 +22,17 @@ internal sealed class WheelManager : GameObjectSystem
if ( !wheels.Any() ) return;
var timeDelta = Time.Delta;
Sandbox.Utility.Parallel.ForEach( wheels, item =>
{
if ( !item.IsProxy && item.IsValid() )
item.DoPhysics( timeDelta );
} );
//foreach ( var item in wheels )
//Sandbox.Utility.Parallel.ForEach( wheels, item =>
//{
// if ( !item.IsProxy && item.IsValid() )
// item.DoPhysics( timeDelta );
//} );
foreach ( var item in wheels )
if ( item.IsValid() && !item.IsProxy )
item.DoPhysics( timeDelta );
foreach ( var wheel in wheels )
if ( !wheel.IsProxy && wheel.IsValid() )
if ( wheel.IsValid() && !wheel.IsProxy)
wheel.UpdateForce();
//sw.Stop();
@ -50,15 +49,15 @@ internal sealed class WheelManager : GameObjectSystem
if ( !engines.Any() ) return;
var timeDelta = Time.Delta;
Sandbox.Utility.Parallel.ForEach( engines, item =>
{
foreach ( var wheel in engines )
if ( !wheel.IsProxy && wheel.IsValid() )
wheel.UpdateEngine( timeDelta );
} );
//foreach ( var wheel in engines )
// if ( !wheel.IsProxy && wheel.IsValid() )
// wheel.UpdateEngine( timeDelta );
//Sandbox.Utility.Parallel.ForEach( engines, item =>
//{
// foreach ( var wheel in engines )
// if ( !wheel.IsProxy && wheel.IsValid() )
// wheel.UpdateEngine( timeDelta );
//} );
foreach ( var wheel in engines )
if ( wheel.IsValid() )
wheel.UpdateEngine( timeDelta );
//sw.Stop();

View File

@ -7,23 +7,14 @@ namespace VeloX;
[Title( "VeloX - Car" )]
public partial class VeloXCar : VeloXBase
{
protected override void FixedUpdate()
{
UpdateInput();
PhysicsSimulate();
UpdateABS();
UpdateESC();
}
protected override void OnFixedUpdate()
{
if ( IsProxy )
return;
base.OnFixedUpdate();
if ( IsProxy )
return;
UpdateABS();
UpdateESC();
var dt = Time.Delta;
//EngineThink( dt );
SimulateAerodinamics( dt );

View File

@ -10,7 +10,7 @@ namespace VeloX;
public class EngineStreamPlayer( EngineStream stream ) : IDisposable
{
private static readonly Mixer EngineMixer = Mixer.FindMixerByName( "Car Engine" );
private static readonly Mixer EngineMixer = Mixer.FindMixerByName( "Engine" );
public EngineStream Stream { get; set; } = stream;
public EngineState EngineState { get; set; }
@ -26,7 +26,7 @@ public class EngineStreamPlayer( EngineStream stream ) : IDisposable
public void Update( float deltaTime, Vector3 position, bool isLocal = false )
{
var globalPitch = 1.0f;
// Gear wobble effect
@ -51,7 +51,7 @@ public class EngineStreamPlayer( EngineStream stream ) : IDisposable
foreach ( var (id, layer) in Stream.Layers )
{
EngineSounds.TryGetValue( layer, out var channel );
if ( !channel.IsValid() && layer.AudioPath.IsValid() )
{
channel = Sound.PlayFile( layer.AudioPath );
@ -103,6 +103,7 @@ public class EngineStreamPlayer( EngineStream stream ) : IDisposable
channel.Position = position;
channel.ListenLocal = isLocal;
channel.Paused = EngineSoundPaused || layer.IsMuted;
channel.FollowParent = true;
channel.TargetMixer = EngineMixer;
}