I'm trying to create and set a new script to an object during runtime
and so far this is what I've come up with:
extends Node2D
func _ready():
var object=$Obj # type KinematicBody2D
var code_path="res://code.gd"
var f=File.new()
f.open(code_path, File.READ)
var new_script=GDScript.new()
new_script.source_code=f.get_as_text()
new_script.resource_path="user://new_script.gd"
ResourceSaver.save(new_script.resource_path, new_script)
new_script.reload()
f.close()
object.set_script(new_script)
object._ready()
This works perfectly when the scene is played from the game engine but as soon as I export it and run the executable it doesn't seem to work and gives the following error in the terminal:
Godot Engine v3.5.stable.mono.official.991bb6ac7 - https://godotengine.org libGL error: glx: failed to create dri3 screen libGL error: failed to load driver: nouveau OpenGL ES 3.0 Renderer: RENOIR (renoir, LLVM 15.0.7, DRM 3.47, 5.19.0-45-generic) Async. shader compilation: OFF Mono: Log file is: '/home/mm/.local/share/godot/app_userdata/Testing/mono/mono_logs/2023-06-25_12.39.43_14788.log' ERROR: File must be opened before use. at: get_as_text (core/bind/core_bind.cpp:2092) ERROR: Script inherits from native type 'Reference', so it can't be instanced in object of type 'KinematicBody2D'. at: instance_create (modules/gdscript/gdscript.cpp:312)
Is there any solution for this? or is it simply not possible in export?
(currently using -v3.5, would something like this be possible in 4.x?)
String
literalOne solution is to inline the text. You can use multi-line string literals:
var like_this := """
LOOK
A
STRING
WITH
MULTIPLE
LINES
"""
.gd
Godot 3 didn't scrap unused scripts by default (the setting is in the Resources tab of the Export Preset).
On export the scripts would be compiled to bytecode by default (the setting is under the Script tab of the Export Preset), you can select Text. And yes, that would be for all scripts, there is no way to set it one by one.
Aside from that, the script might be failing to import for some other reason. For example, it might have parsing errors... Which might make sense if it is is intended as a template. In my case, for templates I ended up using String
literals.
Also, assuming everything is OK with the script, you should be able to load it as a GDScript
directly (e.g. with load
) and use it.
.txt
I believe the issue is that the .txt
file is not being kept in the exported game.
This was indeed an issue in Godot 3. You were supposed to tell Godot in the export setting to keep the file. Which never worked for me.
It was supposedly fixed in Godot 3.3... You are supposed to select the file in the file system, and then import it as "Keep File (No Import)". And it was indeed fixed for .csv
, but not for .txt
because .txt
files do not show up in the file system to begin with, because they are not a recognized Resource
type. No, Godot won't recognize a .txt
as a TextFile
, and I have no idea if that is possible.
No? You want .txt
? OK, we are going to make Godot recognize it.
So, define a custom resource type:
class_name TXTResource
extends Resource
export(String, MULTILINE) var text:String
And we need a custom loader:
tool
class_name TXTLoader
extends ResourceFormatLoader
func get_recognized_extensions() -> PoolStringArray:
return PoolStringArray(["txt"])
func get_resource_type(_path:String) -> String:
return "Resource"
func handles_type(typename: String) -> bool:
return typename == "Resource"
func load(path: String, _original_path: String):
var file := File.new()
var err := file.open(path, File.READ)
if err != OK:
return err
var result = TXTResource.new()
result.text = file.get_as_text()
file.close()
return result
Place them anywhere in your project (they don't need to be in the addons folder).
With that, the .txt
file should be imported as a Resource
correctly.
Addendumm: Depending on you use them, you might also have to implement an EditorImportPlugin
. The purpose of the EditorImportPlugin
is to read the resource file and save in it as a format Godot understands natively (so instead of creating a resource object, it save a resource file in the .import
folder in Godot 3 or the .godot/imported
folder in Godot 4).
Ah, but it does not open in the code editor! I hear you. TextFile
is supposed to do that, but there is no way to use it. I'm not sure if there is a way to hack it, but I don't think it is worth the hassle if there is.