Skip to content

ItemSightAsset — Sight Attachments

ItemSightAsset defines optical attachments that provide zoom, night vision, and reticle features. It inherits from ItemCaliberAsset, giving it access to the full stat modifier system. At 230 lines it is the second-largest attachment class after ItemTacticalAsset. It covers zoom configuration, night vision modes with per-type default colors and fog settings, aim alignment transform routing (sight-based vs gun-based), holographic reticles, scope overlay texel alignment, third-person zoom, and distance marker markings rendered on the scope view.

Source code location: Unturned/Bundles/ItemSightAsset.cs

Inheritance Chain

ItemAsset
  → ItemCaliberAsset
    → ItemSightAsset

Class Definition

csharp
public class ItemSightAsset : ItemCaliberAsset
{
    protected GameObject _sight;
    public GameObject sight => _sight;

    private ELightingVision _vision;
    public ELightingVision vision => _vision;

    public Color nightvisionColor;
    public float nightvisionFogIntensity;

    public float zoom { get; private set; }
    public float thirdPersonZoomFactor { get; private set; }

    private bool _isHolographic;
    public bool isHolographic => _isHolographic;

    public bool shouldZoomUsingEyes;
    public bool shouldOffsetScopeOverlayByOneTexel;

    public EAimAlignmentTransformOwner AimAlignmentTransformOwner { get; set; }
    public string AimAlignmentTransformPath { get; set; }
    public Vector3 AimAlignmentLocalOffset { get; set; }

    public List<DistanceMarker> distanceMarkers;
}

Core Fields

Prefab Loading

FieldType.dat KeyDefaultDescription
_sightGameObjectBundle "Sight"Sight attachment prefab

Zoom Configuration

FieldType.dat KeyDefaultDescription
zoomfloatZoom1.0 (clamped)Zoom factor (e.g., 2 = 2x magnification)
thirdPersonZoomFactorfloatThirdPerson_ZoomUseableGun.DEFAULT_THIRD_PERSON_ZOOM_FACTOR (~0.375)Third-person FOV zoom

Both values are clamped to a minimum of 1.0 via Mathf.Max(1.0f, ...). The zoom field was originally a target field of view (90/fov) prior to 2022-04-11; it was refactored to be a straightforward zoom factor.

Night Vision

FieldType.dat KeyDefaultDescription
_visionELightingVisionVisionNONENight vision type
nightvisionColorColorNightvision_ColorPer-type defaultNV filter color
nightvisionFogIntensityfloatNightvision_Fog_Intensity0.5 (civilian) / 0.25 (military)NV fog override

The ELightingVision enum values are NONE, CIVILIAN, and MILITARY. When the vision type is CIVILIAN or MILITARY, default values for color and fog intensity are applied:

csharp
if (vision == ELightingVision.CIVILIAN)
{
    nightvisionColor = p.data.LegacyParseColor32RGB("Nightvision_Color",
        defaultValue: LevelLighting.NIGHTVISION_CIVILIAN);
    nightvisionFogIntensity = p.data.ParseFloat("Nightvision_Fog_Intensity",
        defaultValue: 0.5f);
}
else if (vision == ELightingVision.MILITARY)
{
    nightvisionColor = p.data.LegacyParseColor32RGB("Nightvision_Color",
        defaultValue: LevelLighting.NIGHTVISION_MILITARY);
    nightvisionFogIntensity = p.data.ParseFloat("Nightvision_Fog_Intensity",
        defaultValue: 0.25f);
}

Military night vision has lower default fog intensity (0.25 vs 0.5), providing clearer vision in foggy conditions. The color uses LegacyParseColor32RGB which expects comma-separated R,G,B values without alpha.

Holographic and Scope Rendering

FieldType.dat KeyDefaultDescription
_isHolographicboolHolographic (flag)falseHolographic reticle
shouldZoomUsingEyesboolZoom_Using_EyesfalseZoom via camera FOV instead of scope overlay
shouldOffsetScopeOverlayByOneTexelboolOffset_Scope_Overlay_By_One_TexelfalseFix scope overlay alignment

Holographic sights typically use shouldZoomUsingEyes = true to zoom the main camera FOV rather than rendering a separate scope camera with an overlay. The shouldOffsetScopeOverlayByOneTexel flag shifts the scope overlay by one texel to keep the center pixel aligned — a precision fix for certain scope textures.

Aim Alignment System

The aim alignment system controls where the camera is positioned when aiming down sights with this scope:

csharp
public enum EAimAlignmentTransformOwner
{
    Sight,  // Look for aim alignment transform relative to sight model.
    Gun,    // Look for aim alignment transform relative to equipable prefab.
}
Field.dat KeyDefaultDescription
AimAlignmentTransformOwnerAimAlignment_OwnerSightWhere to find the alignment transform
AimAlignmentTransformPathAimAlignment_PathnullCustom hierarchy path relative to owner
AimAlignmentLocalOffsetAimAlignment_LocalOffset(0,0,0)Fine-tuning position offset

When the owner is Sight, the default path is Model_0/Aim relative to the sight prefab. When the owner is Gun, a custom AimAlignment_Path must be provided relative to the equipable weapon prefab. The AimAlignmentLocalOffset provides a final position tweak applied to the resolved transform.

Distance Markers

The distanceMarkers list defines rangefinder markings rendered on the sight overlay. Each marker is a struct:

csharp
public struct DistanceMarker : IDatParseable
{
    public enum ESide { Left, Right }

    public float distance;       // Target distance for this marker
    public float lineOffset;     // [0,1] offset from center
    public float lineWidth;      // [0,1] line width
    public ESide side;           // Left or Right
    public bool hasLabel;        // Show distance text
    public Color32 color;        // Marker color
}

Markers are parsed from the .dat using the generic list-of-structs deserializer:

csharp
distanceMarkers = p.data.ParseListOfStructs<DistanceMarker>("DistanceMarkers");

Each marker's TryParse method reads an IDatDictionary:

  • Distance (required): the range for this mark.
  • LineOffset: [0, 1] distance from center to start of line.
  • LineWidth: [0, 1] local width of the horizontal line (default 0.05).
  • Side: Left or Right of the center line (default Right).
  • HasLabel: whether the distance text is visible (default true).
  • Color: the marker color.

BuildDescription

ItemSightAsset overrides BuildDescription to add zoom-specific information:

csharp
public override void BuildDescription(ItemDescriptionBuilder builder, Item itemInstance)
{
    base.BuildDescription(builder, itemInstance);

    if (!builder.HasFlag(EItemDescriptionFlags.Uncategorized))
        return;

    if (zoom != 1.0f)
        builder.Append(localization.format("ItemDescription_ZoomFactor", zoom),
            DescSort_GunAttachmentStat);

    if (thirdPersonZoomFactor != UseableGun.DEFAULT_THIRD_PERSON_ZOOM_FACTOR)
        builder.Append(localization.format("ItemDescription_ThirdPersonZoomFactor",
            thirdPersonZoomFactor), DescSort_GunAttachmentStat + 1);
}

The base class (ItemCaliberAsset.BuildDescription) renders all inherited stat modifiers (recoil, spread, sway, shake, aim duration, movement speed, damage, gravity).

PopulateAsset

csharp
public override void PopulateAsset(in PopulateAssetParameters p)
{
    base.PopulateAsset(in p);

    _sight = loadRequiredAsset<GameObject>(p.bundle, "Sight");

    if (p.data.ContainsKey("Vision"))
    {
        _vision = (ELightingVision)Enum.Parse(typeof(ELightingVision),
            p.data.GetString("Vision"), true);
        // Set nightvision defaults based on vision type...
    }
    else
    {
        _vision = ELightingVision.NONE;
    }

    zoom = Mathf.Max(1.0f, p.data.ParseFloat("Zoom"));
    thirdPersonZoomFactor = Mathf.Max(1.0f,
        p.data.ParseFloat("ThirdPerson_Zoom",
            defaultValue: UseableGun.DEFAULT_THIRD_PERSON_ZOOM_FACTOR));
    shouldZoomUsingEyes = p.data.ParseBool("Zoom_Using_Eyes");
    shouldOffsetScopeOverlayByOneTexel =
        p.data.ParseBool("Offset_Scope_Overlay_By_One_Texel");

    AimAlignmentTransformOwner = p.data.ParseEnum("AimAlignment_Owner",
        EAimAlignmentTransformOwner.Sight);
    AimAlignmentTransformPath = p.data.GetString("AimAlignment_Path");
    AimAlignmentLocalOffset = p.data.ParseVector3("AimAlignment_LocalOffset");

    _isHolographic = p.data.ContainsKey("Holographic");

    distanceMarkers = p.data.ParseListOfStructs<DistanceMarker>("DistanceMarkers");
}

Cargo Data Export

BuildCargoData writes sight-specific fields to the Sight table:

csharp
CargoDeclaration data = builder.GetOrAddDeclaration("Sight");
data.Append("GUID", GUID);
data.Append("Vision", vision);
data.Append("Nightvision_Color", nightvisionColor);
data.Append("Nightvision_Fog_Intensity", nightvisionFogIntensity);
data.Append("Zoom", zoom);
data.Append("ThirdPerson_Zoom", thirdPersonZoomFactor);
data.Append("Zoom_Using_Eyes", shouldZoomUsingEyes);
data.Append("Holographic", isHolographic);

The base class appends caliber fields to the Caliber table.

Inherited Stat Modifiers (ItemCaliberAsset)

All sight assets inherit the full stat modifier system. Common sight configurations:

StatTypical Scope ValueTypical Red Dot Value
Recoil_X1.0 (no change)1.0
Recoil_Y1.01.0
Spread1.01.0
Sway1.3-1.5 (more sway)0.9-1.0 (less sway)
Shake1.01.0
Aim_Duration_Multiplier1.3-1.5 (slower ADS)0.8-0.9 (faster ADS)
Aiming_Movement_Speed_Multiplier0.8-0.91.0

High-magnification scopes typically increase sway and slow down ADS time to balance the zoom advantage. Red dot and holographic sights often reduce sway slightly for faster target acquisition.

Attachment Integration

When a sight is attached to a gun, UseableGun applies its effects:

  1. Zoom application: If shouldZoomUsingEyes is false, a scope camera renders the zoomed view with an overlay texture. If true, the main camera FOV is reduced.
  2. Night vision activation: If _vision is not NONE, the night vision post-process effect is enabled with the sight's color and fog intensity.
  3. Aim alignment: The player's camera position during ADS is determined by the sight's aim alignment transform.
  4. Distance markers: If markers are defined, they are rendered as lines and labels on the scope overlay.
  5. Prefab instantiation: The _sight GameObject is instantiated and parented to the weapon's Hook_Sight transform.

Common Issues

  1. Zoom minimum clamp — The zoom value is clamped to Mathf.Max(1.0f, ...). Setting Zoom to 0.5 produces 1x zoom, not 0.5x. There is no way to create a de-magnified sight.
  2. Night vision without Zoom — A sight with Vision=CIVILIAN but Zoom=1.0 provides night vision with no magnification. The night vision effect is independent of the zoom system.
  3. AimAlignment_Owner=Gun without path — When the owner is Gun but no AimAlignment_Path is set, the alignment transform resolves to the gun's root, which may produce incorrect camera positioning.
  4. ThirdPerson_Zoom defaults — The default third-person zoom factor is approximately 0.375, which matches the standard third-person camera. Customizing this value only matters for sights with non-standard first-person zoom behavior.
  5. Distance marker parsing failures — If a marker's Distance field is missing or invalid, TryParse returns false and the marker is silently skipped. No error is reported for malformed marker entries.
  6. Holographic and Eyes Zoom — Holographic sights typically use Zoom_Using_Eyes with a modest zoom value. Using Holographic without Zoom_Using_Eyes renders the scope overlay over the holographic reticle, which may look incorrect.