ConstrainedMarkers Module

Description

Display a GUI marker of a 3D position, constrained to a boundary.

Demo

Place: marker-demo.rbxl (26.8 KB)

Video

https://giant.gfycat.com/RequiredIncompleteArcticwolf.webm

Changelog

  • v1.1 (2017-12-19)
    • Support Attachment as marker target.
    • Configurable default marker appearance: size, color, transparency, icon.
    • Compensate for rotation of boundary GUI.
    • Compensate for negative size of boundary GUI.
    • Check argument types.
    • Format docs in Markdown.
  • v1.0 (2017-12-18)
    • SetBoundaryType: How boundary is shaped.
      • Rectangle
        • LerpSlide variation (default)
        • SquareSlide variation
        • CircleSlide variation
    • SetConstraintType: How markers behave outside boundary.
    • SetArrowsEnabled: Show/hide arrows globally.
      • ArrowEnabled: Per-marker override.
    • SetOrigin: Position markers from specified Camera, or CurrentCamera.
    • BodyGUI/ArrowGUI: Create markers with a basic default appearance, or
      specified GUIs for custom appearance.
    • Supports Vector3, CFrame, BasePart, or Model as marker target.

TODO

  • More boundary types.
  • Optimize!!!

Synopsis

container = ConstrainedMarkers.New(boundary, enabled, defaults)
boundary = MarkerContainer:BoundaryGUI()
MarkerContainer:SetBoundaryType(type, ...)
MarkerContainer:SetEnabled(enabled)
MarkerContainer:SetConstraintType(type)
MarkerContainer:SetArrowsEnabled(enabled)
MarkerContainer:SetOrigin(origin)
markerState = MarkerContainer:CreateMarker(initialState)
markerStates = MarkerContainer:Markers()
MarkerContainer:RemoveMarkers(...)
MarkerContainer:RemoveAllMarkers()
MarkerContainer:UpdateMarkers(...)

Usage

This module returns a table.

ConstrainedMarkers = require(...)

A MarkerContainer can be created with the New function.

container = ConstrainedMarkers.New(boundary)

The New function can receive an optional GuiObject, which will become the
boundary GUI. This is used to contain all the marker GUIs in the container.
Its Position and Size also determine the boundary limits. If no value is
given, then a default will be created. If needed, the boundary GUI can be
retrieved like so:

boundary = container:BoundaryGUI()

MarkerContainer influences the following properties of the boundary GUI:

  • Visible: Set to true when the container becomes enabled, set to false when
    the container becomes disabled.

Markers are added to the container with CreateMarker. This receives a table
specifying the initial values of the marker.

marker = container:CreateMarker({
	Target = workspace.Part,
})

CreateMarker returns the marker, which is a table that holds the marker’s
state. This state is read by the container every update cycle, which occurs
every render frame.

A marker can be removed with RemoveMarkers. This receives any number of valid
markers.

container:RemoveMarkers(marker)

If a value in the marker state is changed, then UpdateMarkers should be called
with the marker afterwards. While this may not be necessary in some cases, it
will ensure that the change is recognized by the container. UpdateMarkers can
receive any number of valid markers as arguments.

marker.Target = workspace.AnotherPart
container:UpdateMarkers(marker)

A marker’s state contains the BodyGUI and ArrowGUI fields for retrieving and
setting the marker’s GUI. When one or both of these fields is unspecified
while creating the marker, a simple default is created instead.

BodyGUI is the main GUI for the marker. MarkerContainer influences the
following properties of the BodyGUI:

  • Position: Overridden to appear over or near the target.
  • Visible: Overridden to show or hide the marker.
  • Parent: Set to the boundary GUI when the marker is created. Set to nil when
    the marker is removed.

ArrowGUI is used to point at the target while the marker is constrained.
MarkerContainer influences the following properties of the ArrowGUI:

  • Rotation: Overridden to point the arrow at the target.
  • Visible: Overridden to show or hide the arrow.
  • Parent: Set to the BodyGUI when the marker is created. Set to nil when the
    marker is removed.

Other than the overridden properties, both the BodyGUI and ArrowGUI can be
modified to meet the desired appearance. To detect changes made by the
MarkerContainer, use of GetPropertyChangedSignal and Changed is recommended
and encouraged.

Reference

Reference

Note: By convention, only fields that start with an uppercase letter are
considered to be a part of this module’s public API. Other exposed fields are
considered private, may change at any time, and should therefore not be
accessed.


container = ConstrainedMarkers.New(boundary, enabled, defaults)

Create a new container for constrained markers.

Arguments

  • boundary (GuiObject, nil)

    Used as the parent of marker GUIs.

    The limits of the boundary depends on the size of the GUI. Note that,
    since only the position of a marker is constrained, the marker’s body may
    still appear slightly outside the boundary.

    Defaults to a basic GUI object copied from a template.

  • enabled (boolean, nil)

    Sets whether the container will be enabled immediately, before being
    returned by New.

    Defaults to true.

  • defaults (table, nil)

    If specified, configures the appearance of the default marker template.
    The following fields are recognized:

    • Size (UDim, nil)

      Sets the width and height of the marker’s BodyGUI. The Scale component
      is applied to both axes, and is based on the boundary GUI’s shortest
      axis.

      Defaults to UDim.new(0.05, 0).

    • Color3 (Color3, nil)

      Sets the color of the marker.

      Defaults to Color3.fromRGB(242, 72, 72).

    • Transparency (number, nil)

      Sets the transparency of the marker.

      Defaults to 0.

    • Icon (string, nil)

      Sets an image to be displayed on the marker.

      Defaults to no image.

    • IconRect (Rect, nil)

      Sets the sprite offset and size of the icon.

      Defaults to Rect.new(0, 0, 0, 0), which uses the entire image.

    • IconColor3 (Color3, nil)

      Sets the color of the icon.

      Defaults = Color3.fromRGB(255, 255, 255).

Returns

  • container (MarkerContainer)

    An object representing the container, with several methods to modify its
    behavior.


boundary = MarkerContainer:BoundaryGUI()

Returns the boundary GUI.

Returns

  • boundary (GuiObject)

    The boundary GUI.


MarkerContainer:SetBoundaryType(type, ...)

Sets the shape of the boundary. Extra arguments depend on the type.

Arguments

  • type (string)

    May be one of the following values:

    • “Rectangle”

      Fits the exact limits of the boundary GUI.

      This type has the following extra arguments:

      • slideType (string, nil)

        Determines how constrained markers slide along the boundary edge.
        May be one of the following values:

        • “SquareSlide”: Slides in a square-like way.
        • “CircleSlide”: Slides in a circular way.
        • “LerpSlide”: Interpolates between SquareSlide and CircleSlide.

        Defaults to LerpSlide.

    • “Circle” (TODO)

      A circle centered on the boundary. The diameter is the size of the
      boundary’s shortest axis.

    • “Ellipse” (TODO)

      An ellipse stretched to the limits of the boundary GUI.

    • “TruncatedCircle” (TODO)

      Similar to Rectangle, where the sides on one axis are round instead of
      flat.

      This type has the following extra arguments:

      • roundedAxis (string, nil)

        Determines which axis is rounded. May be one of the following
        values:

        • “X”: Rounds the X axis (left and right).
        • “Y”: Rounds the Y axis (top and bottom).

        Defaults to X.

    Defaults to Rectangle.


MarkerContainer:SetEnabled(enabled)

Sets whether markers will be displayed and updated.

When disabled, the boundary GUI will be made invisible, and the update cycle
will not be active. Consequentially, the container can be safely garbage
collected.

The container is enabled by default.

Arguments

  • enabled (boolean)

    Whether the container is enabled.


MarkerContainer:SetConstraintType(type)

Specifies the behavior of markers outside the boundary.

Arguments

  • type (string)

    May be one of the following values:

    • “Constrained”: The marker is constrained to the edge of the boundary.
    • “Hidden”: The marker is hidden.
    • “Unconstrained”: The marker is unaffected by the boundary.

    Defaults to Constrained.


MarkerContainer:SetArrowsEnabled(enabled)

Sets whether a marker will display an arrow pointing to its target when the
marker is constrained. Note that this state can be overwritten per marker with
the marker’s ArrowEnabled field.

Arrows are enabled by default.

Arguments

  • enabled (boolean)

    Whether arrows are enabled.


MarkerContainer:SetOrigin(origin)

Sets the origin from which marker positions are determined. Marker target
positions are projected onto a Camera’s 2D plane.

Arguments

  • origin (Camera, nil)

    Determines the origin from one of a number of types:

    • Camera: Uses the Camera’s CFrame.
    • nil: Uses the value of Workspace.CurrentCamera. Note that this updates
      when the CurrentCamera changes.

markerState = MarkerContainer:CreateMarker(initialState)

Creates a new marker from an initial state.

Arguments

  • initialState (table, nil)

    Each recognized field in the initial state sets the value of the
    corresponding field the marker state. If an initial value is nil, then the
    field will be set to a default instead.

Returns

  • markerState (table)

    A table that holds the marker’s state. The following fields are
    recognized:

    • Target (Vector3, CFrame, BasePart, Attachment, Model, Camera, nil)

      The position being targeted by the marker.

      • Vector3: A position in world space.
      • CFrame: Uses the CFrame’s position.
      • BasePart: Uses the Position property.
      • Attachment: Uses WorldPosition property.
      • Model: Uses the PrimaryPart property. The marker will be hidden
        while the PrimaryPart is nil.
      • nil: Hides the marker.

      Defaults to nil.

    • BodyGUI (GuiObject)

      Used as the body of the marker GUI.

      The GUI will have its Position property modified. It may also have its
      Transparency and Visible properties modified in some cases. The GUI is
      parented to the boundary GUI, and parented to nil if the marker is
      removed.

      Defaults to a basic GUI object copied from a template.

    • ArrowGUI (GuiObject)

      Used as the arrow of the marker GUI.

      The GUI will have its Rotation and Visible properties modified, and
      will be visible only if arrows are enabled. The GUI is parented to the
      body GUI, and parented to nil if the marker is removed.

      Note that a Rotation of 0 should have the arrow pointing in the
      positive X direction.

      Defaults to a basic GUI object copied from a template.

    • ArrowEnabled (boolean, nil)

      Sets whether this marker’s arrow is visible. When nil, the visibility
      is determined by SetArrowsEnabled.

      Defaults to nil.


markerStates = MarkerContainer:Markers()

Returns a list of all the marker states in the container. Note that the order
is undefined.

Returns

  • markerStates (table)

    A list of marker states.


MarkerContainer:RemoveMarker(...)

Removes one or more markers from the container. Changes made to each marker’s
state will no longer be recognized, and the marker will no longer be valid for
the container.

Arguments

  • … (table)

    One or more marker states to be removed. Throws an error if any value is
    not a known marker state.


MarkerContainer:RemoveAllMarkers()

Removes all markers from the container.


MarkerContainer:UpdateMarker(...)

Reevaluates a marker state, forcing all changes made to the state to be
recognized. Also checks if all values in the state are valid.

Arguments

  • … (table)

    One or more marker states to be updated. Throws an error if any value is
    not a known marker state.


48 Likes

Attachment marker targets would be neat.

3 Likes

Updated to v1.1:

  • Support Attachment as marker target.
  • Configurable default marker appearance: size, color, transparency, icon.
  • Compensate for rotation of boundary GUI.
  • Compensate for negative size of boundary GUI.
  • Check argument types.
  • Format docs in Markdown (reference now included in post).

The fading markers idea has been scrapped.

  • There is no global transparency multiplier. Fading markers correctly would require keeping track of each GUI descendant, and its desired transparency.
  • Out of scope; a marker’s position on screen is unrelated to the distance or angle from the target. A user will be able to make these calculations themselves with more flexibility.

Optimizations will be done next. after some initial profiling, I found that each marker requires about 1ms to redraw, which really adds up with a lot of markers.

6 Likes