Visualising an orbit

I’m trying to visually portray an orbit in 3D, i.e. an ellipse. The ellipse is generated based on a given semi-major axis and eccentricity. The actual ellipse itself is 2D (ignoring orbital inclination of any sort, so everything is on the exact same plane):

function Ellipse.new(semiMajorAxis, eccentricity)
	local self = {}
	setmetatable(self, Ellipse)
	-- TODO: translation
	self.a = semiMajorAxis
	self.majorAxis = 2 * self.a
	
	-- eccentricity e = sqrt(1 - (b/a)^2) 
	-- circle: e = 0; ellipse: 0 < e < 1; parbola: e = 1
	self.e = eccentricity
	self.b = self.a * math.sqrt(1- self.e^2)
	self.minorAxis = 2 * self.b
	
	return self
end

Is there any good way to portray an ellipse like this in 3D, or a custom shape in general?

What I’ve tried:

  • Creating a bunch of parts for as many as 720 points in the ellipse. This is performance heavy, looks alright. It could maybe be unioned, but union API seems unequipped to handle something like this.
  • Creating a bunch of ScreenGui frames (just black backgrounds or ‘pixels’, about 5x1px). Every frame corresponds to a certain point on the orbit. Each RenderStepped, every frame has its 2D position updated, according to the 3D orbit position (using Camera.WorldToViewportPoint). This is really performance heavy, and looks ugly because I haven’t accounted for depth.

What I’ve thought about:

  • Creating an “ellipse” mesh for a number of specific orbits. This is limited because every mesh has to correspond to a specific semi-major axis and eccentricity, which means I can only portray certain, pre-determined “on-rails” orbits.

Is this just a limitation of the engine? Surely there’s some way to graphically portray a shape?
If only Roblox had LineRenderer

1 Like

I’m pretty sure if you anchor the parts, 700 shouldn’t be that bad. There are games with thousands of anchored parts that run just fine.

I think a mesh is unrealistic if you are trying to make custom ellipses. You might be able to do something with GUI stretching but that sounds a little wonky and unpredictable. Then again, the eccentricity is pretty much a “stretch” factor, so I don’t see why it couldn’t necessarily work.

Also don’t use RenderStepped, use Heartbeat or Stepped. RenderStepped is supposed to be for user input or camera maneuvers, since it runs things before everything else (inputs and camera thus are supposed to be more responsive). Creating bricks there likely won’t do anything useful for you, and is probably why you are having any sort of performance issues.

Hope that helps

The further thing about parts is that rendering is really weird for an ellipse. Creating a bunch of parts that just draw “lines” from one point in the ellipse to the next, in the far off distance but also at sporadic distances there’s quite a number of parts that aren’t rendered (max settings). This is worrying:

image

Have you considered using Beams between the two furthest points? It would create a smooth line between the points that you could manipulate the width of for the orbit.

2 Likes

Any idea why the beams aren’t curved? The attachments are in a straight line, but there should still be a curve as per CurveSize and this image from the wiki?

image
image

function Ellipse.new(semiMajorAxis, eccentricity)
	local self = {}
	setmetatable(self, Ellipse)
	-- TODO: self.origin (translation)
	self.origin = originPos
	
	self.a = semiMajorAxis
	self.majorAxis = 2 * self.a
	
	-- eccentricity e = sqrt(1 - (b/a)^2) 
	-- circle: e = 0; ellipse: 0 < e < 1; parbola: e = 1
	self.e = eccentricity
	self.b = self.a * math.sqrt(1- self.e^2)
	
	self.minorAxis = 2 * self.b
	
	return self
end

function Ellipse:visualise()
	local container_part = Instance.new("Part") 
	container_part.Anchored = true
	container_part.CanCollide = false
	container_part.Transparency = 1
	container_part.Position = self.origin
	container_part.Parent = workspace
	
	local attachmentNegativeA = Instance.new("Attachment")
	attachmentNegativeA.WorldPosition = Vector3.new(-self.a, 0, 0)
	attachmentNegativeA.Parent = container_part
	
	local attachmentPositiveA = Instance.new("Attachment")
	attachmentPositiveA.WorldPosition = Vector3.new(self.a, 0, 0)
	attachmentPositiveA.Parent = container_part
	
	local beam1 = Instance.new("Beam")
	beam1.Attachment0 = attachmentNegativeA
	beam1.Attachment1 = attachmentPositiveA
	beam1.CurveSize0 = self.b
	beam1.CurveSize1 = self.b
	beam1.Segments = 100
	beam1.Parent = container_part
	
	local beam2 = Instance.new("Beam")
	beam2.Attachment1 = attachmentNegativeA
	beam2.Attachment0 = attachmentPositiveA
	beam2.CurveSize0 = -self.b
	beam2.CurveSize1 = -self.b
	beam2.Segments = 100
	beam2.Parent = container_part
end

move the attachments around and try changing their axis. It might be on the wrong one, it flattens the curve.

A beam’s segment count is affected by graphics setting. If your graphics is low, all beams will only have 1 segment each, meaning they can’t be curved. It makes beams a lot less useful but that just how it be

If you open Studio settings and set EditQualityLevel(?) to 21, you should see the beam curve as expected.

I found this webpage a few months ago when I was trying to do the exact same thing:

http://www.spaceroots.org/documents/ellipse/node22.html

You need the attachments facing the right way and that page should provide the multipliers needed for the CurveSize properties in each of the 4 segments.

1 Like

That sucks, but my EditQuality is 21. I can see curves if I randomly move the attachments around, but that’s not useful :joy:

Are the attachments rotated like in the diagram?

Looks like “facing the right way” was the problem, though I’m not sure why this is a thing.

Fix: I just made the 2 attachments face eachother. lol.

	local attachmentNegativeA = Instance.new("Attachment")
	attachmentNegativeA.CFrame = CFrame.new(Vector3.new(-self.a, self.origin.Y, 0), Vector3.new(self.a, self.origin.y, 0))
	attachmentNegativeA.Parent = container_part
	
	local attachmentPositiveA = Instance.new("Attachment")
	attachmentPositiveA.CFrame = CFrame.new(Vector3.new(self.a, self.origin.Y, 0), Vector3.new(-self.a, self.origin.y, 0))
	attachmentPositiveA.Parent = container_part

Thanks for the link too!

1 Like

Forgot to check that site didn’t really provide the values but I think this does:

For an ellipse you would replace R with your values for the semimajor and semimonir axes, obviously.

Oops, that’s a really good point, I just put CurveSize as the minor axis without really thinking.

Did you run into any problems implementing orbit visualization? The edit quality thing concerns me

I eventually decided not to use beams and just draw the ellipse using 72 attachments at 5 degree parametric intervals, and connect them with RodConstraints. The script I’m working on also had to calculate hyperbolic trajectories and I got too lazy to figure out how to draw those using beams. I can upload those scripts for you if you’d like.

How can you use RodConstraints for drawing?

If you’re happy to upload the scripts, that would definitely be appreciated. :slight_smile: I’m really happy that this is quite achievable, I had no idea Beams really existed.

The rod constraints basically draw ‘lines’ between each node, so it looks alright on small scales (which is what I use them for), though if you want it to appear smoother you would have to edit the scripts to create more attachments at smaller intervals. Mine uses 5 degree intervals so it only requires 72 attachments, but it suits my needs. Having lots of attachments doesn’t really seem to slow down the game for me unless they get into the thousands.

2 Likes

If you need help with the really confusing math behind orbital motion as well:

3 Likes