Godot Version
4.3
Question
Is there a way to create a callable by defining it inside of a string?
e.g.
#This is a string!
@export var skibidi_callable_string: String = "func(s: String): print("skibidi " + s)"
#This is a callable!
var skibidi_callable: Callable = string_to_callable(skibidi_callable_string)
func _ready() -> void:
skibidi_callable.call("hello world!")
#Should print "skibidi hello world!"
func string_to_callable(string: String) -> Callable:
#???
Is there any “string_to_callable” forbidden function or any way to create it?
Thanks in advance!
No, but you can use call
/callv
/call_deferred
instead.
Inherited By: AudioServer, CameraServer, ClassDB, DisplayServer, EditorFileSystemDirectory, EditorInterface, EditorPaths, EditorSelection, EditorUndoRedoManager, EditorVCSInterface, Engine, EngineD...
Yeah, it’s just easier than what you are doing.
here’s the doc entry .
var string_name_of_function : = "first_bit"+"second_bit"+"third_bit"
var my_function = Callable(node_with_function, string_name_of_function)
my_function.call()
I was thinking about creating a new function only based on a string
However I had an idea:
Creating a new script based on the string so that it has the function defined in the string into itself, then attach it to a ref counted or a resource and call the function from it. I’m gonna try it and see if it works!
It worked!
class FuncHolder:
var ref: RefCounted
var func_script: Script
func _init(new_ref: RefCounted, new_script: Script) -> void:
ref = new_ref
func_script = new_script
var functions: Array[FuncHolder]
func toot() -> void:
print("toot!")
func _ready() -> void:
name = "uvuguggi"
func string_to_callable(func_string: String) -> Callable:
var execute_string_resource := RefCounted.new()
var execute_string_script := GDScript.new()
var new_source_code: String = "var base_node: Node\n"
if not func_string.begins_with("\n"):
new_source_code += "\n"
new_source_code += func_string
execute_string_script.set_source_code(new_source_code)
assert(execute_string_script.reload() == OK)
execute_string_resource = RefCounted.new()
execute_string_resource.set_script(execute_string_script)
execute_string_resource.base_node = self
var func_name = func_string.right(-6).get_slice("(", 0)
var this_func := FuncHolder.new(execute_string_resource, execute_string_script)
functions.append(this_func)
return Callable(execute_string_resource, func_name)
Usage example:
extends Node
class FuncHolder:
var ref: RefCounted
var func_script: Script
func _init(new_ref: RefCounted, new_script: Script) -> void:
ref = new_ref
func_script = new_script
var functions: Array[FuncHolder]
func toot() -> void:
print("toot!")
func _ready() -> void:
name = "uvuguggi"
var skibidi_func_string: String = \
"""
func skibidi_print(what: String) -> void:
print(base_node.name+" printed: skibidi "+what)
"""
var skibidi_callable: Callable = string_to_callable(skibidi_func_string)
skibidi_callable.call("hello world")
skibidi_callable.call("callable")
var tooting_func_string: String = \
"""
func toot_base_node() -> void:
base_node.toot()
"""
var tooting_callable: Callable = string_to_callable(tooting_func_string)
tooting_callable.call()
func string_to_callable(func_string: String) -> Callable:
var execute_string_resource := RefCounted.new()
var execute_string_script := GDScript.new()
var new_source_code: String = "var base_node: Node\n"
if not func_string.begins_with("\n"):
new_source_code += "\n"
new_source_code += func_string
execute_string_script.set_source_code(new_source_code)
assert(execute_string_script.reload() == OK)
execute_string_resource = RefCounted.new()
execute_string_resource.set_script(execute_string_script)
execute_string_resource.base_node = self
var func_name = func_string.right(-6).get_slice("(", 0)
var this_func := FuncHolder.new(execute_string_resource, execute_string_script)
functions.append(this_func)
return Callable(execute_string_resource, func_name)
prints:
uvuguggi printed: skibidi hello world
uvuguggi printed: skibidi callable
toot!
It might be best used added to an autoload script
You should however change the “base_node” property of the ref
like this:
extends Node
class FuncHolder:
var ref: RefCounted
var func_script: Script
func _init(new_ref: RefCounted, new_script: Script) -> void:
ref = new_ref
func_script = new_script
var functions: Array[FuncHolder]
func string_to_callable(func_string: String, base_node: Node = null) -> Callable:
var execute_string_resource := RefCounted.new()
var execute_string_script := GDScript.new()
var new_source_code: String = ""
if base_node:
new_source_code = "var base_node: Node\n"
new_source_code += func_string
new_source_code += func_string
execute_string_script.set_source_code(new_source_code)
assert(execute_string_script.reload() == OK)
execute_string_resource = RefCounted.new()
execute_string_resource.set_script(execute_string_script)
if base_node:
execute_string_resource.base_node = self
var func_name = func_string.get_slice(" ", 1).get_slice("(", 0)
var this_func := FuncHolder.new(execute_string_resource, execute_string_script)
functions.append(this_func)
return Callable(execute_string_resource, func_name)