|
| 1 | +.. _doc_runtime_loading_and_saving: |
| 2 | + |
| 3 | +Runtime file loading and saving |
| 4 | +=============================== |
| 5 | + |
| 6 | +.. seealso:: |
| 7 | + |
| 8 | + See :ref:`doc_saving_games` for information on saving and loading game progression. |
| 9 | + |
| 10 | +Sometimes, :ref:`doc_exporting_pcks` is not ideal when you want players to be |
| 11 | +able to load user-generated content in your project. It requires users to |
| 12 | +generate a PCK or ZIP file through the Godot editor, which contains resources |
| 13 | +imported by Godot. |
| 14 | + |
| 15 | +Example use cases for runtime file loading and saving include: |
| 16 | + |
| 17 | +- Loading texture packs designed for the game. |
| 18 | +- Loading user-provided audio tracks and playing them back in an in-game radio station. |
| 19 | +- Loading custom levels or 3D models that can be designed with any 3D DCC that |
| 20 | + can export to glTF (including glTF scenes saved by Godot at runtime). |
| 21 | +- Using user-provided fonts for menus and HUD. |
| 22 | +- Saving/loading a file format that can contain multiple files but can still |
| 23 | + easily be read by other applications (ZIP). |
| 24 | +- Loading files created by another game or program, or even game data files from |
| 25 | + another game not made with Godot. |
| 26 | + |
| 27 | +.. warning:: |
| 28 | + |
| 29 | + Do **not** use this runtime loading approach to load resources that are part |
| 30 | + of the project, as it's less efficient and doesn't allow benefiting from |
| 31 | + Godot's resource handling functionality (such as translation remaps). See |
| 32 | + :ref:`doc_import_process` for details. |
| 33 | + |
| 34 | +.. seealso:: |
| 35 | + |
| 36 | + You can see how saving and loading works in action using the |
| 37 | + `Run-time File Saving and Loading (Serialization) demo project <https://github.com/godotengine/godot-demo-projects/blob/master/loading/runtime_save_load>`__. |
| 38 | + |
| 39 | +Plain text and binary files |
| 40 | +--------------------------- |
| 41 | + |
| 42 | +Godot's :ref:`class_FileAccess` class provides methods to access files on the |
| 43 | +filesystem for reading and writing: |
| 44 | + |
| 45 | +:: |
| 46 | + |
| 47 | + func save_file(content): |
| 48 | + var file = FileAccess.open("/path/to/file.txt", FileAccess.WRITE) |
| 49 | + file.store_string(content) |
| 50 | + |
| 51 | + func load_file(): |
| 52 | + var file = FileAccess.open("/path/to/file.txt", FileAccess.READ) |
| 53 | + var content = file.get_as_text() |
| 54 | + return content |
| 55 | + |
| 56 | +To handle custom binary formats (such as loading file formats not supported by |
| 57 | +Godot), :ref:`class_FileAccess` provides several methods to read/write integers, |
| 58 | +floats, strings and more. These FileAccess methods have names that start with |
| 59 | +``get_`` and ``store_``. |
| 60 | + |
| 61 | +If you need more control over reading binary files or need to read binary |
| 62 | +streams that are not part of a file, :ref:`class_PackedByteArray` provides |
| 63 | +several helper methods to decode/encode series of bytes to integers, floats, |
| 64 | +strings and more. These PackedByteArray methods have names that start with |
| 65 | +``decode_`` and ``encode_``. See also :ref:`doc_binary_serialization_api`. |
| 66 | + |
| 67 | +Images |
| 68 | +------ |
| 69 | + |
| 70 | +Image's :ref:`load_from_file<class_Image_method_load_from_file>` static method |
| 71 | +handles everything, from format detection based on file extension to reading the |
| 72 | +file from disk. |
| 73 | + |
| 74 | +If you need error handling or more control (such as changing the scale a SVG is |
| 75 | +loaded at), use one of the following methods depending on the file format: |
| 76 | + |
| 77 | +- :ref:`load_jpg_from_buffer<class_Image_method_load_jpg_from_buffer>` |
| 78 | +- :ref:`load_ktx_from_buffer<class_Image_method_load_ktx_from_buffer>` |
| 79 | +- :ref:`load_png_from_buffer<class_Image_method_load_png_from_buffer>` |
| 80 | +- :ref:`load_svg_from_buffer<class_Image_method_load_svg_from_buffer>` |
| 81 | + or :ref:`load_svg_from_string<class_Image_method_load_svg_from_string>` |
| 82 | +- :ref:`load_tga_from_buffer<class_Image_method_load_tga_from_buffer>` |
| 83 | +- :ref:`load_webp_from_buffer<class_Image_method_load_webp_from_buffer>` |
| 84 | + |
| 85 | +Several image formats can also be saved by Godot at runtime using the following |
| 86 | +methods: |
| 87 | + |
| 88 | +- :ref:`save_png<class_Image_method_save_png>` |
| 89 | + or :ref:`save_png_to_buffer<class_Image_method_save_png_to_buffer>` |
| 90 | +- :ref:`save_webp<class_Image_method_save_webp>` |
| 91 | + or :ref:`save_webp_to_buffer<class_Image_method_save_webp_to_buffer>` |
| 92 | +- :ref:`save_jpg<class_Image_method_save_jpg>` |
| 93 | + or :ref:`save_jpg_to_buffer<class_Image_method_save_jpg_to_buffer>` |
| 94 | +- :ref:`save_exr<class_Image_method_save_exr>` |
| 95 | + or :ref:`save_exr_to_buffer<class_Image_method_save_exr_to_buffer>` |
| 96 | + *(only available in editor builds, cannot be used in exported projects)* |
| 97 | + |
| 98 | +The methods with the ``to_buffer`` suffix save the image to a PackedByteArray |
| 99 | +instead of the filesystem. This is useful to send the image over the network or |
| 100 | +into a ZIP archive without having to write it on the filesystem. This can |
| 101 | +increase performance by reducing I/O utilization. |
| 102 | + |
| 103 | +Example of loading an image and displaying it in a :ref:`class_TextureRect` node |
| 104 | +(which requires conversion to :ref:`class_ImageTexture`): |
| 105 | + |
| 106 | +:: |
| 107 | + |
| 108 | + # Load an image of any format supported by Godot from the filesystem. |
| 109 | + var image = Image.load_from_file(path) |
| 110 | + $TextureRect.texture = ImageTexture.create_from_image(image) |
| 111 | + |
| 112 | + # Save the loaded Image to a PNG image. |
| 113 | + image.save_png("/path/to/file.png") |
| 114 | + |
| 115 | + # Save the converted ImageTexture to a PNG image. |
| 116 | + $TextureRect.texture.get_image().save_png("/path/to/file.png") |
| 117 | + |
| 118 | +Audio/video files |
| 119 | +----------------- |
| 120 | + |
| 121 | +Godot supports loading Ogg Vorbis audio at runtime. Note that not *all* files |
| 122 | +with an ``.ogg`` extension may be Ogg Vorbis files. Some may be Ogg Theora |
| 123 | +videos, or contain Opus audio within an Ogg container. These files will **not** |
| 124 | +load correctly as audio files in Godot. |
| 125 | + |
| 126 | +Example of loading an Ogg Vorbis audio file in an :ref:`class_AudioStreamPlayer` node: |
| 127 | + |
| 128 | +:: |
| 129 | + |
| 130 | + $AudioStreamPlayer.stream = AudioStreamOggVorbis.load_from_file(path) |
| 131 | + |
| 132 | +Example of loading an Ogg Theora video file in a :ref:`class_VideoStreamPlayer` node: |
| 133 | + |
| 134 | +:: |
| 135 | + |
| 136 | + var video_stream_theora = VideoStreamTheora.new() |
| 137 | + # File extension is ignored, so it is possible to load Ogg Theora videos |
| 138 | + # that have an `.ogg` extension this way. |
| 139 | + video_stream_theora.file = "/path/to/file.ogv" |
| 140 | + $VideoStreamPlayer.stream = video_stream_theora |
| 141 | + |
| 142 | + # VideoStreamPlayer's Autoplay property won't work if the stream is empty |
| 143 | + # before this property is set, so call `play()` after setting `stream`. |
| 144 | + $VideoStreamPlayer.play() |
| 145 | + |
| 146 | +.. note:: |
| 147 | + |
| 148 | + Godot doesn't support runtime loading of MP3 or WAV files yet. Until this is |
| 149 | + implemented, it's feasible to implement runtime WAV loading using a script |
| 150 | + since :ref:`class_AudioStreamWAV`'s ``data`` property is exposed to |
| 151 | + scripting. |
| 152 | + |
| 153 | + It's still possible to *save* WAV files using |
| 154 | + :ref:`save_to_wav<class_AudioStreamWAV_method_save_to_wav>`, which is useful |
| 155 | + for procedurally generated audio or microphone recordings. |
| 156 | + |
| 157 | +3D scenes |
| 158 | +--------- |
| 159 | + |
| 160 | +Godot has first-class support for glTF 2.0, both in the editor and exported |
| 161 | +projects. Using :ref:`class_gltfdocument` and :ref:`class_gltfstate` together, |
| 162 | +Godot can load and save glTF files in exported projects, in both text |
| 163 | +(``.gltf``) and binary (``.glb``) formats. The binary format should be preferred |
| 164 | +as it's faster to write and smaller, but the text format is easier to debug. |
| 165 | + |
| 166 | +Example of loading a glTF scene and appending its root node to the scene: |
| 167 | + |
| 168 | +:: |
| 169 | + |
| 170 | + # Load an existing glTF scene. |
| 171 | + # GLTFState is used by GLTFDocument to store the loaded scene's state. |
| 172 | + # GLTFDocument is the class that handles actually loading glTF data into a Godot node tree, |
| 173 | + # which means it supports glTF features such as lights and cameras. |
| 174 | + var gltf_document_load = GLTFDocument.new() |
| 175 | + var gltf_state_load = GLTFState.new() |
| 176 | + var error = gltf_document_load.append_from_file("/path/to/file.gltf", gltf_state_load) |
| 177 | + if error == OK: |
| 178 | + var gltf_scene_root_node = gltf_document_load.generate_scene(gltf_state_load) |
| 179 | + add_child(gltf_scene_root_node) |
| 180 | + else: |
| 181 | + show_error("Couldn't load glTF scene (error code: %s)." % error_string(error)) |
| 182 | + |
| 183 | + # Save a new glTF scene. |
| 184 | + var gltf_document_save := GLTFDocument.new() |
| 185 | + var gltf_state_save := GLTFState.new() |
| 186 | + gltf_document_save.append_from_scene(gltf_scene_root_node, gltf_state_save) |
| 187 | + # The file extension in the output `path` (`.gltf` or `.glb`) determines |
| 188 | + # whether the output uses text or binary format. |
| 189 | + # `GLTFDocument.generate_buffer()` is also available for saving to memory. |
| 190 | + gltf_document_save.write_to_filesystem(gltf_state_save, path) |
| 191 | + |
| 192 | +.. _doc_runtime_file_loading_and_saving_fonts: |
| 193 | + |
| 194 | +Fonts |
| 195 | +----- |
| 196 | + |
| 197 | +:ref:`load_dynamic_font<class_FontFile_method_load_bitmap_font>` supports the following |
| 198 | +font file formats: TTF, OTF, WOFF, WOFF2, PFB, PFM |
| 199 | + |
| 200 | +On the other hand, :ref:`load_bitmap_font<class_FontFile_method_load_bitmap_font>` supports |
| 201 | +the `BMFont <https://www.angelcode.com/products/bmfont/>`__ format (``.fnt`` or ``.font``). |
| 202 | + |
| 203 | +Additionally, it is possible to load any font that is installed on the system using |
| 204 | +Godot's support for :ref:`doc_using_fonts_system_fonts`. |
| 205 | + |
| 206 | +Example of loading a font file automatically according to its file extension, |
| 207 | +then adding it as a theme override to a :ref:`class_Label` node: |
| 208 | + |
| 209 | +:: |
| 210 | + |
| 211 | + var path = "/path/to/font.ttf" |
| 212 | + var path_lower = path.to_lower() |
| 213 | + var font_file = FontFile.new() |
| 214 | + if ( |
| 215 | + path_lower.ends_with(".ttf") |
| 216 | + or path_lower.ends_with(".otf") |
| 217 | + or path_lower.ends_with(".woff") |
| 218 | + or path_lower.ends_with(".woff2") |
| 219 | + or path_lower.ends_with(".pfb") |
| 220 | + or path_lower.ends_with(".pfm") |
| 221 | + ): |
| 222 | + font_file.load_dynamic_font(path) |
| 223 | + elif path_lower.ends_with(".fnt") or path_lower.ends_with(".font"): |
| 224 | + font_file.load_bitmap_font(path) |
| 225 | + else: |
| 226 | + push_error("Invalid font file format.") |
| 227 | + |
| 228 | + if not font_file.data.is_empty(): |
| 229 | + # If font was loaded successfully, add it as a theme override. |
| 230 | + $Label.add_theme_font_override("font", font_file) |
| 231 | + |
| 232 | +ZIP archives |
| 233 | +------------ |
| 234 | + |
| 235 | +Godot supports reading and writing ZIP archives using the :ref:`class_zipreader` |
| 236 | +and :ref:`class_zippacker` classes. This supports any ZIP file, including files |
| 237 | +generated by Godot's "Export PCK/ZIP" functionality (although these will contain |
| 238 | +imported Godot resources rather than the original project files). |
| 239 | + |
| 240 | +.. note:: |
| 241 | + |
| 242 | + Use :ref:`load_resource_pack<class_ProjectSettings_method_load_resource_pack>` |
| 243 | + to load PCK or ZIP files exported by Godot as |
| 244 | + :ref:`additional data packs <doc_exporting_pcks>`. That approach is preferred |
| 245 | + for DLCs, as it makes interacting with additional data packs seamless (virtual filesystem). |
| 246 | + |
| 247 | +This ZIP archive support can be combined with runtime image, 3D scene and audio |
| 248 | +loading to provide a seamless modding experience without requiring users to go |
| 249 | +through the Godot editor to generate PCK/ZIP files. |
| 250 | + |
| 251 | +Example that lists files in a ZIP archive in an :ref:`class_ItemList` node, |
| 252 | +then writes contents read from it to a new ZIP archive (essentially duplicating the archive): |
| 253 | + |
| 254 | +:: |
| 255 | + |
| 256 | + # Load an existing ZIP archive. |
| 257 | + var zip_reader = ZIPReader.new() |
| 258 | + zip_reader.open(path) |
| 259 | + var files = zip_reader.get_files() |
| 260 | + # The list of files isn't sorted by default. Sort it for more consistent processing. |
| 261 | + files.sort() |
| 262 | + for file in files: |
| 263 | + $ItemList.add_item(file, null) |
| 264 | + # Make folders disabled in the list. |
| 265 | + $ItemList.set_item_disabled(-1, file.ends_with("/")) |
| 266 | + |
| 267 | + # Save a new ZIP archive. |
| 268 | + var zip_packer = ZIPPacker.new() |
| 269 | + var error = zip_packer.open(path) |
| 270 | + if error != OK: |
| 271 | + push_error("Couldn't open path for saving ZIP archive (error code: %s)." % error_string(error)) |
| 272 | + return |
| 273 | + |
| 274 | + # Reuse the above ZIPReader instance to read files from an existing ZIP archive. |
| 275 | + for file in zip_reader.get_files(): |
| 276 | + zip_packer.start_file(file) |
| 277 | + zip_packer.write_file(zip_reader.read_file(file)) |
| 278 | + zip_packer.close_file() |
| 279 | + |
| 280 | + zip_packer.close() |
0 commit comments