< Summary

Information
Class: Gravitas.PhysicsSettingsSaver
Assembly: Gravitas
File(s): /home/runner/work/Gravitas/Gravitas/src/Gravitas/Settings/PhysicsSettingsSaver.cs
Line coverage
100%
Covered lines: 48
Uncovered lines: 0
Coverable lines: 48
Total lines: 192
Line coverage: 100%
Branch coverage
100%
Covered branches: 30
Total branches: 30
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
BindContext(...)100%11100%
ApplyTo(...)100%11100%
CreateSettings()100%1818100%
OnEarlyApply()100%11100%
CreateCollisionMatrix()100%1212100%

File(s)

/home/runner/work/Gravitas/Gravitas/src/Gravitas/Settings/PhysicsSettingsSaver.cs

#LineLine coverage
 1//=======================================================================
 2// PhysicsSettingsSaver.cs
 3//=======================================================================
 4// MIT License, Copyright (c) 2026–present David Oravsky (mrdav30)
 5// See LICENSE file in the project root for full license information.
 6//=======================================================================
 7
 8using Chronicler;
 9using FixedMathSharp;
 10using Gravitas.Support;
 11using MemoryPack;
 12using System;
 13using System.Text.Json.Serialization;
 14
 15namespace Gravitas;
 16
 17/// <summary>
 18/// Stores one serialized row of a physics collision matrix.
 19/// </summary>
 20[Serializable]
 21[MemoryPackable]
 22public partial struct MatrixRow
 23{
 24    /// <summary>
 25    /// Collision enablement values for this matrix row.
 26    /// </summary>
 27    [JsonInclude]
 28    public bool[] row;
 29}
 30
 31/// <summary>
 32/// Serializes optional physics settings and applies them to an explicit world context.
 33/// </summary>
 34[Serializable]
 35[MemoryPackable]
 36public sealed partial class PhysicsSettingsSaver : DefaultSaver
 37{
 38    /// <summary>
 39    /// Optional fixed-step frame rate in simulation frames per second.
 40    /// </summary>
 41    [JsonInclude]
 42    public int? FrameRate;
 43
 44    /// <summary>
 45    /// Optional square layer-to-layer collision matrix.
 46    /// </summary>
 47    [JsonInclude]
 48    public MatrixRow[]? CollisionMatrix;
 49
 50    /// <summary>
 51    /// Optional include-mask bits for ground and support checks.
 52    /// </summary>
 53    [JsonInclude]
 54    public int? GroundCheckLayerMaskBits;
 55
 56    /// <summary>
 57    /// Optional default continuous-collision mode.
 58    /// </summary>
 59    [JsonInclude]
 60    public ContinuousCollisionMode? DefaultContinuousCollisionMode;
 61
 62    /// <summary>
 63    /// Optional maximum same-frame continuous-collision time-of-impact iterations.
 64    /// </summary>
 65    [JsonInclude]
 66    public int? ContinuousCollisionMaxToiIterations;
 67
 68    /// <summary>
 69    /// Optional discrete 3D constraint-island solver iteration count.
 70    /// </summary>
 71    [JsonInclude]
 72    public int? DiscreteSolverIterations;
 73
 74    /// <summary>
 75    /// Optional closing-speed threshold below which restitution is disabled.
 76    /// </summary>
 77    [JsonInclude]
 78    public Fixed64? RestitutionVelocityThreshold;
 79
 80    /// <summary>
 81    /// Optional number of frames an empty partition remains retained for reuse.
 82    /// </summary>
 83    [JsonInclude]
 84    public int? RetainedPartitionTimeToKillFrames;
 85
 86    /// <summary>
 87    /// Optional maximum retained partitions checked per retirement sweep.
 88    /// </summary>
 89    [JsonInclude]
 90    public int? RetainedPartitionRetirementSweepBudget;
 91
 92    /// <summary>
 93    /// Optional dimensional runtime mode.
 94    /// </summary>
 95    [JsonInclude]
 96    public PhysicsRuntimeMode? RuntimeMode;
 97
 98    /// <summary>
 99    /// Optional Y-axis half-thickness for 2D colliders embedded in mixed queries and contacts.
 100    /// </summary>
 101    [JsonInclude]
 102    public Fixed64? Mixed2DHalfThickness;
 103
 104    [NonSerialized]
 105    [MemoryPackIgnore]
 106    private GravitasWorldContext? _context;
 107
 108    /// <summary>
 109    /// Binds the context used by the inherited early-apply phase.
 110    /// </summary>
 111    public void BindContext(GravitasWorldContext context)
 112    {
 1113        SwiftThrowHelper.ThrowIfNull(context, nameof(context));
 1114        _context = context;
 1115    }
 116
 117    /// <summary>
 118    /// Creates and applies these settings to the specified context.
 119    /// </summary>
 120    public void ApplyTo(GravitasWorldContext context)
 121    {
 4122        SwiftThrowHelper.ThrowIfNull(context, nameof(context));
 4123        context.ApplySettings(CreateSettings());
 2124    }
 125
 126    /// <summary>
 127    /// Creates validated physics settings, using defaults for omitted values.
 128    /// </summary>
 129    public PhysicsSettings CreateSettings()
 130    {
 10131        var settings = new PhysicsSettings(
 10132            FrameRate,
 10133            CreateCollisionMatrix(),
 10134            GroundCheckLayerMaskBits.HasValue
 10135                ? new PhysicsLayerMask(GroundCheckLayerMaskBits.Value)
 10136                : null);
 137
 7138        if (DefaultContinuousCollisionMode.HasValue)
 4139            settings.DefaultContinuousCollisionMode = DefaultContinuousCollisionMode.Value;
 5140        if (ContinuousCollisionMaxToiIterations.HasValue)
 2141            settings.ContinuousCollisionMaxToiIterations = ContinuousCollisionMaxToiIterations.Value;
 5142        if (DiscreteSolverIterations.HasValue)
 2143            settings.DiscreteSolverIterations = DiscreteSolverIterations.Value;
 5144        if (RestitutionVelocityThreshold.HasValue)
 3145            settings.RestitutionVelocityThreshold = RestitutionVelocityThreshold.Value;
 5146        if (RetainedPartitionTimeToKillFrames.HasValue)
 2147            settings.RetainedPartitionTimeToKillFrames = RetainedPartitionTimeToKillFrames.Value;
 5148        if (RetainedPartitionRetirementSweepBudget.HasValue)
 2149            settings.RetainedPartitionRetirementSweepBudget = RetainedPartitionRetirementSweepBudget.Value;
 5150        if (RuntimeMode.HasValue)
 2151            settings.RuntimeMode = RuntimeMode.Value;
 5152        if (Mixed2DHalfThickness.HasValue)
 2153            settings.Mixed2DHalfThickness = Mixed2DHalfThickness.Value;
 154
 5155        return settings;
 156    }
 157
 158    /// <summary>
 159    /// Applies these settings to the context bound by <see cref="BindContext"/>.
 160    /// </summary>
 161    protected override void OnEarlyApply()
 162    {
 2163        SwiftThrowHelper.ThrowIfTrue(
 2164            _context == null,
 2165            nameof(PhysicsSettingsSaver),
 2166            "PhysicsSettingsSaver requires an explicit GravitasWorldContext. Call BindContext or ApplyTo before applying
 167
 1168        ApplyTo(_context!);
 1169    }
 170
 171    private bool[,]? CreateCollisionMatrix()
 172    {
 10173        int numberOfLayers = CollisionMatrix?.Length ?? 0;
 10174        if (CollisionMatrix == null || numberOfLayers == 0)
 5175            return null;
 176
 5177        bool[,] matrix = new bool[numberOfLayers, numberOfLayers];
 22178        for (int i = 0; i < numberOfLayers; ++i)
 179        {
 9180            bool[] row = CollisionMatrix[i].row;
 9181            SwiftThrowHelper.ThrowIfTrue(
 9182                row == null || row.Length != numberOfLayers,
 9183                nameof(CollisionMatrix),
 9184                "Physics settings collision matrix rows must be square.");
 185
 36186            for (int j = 0; j < numberOfLayers; ++j)
 12187                matrix[i, j] = row[j];
 188        }
 189
 2190        return matrix;
 191    }
 192}