[4.6.2] How do I modify font, size, position, etc of objects via GDScript

Godot Version

4.6.2, 2D

Question

Hello! I’m currently trying to make a dating sim that builds everything from .txt files. Part of that is creating, modifying, deloading, and deleting completely new objects without ever making a new scene or even opening GDScript. I’ve attach the full restrictions below.

The restrictions I am abiding by. Not fully relevant though...
  • Must be Godot (obviously)
  • Must be able to make an entire “scene” without entering Godot
  • Must be able to read from easy to access .txt files
  • Cannot have more then one Godot scene
  • Cannot have any other objects besides Node2D to start with
  • Cannot modify anything from withing Godot (besides the code, of course)

It’s these last two that are proving the most troublesome, and have put me with the most research.

However…I can’t really find good documentation. Or it might be that I’m just bad at interpreting it. Either way, I do need help with figuring out how to set the (minimum) size, the position, and the font/font color, with the font/font color on a case by case basis instead of globally. I also want to apply the methods to other objects besides whats implemented currently (like spin boxes, input boxes, text boxes, standalone pictures, animations, etc).
(you might see the method and/or property “custom_minimum_size”. I…never really figured out how to use it)

Current code
extends Node2D

var HeldFile
var ActiveInstances = []

func _ready() -> void:
	Manage("res://OnStart.txt")

func Manage(File):
	if FileAccess.open(File, FileAccess.READ) != null:
		HeldFile = (FileAccess.open(File, FileAccess.READ).get_as_text()).split("\n")
		HeldFile.append("0")
		print(HeldFile)
	elif File != "0" and File != "1":
		print("FileErr : " + File + " : File does not exist or did not have a valid output")
		return("Failed")
	elif File == "1":
		for ii in len(ActiveInstances):
			ActiveInstances[ii].queue_free()
		ActiveInstances = []
	var i = 0
	while int(HeldFile[-1]) < len(HeldFile) and (HeldFile[0] != "//" or HeldFile[1] != "//"):
		var Local = (HeldFile[int(HeldFile[-1])].split(HeldFile[0]))
		if Local[0].to_lower() == "button" and len(Local) == 10:
			print("Command:Button")
			ActiveInstances.append(Button.new())
			add_child(ActiveInstances[i])
			ActiveInstances[i].text = Local[6]
			#ActiveInstances[i].custom_minimum_size(Local[1], Local[2])
			#if Local[7] != "nill" and len(Local[7].split(", ")) == 4:
			#	var LColor = Local[7].split(", ")
			#	ActiveInstances[i].font_color(int(LColor[0]), int(LColor[1]), int(LColor[2]), int(LColor[3]))
			i += 1
		if Local[0].to_lower() == "textbox" and (len(Local) == 1 or len(Local) == 2):
			print("Command:TextBox")
		HeldFile[-1] = str(int(HeldFile[-1]) + 1)
		print(Local)

What’s the reason for those restrictions? Is this some kind of challenge?

I would recommend to use static typing, so it’ll be easier to see the available properties and functions for your objects. For example, assign the new Button to a typed variable and modify it through this.

			var button := Button.new()
			
			button.text = Local[6]

Doing it this way, Godot will tell you that custom_minimum_size is a Vector2 property and not a function when you were treating it as a function.

The array is there as I need to expand and deload objects on a whim as much as I want, as per the restrictions. The restrictions are part challenge, part future proofing, part needed for my project. If I can help it, I don’t want to have a cap on the number of objects by assigning a variable.

The custom minimum size is like that because:

var abc = Vector2i(Local[1], Local[2])
ActiveInstances[i].custom_minimum_size = abc

didn’t work. It was more my fiddling and less that i thought it to be a method.

You can still add it to the array. If the variable is declared local inside the while-loop, it won’t restrict the amount of instantiated objects - you just use it to initiliaze the button.

Did you get any error message from that previous attempt? In what way did it not work?

“Invalid call. Nonexistent ‘Vector2i’ constructor.”

Static typing would help with this as well. Local is a PackedStringArray, all its values are strings. You need to convert them to int.

var abc := Vector2i( Local[1].to_int(), Local[2].to_int() )

mb I straight up forgot to lmao. Give me a second to test some stuff and I’ll get back to you.

That solves my size and position issue, remembering to convert to int. Is there any way to set font, font size, and font color individually? I didn’t get anywhere with ActiveInstances[i].font_size = int(Local[5]).

I think they are part of the theme? So you would have to override the theme, using the add_theme...override() functions from Control.

ActiveInstances[i].add_theme_font_size_override( "font_size", int(Local[5]) )

That worked out for font size, but it doesn’t seem to translate to font color. Do you know if there is documentation on this command I can look over? I couldn’t find any.

There are six different functions, all part of the Control class.

That seems to have most of the commands I’ll need. Thank you!

Can you show what would one of theses .txt files would look like?

Give me some time to get on my computer. I’ll give you my updated code and the truncated (to remove pointless lines) .txt file

you don’t need to send it anymore, that message was meant to be sent way earlier, but i had to wait for a mod to approve it so it only got sent after the solution was posted :confused:

That is, admittedly, quite funny. I’m already here though, so here ye go:

Code
extends Node2D

var HeldFile: PackedStringArray
var ActiveInstances: Array = []

func _ready() -> void:
	Manage("res://OnStart.txt")

func Manage(File):
	if FileAccess.open(File, FileAccess.READ) != null:
		HeldFile = (FileAccess.open(File, FileAccess.READ).get_as_text()).split("\n")
		HeldFile.append("0")
		print(HeldFile)
	elif File != "0" and File != "1":
		if File == "res://OnStart.txt":
			OS.alert(File + " has failed to load. The game can't load anything without " + File + ". \n\nPlease refer to HELP.txt and fix the issue.")
			get_tree().quit()
		else:
			OS.alert(File + " has failed to load. Ignoring scene change...")
		return("Failed")
	elif File == "1":
		for ii in len(ActiveInstances):
			ActiveInstances[ii].queue_free()
		ActiveInstances.clear()
	var i = 0
	while int(HeldFile[-1]) < len(HeldFile) and (HeldFile[0] != "//" or HeldFile[1] != "//"):
		var Local = (HeldFile[int(HeldFile[-1])].split(HeldFile[0]))
		if Local[0].to_lower() == "button" and len(Local) == 10:
			print("Command:Button")
			ActiveInstances.append(Button.new())
			add_child(ActiveInstances[i])
			if Local[6] != "nill":
				ActiveInstances[i].text = Local[6]
			ActiveInstances[i].position = Vector2i(int(Local[3]), int(Local[4]))
			ActiveInstances[i].custom_minimum_size = Vector2i(int(Local[1]), int(Local[2]))
			if Local[5] != "nill":
				ActiveInstances[i].add_theme_font_size_override("font_size",int(Local[5]))
			if Local[7] != "nill":
				var LColor = Local[7].split(", ")
				ActiveInstances[i].add_theme_color_override("font_color", Color(int(LColor[0]), int(LColor[1]), int(LColor[2])))
			#if Local[8] != "nill":
			#	ActiveInstances[i].add_theme_font_override("rp_font", Object.new(Local[8]))
			i += 1
		if Local[0].to_lower() == "textbox" and (len(Local) == 1 or len(Local) == 2):
			print("Command:TextBox")
		HeldFile[-1] = str(int(HeldFile[-1]) + 1)
		print(Local)
res://OnStart.txt (truncated)

;
Button; 100; 100; 200; 200; 50; hewo; 0, 0, 1; DemoType.otf; nill

Result