Trying out some level composition ideas.
Your game is looking to be perfect in every way.
Design style fits Roblox really well.
Looks like a running game, easy controls to port to other devices.
Great stuff.
I know this is from 10 days ago but,
tables are so much better.
Yes. WorldToScreenPoint returns a Vector3, where X and Y form the position of the marker. If Z is less than 0, then the position should be flipped:
if coord.Z < 0 then
position = parent.AbsoluteSize - position
end
For an arrow that (kind of) points at the target:
local diff = position - parent.AbsoluteSize/2
arrow.Rotation = math.deg(math.atan2(diff.Y, diff.X)) -- 0 points to the right.
To constrain within a minimum and maximum:
local min = 20 -- Border size
local max = parent.AbsoluteSize - min
position = Vector2.new(
position.X < min.X and min.X or position.X > max.X and max.X or position.X,
position.Y < min.Y and min.Y or position.Y > max.Y and max.Y or position.Y
)
The Z coordinate can also be used to sort the markers, so that the nearest will appear on top. This will be easier to do when ZIndexBehavior.Sibling is enabled.
local maxZ -- Z of marker that is furthest away.
marker.ZIndex = 1 + (maxZ - coord.Z)*100
I am working on a drawing. I tend to swap between drawing, building and scripting at the moment. Hopefully that’s a good thing.
Here’s the WIP main menu for my upcoming game. Expected PC/Mobile release mid-2018.
If the off-center lock bothers you, rest assured it will be fixed one day
Looks like my character lol.
Not failing my finals… and then I’ll be going hard on Flight! for a while.
Working on my RPG game, Dungeoneer still. Here’s a mount which one of the builders has made.
And here’s a screenshot from one of the maps.
I’ve finished a second iteration of constrained GUIs. Instead of being hard-coded, a function is used to determine the boundary. It receives a rectangle (representing the screen), and the point to be constrained. It returns the point constrained to the edge of the boundary, and whether the point was inside. With this, I’ll be able to implement several types of boundaries, including the one with the curved edges in the previous video.
For now, I started with a simple rectangular boundary. An issue to be resolved was how to position the markers on their sliding axis (whichever axis is moving when the marker is constrained). There are several decent ways to go about it:
Dist-Radius
The position is based off the distance from the point to the center of the rectangle ((point - center).magnitude
).
https://giant.gfycat.com/ElaborateVainGerbil.webm
This produces continuous motion as the markers cross the boundary. However, when the markers are constrained, they tend to skip around and get bunched up, especially when facing away from their targets. It isn’t demonstrated in the video, but it looks awful.
Size-Radius
The position is based of the size of the rectangle ((size/2).magnitude
).
https://giant.gfycat.com/FittingBlondEyra.webm
This produces constrained markers that move around a lot more smoothly. It also does a better job of indicating distance. However, the markers will jump as they cross the boundary, which also looks pretty bad.
Lerp-Radius
This combines both of the previous methods. When the point is at the edge, it behaves like the dist-radius. As the point moves further away from the edge, it lerps towards size-radius.
https://giant.gfycat.com/GreenSaneKiskadee.webm
This produces markers that move around both smoothly and continuously, and looks much better. Perhaps one small drawback is that markers will tend to weave around at certain points around the edge, which may give a false sense of movement. But overall, I like it more than the other two methods.
Since this thing seems to be quite popular, I’ll focus on turning it into a proper module. Here are some features I’m mulling over:
- Boundary determined by GuiObject.
- Configure size and appearance of markers.
- Maybe just receive a GuiObject.
- Mark Vector3s, CFrames, BaseParts, Models (PrimaryPart).
- Optional arrows that point at position.
- Optional fade based on distance.
- Several boundary types.
- rectangle (lerp)
- rectangle-dist
- rectangle-size
- circle
- ellipse
- Other things I haven’t thought of yet.
Finally, Desmos graphing calculator is great tool if your having trouble conceptualizing difficult math.
Surprised that those meshes are under 5k triangles, your using a subsurface modifier?
p.s. Cool work, I would love to check out the game when completed. (I’m a space enthusiast)
Here is a working copy of how I plan to implement the module API. Feel free to make suggestions.
ConstrainedMarkers
Display a marker of a 3D position, constrained to a boundary.
Synopsis
+ ConstrainedMarkers.New(boundary) container
+ MarkerContainer:BoundaryGUI() boundary
+ MarkerContainer:SetBoundaryType(type, ...)
+ MarkerContainer:SetEnabled(enabled)
+ MarkerContainer:SetConstraintType(type) Constrain --> Constraint!
+ MarkerContainer:SetArrowsEnabled(enabled)
+ MarkerContainer:SetFadeDistance(distanceRange)
+ MarkerContainer:SetFadeDistanceOrigin(origin)
+ MarkerContainer:SetFadeAngle(angleRange)
+ MarkerContainer:SetFadeAngleOrigin(origin)
+ MarkerContainer:CreateMarker(initialState) markerState
+ MarkerContainer:Markers() markerStates
+ MarkerContainer:RemoveMarker(...)
+ MarkerContainer:RemoveAllMarkers()
+ MarkerContainer:UpdateMarker(...)
- ConstrainedMarkers.New(properties) container
- MarkerContainer:AddMarker(position, id, body, arrow) id
- MarkerContainer:RemoveMarker(id)
- MarkerContainer:RemoveAllMarkers()
- MarkerContainer:Markers() ids
- MarkerContainer:MarkerGUI(id) body, arrow
- MarkerContainer:Get(name) value
- MarkerContainer:Set(name, value)
- MarkerContainer Properties:
- BoundaryGUI
- BoundaryType
- ConstrainType
- ArrowsDisabled
- FadeDistance
- FadeDistanceOrigin
- FadeAngle
- FadeAngleOrigin
- Disabled
TODO
- Support rotation of the boundary GUI.
- Add interpolation types for fade transitions.
Reference
container = ConstrainedMarkers.New(boundary)
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.
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, nil)
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, nil)
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:SetFadeDistance(distanceRange)
Actively sets the transparency of markers based on the distance (in
studs) between the marker's target and an origin.
FadeDistance is disabled by default.
Arguments:
distanceRange (NumberRange, nil)
A marker will be fully opaque when the distance is less than
or equal to the minimum value, and fully transparent when
greater than or equal to the maximum value.
If unspecified, or either limit is less than 0, then fading
will be disabled.
If FadeAngle is also enabled, then the two transparencies will be
multiplied.
MarkerContainer:SetFadeDistanceOrigin(origin)
Sets the location from which FadeDistance is determined.
Arguments:
origin (Vector3, CFrame, BasePart, Model, Camera, nil)
Determines the origin from one of a number of types:
- Vector3: A position in world space.
- CFrame: Uses the CFrame's position in world space.
- BasePart: Uses the Position property.
- Model: Uses the PrimaryPart property. Fading will be
disabled while the PrimaryPart is nil.
- Camera: Uses the Camera's CFrame.
- nil: Uses the value of Workspace.CurrentCamera. Note that
this updates when the CurrentCamera changes.
MarkerContainer:SetFadeAngle()
Actively sets the transparency markers based on the angle (in radians)
between the marker's target and an origin.
FadeAngle is disabled by default.
Arguments:
angleRange (NumberRange, nil)
A marker will be fully opaque when the angle is less than or
equal to the minimum value, and fully transparent when greater
than or equal to the maximum value.
If unspecified, or either limit is less than 0, then fading
will be disabled.
If FadeDistance is also enabled, then the two transparencies
will be multiplied.
MarkerContainer:SetFadeAngleOrigin(origin)
Sets the location from which FadeAngle is determined.
Arguments:
origin (CFrame, BasePart, Model, Camera, nil)
Determines the origin from one of a number of types:
- CFrame: Uses the CFrame's orientation in world space.
- BasePart: Uses the CFrame property.
- Model: Uses the PrimaryPart property. Fading will be
disabled while the PrimaryPart is nil.
- 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 using an initial state.
Arguments:
initialState (table, nil)
Each recognized field in the initial state sets the
corresponding value the marker state. If an initial value is
nil, then the marker field will be set to a default instead.
Returns:
markerState (table)
A table that holds the marker's state. The following fields
are recognized:
Position (Vector3, CFrame, BasePart, 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.
- Model: Uses the PrimaryPart property. The marker will be
hidden while the PrimaryPart is nil.
- nil: Hides the marker.
Defaults to nil.
Body (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.
Arrow (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 the container's
SetArrowsEnabled.
Defaults to nil.
markerStates = MarkerContainer:Markers()
Returns a list of all the marker states in the container.
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.
Arguments:
... (table)
One or more marker states to be removed. Errors 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.
Arguments:
... (table)
One or more marker states to be updated. Errors if any value
is not a known marker state.