Trick: Make a blender like radial(pie) 3D space navigation menu for godot editor

Yo,

So, I really like this feature of blender that lets you navigate in 3D space. When you press KEY_QUOTELEFT it shows a radial(pie) menu that lets you view 3D space from different angles. So, I decided to create this feature for godot.

Turns out godot’s “EditorPlugin” is a really awesome feature.

Demonstration gif :
godot-pie-gif

Code:

@tool
extends EditorPlugin

var viewport3d: SubViewport
var is_pie_menu_key_pressed: bool
var is_skip_input: bool = false
var mouse_pressed_pos: Vector2
var active_main_screen: String
var pie_ui: Sprite2D

func _enter_tree() -> void:
	self.main_screen_changed.connect(on_change_test)
	viewport3d = get_editor_interface().get_editor_viewport_3d()
	is_pie_menu_key_pressed = false
	
	pie_ui = load("res://pie_ui.tscn").instantiate()
	pie_ui.visible = false
	viewport3d.get_parent().add_child(pie_ui)

func _unhandled_input(event: InputEvent) -> void:
	if !(event is InputEventKey):
		return
	
	if event.keycode != KEY_QUOTELEFT:
		return
	
	if event.is_pressed():
		if !is_pie_menu_key_pressed:
			is_pie_menu_key_pressed = true
			
			mouse_pressed_pos = viewport3d.get_mouse_position()
			if (
				mouse_pressed_pos.x > 0 and mouse_pressed_pos.y > 0 and 
				mouse_pressed_pos.x < viewport3d.size.x and mouse_pressed_pos.y < viewport3d.size.y and 
				active_main_screen == "3D"
			):
				pie_ui.visible = true
				
				pie_ui.position.x = clamp(viewport3d.get_mouse_position().x, 200, viewport3d.size.x - 200)
				pie_ui.position.y = clamp(viewport3d.get_mouse_position().y, 200, viewport3d.size.y - 200)
				
				is_skip_input = false
			else:
				is_skip_input = true
				return
	
	elif event.is_released():
		if is_skip_input:
			is_pie_menu_key_pressed = false
			return
		
		pie_ui.visible = false
		var mouse_released_pos = viewport3d.get_mouse_position()
		
		var diff_angle_deg = rad_to_deg((mouse_released_pos - pie_ui.position).angle())
		diff_angle_deg += 180
		
		diff_angle_deg += 22.5
		if diff_angle_deg > 360:
			diff_angle_deg -= 360
		
		
		var angle_start = floor(diff_angle_deg / 45)
		var test_event = InputEventKey.new()
		test_event.pressed = true
		
		match int(angle_start):
			0:
				test_event.keycode = KEY_KP_3
				test_event.alt_pressed = true
				Input.parse_input_event(test_event)
				#print("LEFT VIEW")
			1:
				test_event.keycode = KEY_KP_1
				Input.parse_input_event(test_event)
				#print("FRONT VIEW")
			2:
				test_event.keycode = KEY_KP_7
				Input.parse_input_event(test_event)
				#print("TOP VIEW")
			3:
				test_event.keycode = KEY_KP_1
				test_event.alt_pressed = true
				Input.parse_input_event(test_event)
				#print("BACK VIEW")
			4:
				test_event.keycode = KEY_KP_3
				Input.parse_input_event(test_event)
				#print("RIGHT VIEW")
			5:
				test_event.keycode = KEY_F
				Input.parse_input_event(test_event)
				#print("VIEW SELECTED")
			6:
				test_event.keycode = KEY_KP_7
				test_event.alt_pressed = true
				Input.parse_input_event(test_event)
				#print("BOTTOM VIEW")
			7:
				test_event.keycode = KEY_P
				test_event.ctrl_pressed = true
				Input.parse_input_event(test_event)
				#print("CAMERA VIEW")
			_:
				print(angle_start)
				printerr("BAD MATCH CASE")
		
		is_pie_menu_key_pressed = false

func _exit_tree() -> void:
	pass

func on_change_test(screen_name : String):
	active_main_screen = screen_name

Image overlay that I used for UI
Screenshot_20240706_094454

3 Likes

I followed this tutorial for plugin basics.

UI just sprite with image texture. Code is more of a hack approach than proper implementation. Feel free to ask any questions.

2 Likes