GDQuest / godot-steering-ai-framework

A complete framework for Godot to create beautiful and complex AI motion. Works both in 2D and in 3D.

Home Page:http://gdquest.com/docs/godot-steering-ai-framework

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Automatically build a GUI by looking at the exported variables and their types

Razoric480 opened this issue · comments

While the Demo Selector is useful, one downside is that you can't change values quite as easily; needing to go into the Remote Tree to do so. So it's nice for showcase, but for actual study and playing around with the variables, opening the actual demo will still be called for.

Occasionally, Godot's keep-window-on-top option also stops working for unexplained reasons, depending on the state of your alt-tabbing and clicking, so using the inspector for all tweaking starts to lose some of its lustre.

An in-demo GUI system that we don't have to manually build but that automatically outputs the exported variables that are on the root node would alleviate all of those issues.

I don't know if it's possible to get the exported variables specifically - well, with the language server's json export it is, but that could be a little heavy to probe.

In any case, that'd be a good general debug tool, so we could make it part of another repo where we have debug and/or UI components

If we use the language server, we'd have to grab the data before running, because the language server is only available in Editor as a tool script. With that in mind, then I can see an EditorScript that generates/updates a TSCN file.

If we want to go on-the-fly, then we'd probably stick with parsing the script as a text document, and regex-match variables based on some parameters of our choosing.

But yes, it should be its own project.

On my own time/for fun, was messing around and wrote the following:

tool
extends EditorScript


export(int, 0, 200, 5) var range_test: int = 50
export var standard_value_test := 20.0
export var variant_test = ""
export(ImageTexture) var resource_test


func _run() -> void:
	var workspace = (
			Engine.get_singleton('GDScriptLanguageProtocol').get_workspace()
	)
	workspace.parse_local_script("res://SymbolTest.gd")
	var api: Dictionary = workspace.generate_script_api("res://SymbolTest.gd")
	var file := File.new()
	file.open("res://SymbolTest.gd", File.READ)
	var content := file.get_as_text()
	file.close()
	
	var values := []
	
	for m in api.members:
		if m.export:
			var var_name: String = m.name
			var var_value: String = str(m.default_value) if m.default_value else ""
			var type: String = m.data_type
			var export_line := ""
			
			var regex := RegEx.new()
			regex.compile("export[ \t]*?\\((.*?)\\)[ \t]*?var " + var_name)
			var line := regex.search(content)
			var has_hints := not line == null
			
			if has_hints:
				var export_data: PoolStringArray = line.strings.slice(1, line.strings.size()-1)
				export_line = export_data.join(", ")
			values.append({name = var_name, value = var_value, controls = has_hints})

	var scene := _build_scene(values)
	ResourceSaver.save("res://SymbolTestGUI.tscn", scene)


func _build_scene(values: Array) -> PackedScene:
	var node := MarginContainer.new()
	node.set("custom_constants/margin_top", 20)
	node.set("custom_constants/margin_left", 20)
	node.set("custom_constants/margin_bottom", 20)
	node.set("custom_constants/margin_right", 20)

	var vbox := VBoxContainer.new()
	
	node.add_child(vbox)
	vbox.owner = node
	
	for v in values:
		var name: String = v.name
		var value: String = v.value
		var controls: bool = v.controls
		
		var name_label := Label.new()
		name_label.text = name + (" = " if not value.empty() else "")
		
		var value_label := Label.new()
		value_label.text = value
		
		var hbox := HBoxContainer.new()
		hbox.add_child(name_label)
		hbox.add_child(value_label)
		
		var controls_node: Label
		
		if controls:
			controls_node = Label.new()
			controls_node.text = "Slider/etc goes here"
			hbox.add_child(controls_node)
		
		vbox.add_child(hbox)
		
		name_label.owner = node
		value_label.owner = node
		hbox.owner = node
		if controls_node:
			controls_node.owner = node
	
	var scene := PackedScene.new()
	scene.pack(node)
	
	return scene

As a show of idea.

You can get the object variables at runtime with Object.get_property_list() and then filter those. This might work:

func _ready():
	var list = get_property_list()
	for v in list:
		if v.usage & PROPERTY_USAGE_SCRIPT_VARIABLE and v.usage & PROPERTY_USAGE_EDITOR:
			print(v)