Drawing circles in 2D space using 3D objects with Camera.unproject_position weirdness.

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By directive0

I have a game with a solar system. It has planets rotating around the sun.

I’d really like to have the orbit of whatever target is selected to be defined by a 2d circle that is drawn in realtime.

So far I am able to get the coordinates of my target object (lets say the earth) and the coordinates of the center of my solar system and I feed those into a circle drawing function that someone offered in a post here to write a function that draws the circle.

So great. Now I can draw a circle. Only problem is when I try to draw them and I zoom in and out I get this:

Here’s my code:

extends Node2D

var camera
var radius
var center
# class member variables go here, for example:
# var a = 2
# var b = "textvar"

func _ready():
	# get my camera
	camera = get_tree().get_root().get_node("Node").get_node("Camera")

	# get the target to be the radius of the circle
	var target = get_parent().get_node("target_reticule").get_position()
	
	radius = target
	
	# this is where the center of my solar system is.
	center = Vector3(0,-100,0)



 
func _draw():
	draw_orbit()


func draw_orbit():
	
	# pointless shuffling.
	var radius2d = radius
	
	# get the location onscreen of the object I want to trace an orbit for.
	var center2d = camera.unproject_position(center)

	draw_empty_circle(center2d,radius2d,Color(1,1,1,1),1,3)

	


func draw_empty_circle(circle_center, circle_radius, color, resolution, thick):
	var draw_counter = 1
	var line_origin = Vector2()
	var line_end = Vector2()
	line_origin = circle_radius + circle_center

	while draw_counter <= 360:
		line_end = circle_radius.rotated(deg2rad(draw_counter)) + circle_center
		draw_line(line_origin, line_end, color, thick)
		draw_counter += 1 / resolution
		line_origin = line_end

	line_end = circle_radius.rotated(deg2rad(360)) + circle_center
	draw_line(line_origin, line_end, color, thick)


func _process(delta):
	#update values every cycle.
	var target = get_parent().get_node("target_reticule").get_position()
	radius = target
	update()

    

Can anyone tell me what the heck I’m doing wrong?

Sooo. Why are using a 3D camera in a 2D scene? I can’t really watch video since I’m on mobile data so I can’t tell.

edit: nevermind I can watch it. But still. Why does it need to be both 2D and 3D?

SIsilicon | 2018-05-24 18:23

The scene is 3d only overlay elements like reticules, control panels and indicators are 2d. The reason for this is so I can make 3d assets (far easier to make for me) and take advantage of 3d dynamic lighting.

I want to draw a circle over a 3d scene that is representative of the position of 3d objects in 3d space. It would be kind of unwieldy to use a 3d mesh to represent this so I am opting to use a 2d circle instead.

If your suggestion is to just use 2d that’s fine, and I am considering it,but I can’t believe the behaviour I’m experiencing fromy set up is expected so I am asking if anyone can offer guidance as to why this is happening and help me fix it.

directive0 | 2018-05-24 22:16

:bust_in_silhouette: Reply From: SIsilicon

Well I honestly it would be just a easy to make a 3D mesh using ImmediateGeometry.
Your code for ‘redrawing’ your immediate geometry would look something like this.

func draw_empty_circle(circle_center, circle_radius):
    var UP = Vector3(0,1,0)

    clear()
    begin(Mesh.PRIMITIVE_LINE_LOOP)
    for i in range(int(resolution)):
        var rotation = float(i) / resolution * TAU
        add_vertex(rotated(UP, circle_radius-circle_center) + circle_center)
    end()

Note: The UP vector is the direction the circle will be facing. So if its not pointing the right way, then change it.

You’ll notice that there is no thickness or color parameter. That’s because the geometry’s material holds them. Specifically the spatialmaterial. line_width and albedo. Just make sure to make unshaded true.

Thats really cool! I will probably just adopt that.

I’d love to know why my solution doesn’t work though, so I could stop labouring under a misconception.

directive0 | 2018-05-25 19:10

Just wanted to say thanks. Had to modify your example to get it to work but at least its working now. Thanks again for all your help.

directive0 | 2018-05-29 13:12

Vote n’ Select :wink:

SIsilicon | 2018-05-29 14:29

Do you know a way to draw filled circles?

sairam123 | 2021-07-21 13:29

:bust_in_silhouette: Reply From: EGodAmonRa

Here is a C# cut of my Circle draw method. Tested with GD 3.3

public void DrawCircle(ImmediateGeometry immediateGeometry, float radius = 5.0f)
{
    //How many edges the circle will have. Anything over 16 looks okay, pref 32.
    int resolution = 32;

    immediateGeometry.Clear();

    immediateGeometry.Begin(Mesh.PrimitiveType.LineLoop);

    immediateGeometry.SetNormal(new Vector3(0, 1, 0));
    immediateGeometry.SetUv(new Vector2(1, 1));

    //You need to enable using vertex color as an albedo in attached material.
    //ImmediateGeometry: Geometry Instance - Geometry - Material Override - Spatial Material - Vertex Color - Use As Albedo.
    //Also you can change the line width my modifiying the SpatialMaterial under: Parameters - Line Width  (NOT Tested )
    //immediateGeometry.SetColor(new Color(1, 0, 0, 1));

    Vector3 position = new Vector3(0.0f, 0.0f, radius);

    for (int i = 0; i < resolution; i++)
    {
        float rotation = (float)i / resolution * Mathf.Tau;
        immediateGeometry.AddVertex(position.Rotated(Vector3.Up, rotation));
    }

    immediateGeometry.End();
}