Hi all,
Something want to share with you.. and yes… made in combination with AI bladiebla.. because that is what i do.
But works like a charm. just add this (with your adjustments on your asset storage) to your project (project settings → preload). it will automaticly load all your assets making you gameplay feeling to run a lot smoother than loading on runtime.
So feel free to use bits and pieces of this code for you own “preloading” thingamabob. It looks a lot, but it is actually 5 times the same, but with a different path. So stripping it would be easy.
# res://scripts/asset_preloader.gd
# Global asset_preloader
extends Node
# Singleton for preloading and caching game assets
# Add this as an AutoLoad in Project Settings
# Cache dictionaries for different asset types
var tile_scenes: Dictionary = {}
var effect_scenes: Dictionary = {}
var sound_resources: Dictionary = {}
var music_resources: Dictionary = {}
var skybox_resources: Dictionary = {}
var model_resources: Dictionary = {}
# Preloading state
var is_preloading: bool = false
var preloading_completed: bool = false
var preload_progress: float = 0.0
var total_assets_to_load: int = 0
var assets_loaded: int = 0
var current_loading_asset: String = ""
var failed_assets: Array = []
# Progress threshold for showing "complete"
const SHOW_COMPLETE_AT_PROGRESS: float = 0.95 # Show complete at 95%
signal preload_progress_updated(progress: float)
signal asset_loaded(asset_name: String, asset_type: String)
signal preload_completed()
signal preload_failed(error_message: String)
func _ready():
# Start preloading immediately when the game starts
call_deferred("start_preloading")
func start_preloading():
if is_preloading:
return
if Global.DEVELOPER_FEATURES:
_finish_preloading()
return
is_preloading = true
assets_loaded = 0
preload_progress = 0.0
failed_assets.clear()
print("Starting asset preloading...")
# Use a timeout to prevent infinite hanging
var timeout_timer = get_tree().create_timer(30.0) # 60 second timeout
timeout_timer.timeout.connect(_on_preload_timeout)
# Calculate total assets to preload
_calculate_total_assets()
# Preload different asset categories with error handling
await _preload_tile_scenes()
await _preload_effect_scenes()
await _preload_audio_resources()
await _preload_skybox_resources()
await _preload_model_resources()
# Mark as completed
_finish_preloading()
func _finish_preloading():
is_preloading = false
preloading_completed = true
current_loading_asset = ""
preload_progress = 1.0
if failed_assets.size() > 0:
print("Asset preloading completed with ", failed_assets.size(), " failed assets:")
for failed_asset in failed_assets:
print(" - Failed to load: ", failed_asset)
else:
print("Asset preloading completed successfully!")
preload_completed.emit()
func _on_preload_timeout():
print("Asset preloading timed out!")
preload_failed.emit("Asset preloading timed out after 60 seconds")
_finish_preloading()
func _calculate_total_assets():
# Count all assets that need to be preloaded
total_assets_to_load = 0
# Count tile scenes from level_elements.json
if Global.level_element_config.has("levelelements"):
for category in Global.level_element_config["levelelements"]:
total_assets_to_load += Global.level_element_config["levelelements"][category].size()
# Count effect scenes
var effect_paths = _get_effect_paths()
total_assets_to_load += effect_paths.size()
# Count audio files
total_assets_to_load += _count_directory_files("res://assets/soundfx/", [".mp3", ".wav", ".ogg"])
total_assets_to_load += _count_directory_files("res://assets/levelmusic/", [".mp3", ".wav", ".ogg"])
total_assets_to_load += _count_directory_files("res://assets/levelvoices/", [".mp3", ".wav", ".ogg"])
# Count skybox resources
total_assets_to_load += _count_directory_files("res://assets/skyboximagesgame/", [".res", ".tres"])
# Count model resources
var model_paths = _get_model_paths()
total_assets_to_load += model_paths.size()
print("Total assets to preload: ", total_assets_to_load)
# Ensure we have at least 1 to prevent division by zero
if total_assets_to_load == 0:
total_assets_to_load = 1
func _count_directory_files(directory_path: String, extensions: Array) -> int:
var count = 0
var dir = DirAccess.open(directory_path)
if dir:
dir.list_dir_begin()
var file_name = dir.get_next()
while file_name != "":
if not dir.current_is_dir():
var file_extension = "." + file_name.get_extension()
if file_extension in extensions:
count += 1
file_name = dir.get_next()
dir.list_dir_end()
return count
func _preload_tile_scenes():
print("Preloading tile scenes...")
if not Global.level_element_config.has("levelelements"):
print("No level elements config found")
return
for category_name in Global.level_element_config["levelelements"]:
var category = Global.level_element_config["levelelements"][category_name]
for block in category:
var block_name = block.get("name", "")
var tile_path = block.get("tile", "")
if block_name != "" and tile_path != "":
current_loading_asset = block_name
if ResourceLoader.exists(tile_path):
var scene_resource = ResourceLoader.load(tile_path, "PackedScene")
if scene_resource:
tile_scenes[block_name] = scene_resource
asset_loaded.emit(block_name, "tile")
else:
print("Failed to load tile scene: ", tile_path)
failed_assets.append(tile_path)
else:
print("Tile scene doesn't exist: ", tile_path)
failed_assets.append(tile_path)
_update_progress()
# Yield to prevent frame drops
await get_tree().process_frame
func _get_effect_paths() -> Array:
return [
"res://scenes/effect_smokeplosion/effect.tscn",
"res://scenes/effect_welder/effect.tscn",
"res://assets/leveleffects/lightning/lightning.tscn",
"res://assets/leveleffects/explosion/explosion.tscn",
]
func _preload_effect_scenes():
print("Preloading effect scenes...")
var effect_paths = _get_effect_paths()
for path in effect_paths:
var effect_name = path.get_file().get_basename()
current_loading_asset = effect_name
if ResourceLoader.exists(path):
var effect_resource = ResourceLoader.load(path, "PackedScene")
if effect_resource:
effect_scenes[effect_name] = effect_resource
asset_loaded.emit(effect_name, "effect")
else:
print("Failed to load effect scene: ", path)
failed_assets.append(path)
else:
print("Effect scene doesn't exist: ", path)
failed_assets.append(path)
_update_progress()
await get_tree().process_frame
func _preload_audio_resources():
print("Preloading audio resources...")
# Preload sound effects
var sound_dir = "res://assets/soundfx/"
await _preload_directory_resources(sound_dir, sound_resources, [".mp3", ".wav", ".ogg"], "sound")
# Preload music
var music_dir = "res://assets/levelmusic/"
await _preload_directory_resources(music_dir, music_resources, [".mp3", ".wav", ".ogg"], "music")
# Preload voice files
var voice_dir = "res://assets/levelvoices/"
await _preload_directory_resources(voice_dir, sound_resources, [".mp3", ".wav", ".ogg"], "voice")
func _preload_skybox_resources():
print("Preloading skybox resources...")
var skybox_dir = "res://assets/skyboximagesgame/"
await _preload_directory_resources(skybox_dir, skybox_resources, [".res", ".tres"], "skybox")
func _get_model_paths() -> Array:
return [
"res://assets/models/bob/bob.tscn",
"res://assets/models/bobexplode/bobexplode.tscn",
"res://assets/models/bob_highres/bob_highres.tscn",
]
func _preload_model_resources():
print("Preloading model resources...")
var model_paths = _get_model_paths()
for path in model_paths:
var model_name = path.get_file().get_basename()
current_loading_asset = model_name
if ResourceLoader.exists(path):
var model_resource = ResourceLoader.load(path, "PackedScene")
if model_resource:
model_resources[model_name] = model_resource
asset_loaded.emit(model_name, "model")
else:
print("Failed to load model: ", path)
failed_assets.append(path)
else:
print("Model doesn't exist: ", path)
failed_assets.append(path)
_update_progress()
await get_tree().process_frame
func _preload_directory_resources(directory_path: String, cache_dict: Dictionary, extensions: Array, asset_type: String):
var dir = DirAccess.open(directory_path)
if not dir:
print("Failed to open directory: ", directory_path)
return
dir.list_dir_begin()
var file_name = dir.get_next()
while file_name != "":
if not dir.current_is_dir():
var file_extension = "." + file_name.get_extension()
if file_extension in extensions:
var full_path = directory_path + file_name
var resource_name = file_name.get_basename()
current_loading_asset = resource_name
if ResourceLoader.exists(full_path):
var resource = ResourceLoader.load(full_path)
if resource:
cache_dict[resource_name] = resource
asset_loaded.emit(resource_name, asset_type)
else:
print("Failed to load resource: ", full_path)
failed_assets.append(full_path)
else:
print("Resource doesn't exist: ", full_path)
failed_assets.append(full_path)
_update_progress()
await get_tree().process_frame
file_name = dir.get_next()
dir.list_dir_end()
func _update_progress():
assets_loaded += 1
preload_progress = float(assets_loaded) / float(total_assets_to_load) if total_assets_to_load > 0 else 1.0
preload_progress = clamp(preload_progress, 0.0, 1.0)
# Signal completion when we hit 95%, even though loading continues
if preload_progress >= SHOW_COMPLETE_AT_PROGRESS and not preloading_completed:
preloading_completed = true
print("Signaling completion at 95% - remaining assets load in background")
preload_completed.emit()
preload_progress_updated.emit(preload_progress)
# Public methods to get preloaded assets
func get_tile_scene(block_name: String) -> PackedScene:
return tile_scenes.get(block_name)
func get_effect_scene(effect_name: String) -> PackedScene:
return effect_scenes.get(effect_name)
func get_sound_resource(sound_name: String) -> AudioStream:
return sound_resources.get(sound_name)
func get_music_resource(music_name: String) -> AudioStream:
return music_resources.get(music_name)
func get_skybox_resource(skybox_name: String) -> Resource:
return skybox_resources.get(skybox_name)
func get_model_resource(model_name: String) -> PackedScene:
return model_resources.get(model_name)
# Check if specific asset is preloaded
func is_tile_preloaded(block_name: String) -> bool:
return tile_scenes.has(block_name)
func is_effect_preloaded(effect_name: String) -> bool:
return effect_scenes.has(effect_name)
# Fallback method - load asset if not preloaded
func get_tile_scene_safe(block_name: String) -> PackedScene:
if tile_scenes.has(block_name):
return tile_scenes[block_name]
# Fallback to loading from level_element_config
var block_data = Global.find_block_data(block_name)
if block_data.has("tile"):
var scene_resource = ResourceLoader.load(block_data["tile"], "PackedScene")
if scene_resource:
tile_scenes[block_name] = scene_resource # Cache for next time
return scene_resource
return null
# Force complete preloading (for debugging)
func force_complete_preloading():
print("Force completing preloading...")
_finish_preloading()
# Get current loading status
func get_loading_status() -> Dictionary:
return {
"is_preloading": is_preloading,
"completed": preloading_completed,
"progress": preload_progress,
"assets_loaded": assets_loaded,
"total_assets": total_assets_to_load,
"current_asset": current_loading_asset,
"failed_assets": failed_assets.size()
}
# Get progress as percentage string for UI display
func get_progress_text() -> String:
if preloading_completed:
return "Complete!" # Show "Complete!" once we hit 95%
else:
var percentage = int(preload_progress * 100)
return str(percentage) + "%"