バックグラウンド読み込み¶
ゲームのメインシーンを切り替える(たとえば、新しいレベルに移動する)場合は、進行中の状態を示すロード画面を表示することができます。メインのロードメソッド ResourceLoader::load
または単にGDScriptからの load
)はスレッドをブロックし、リソースのロード中にゲームがフリーズして応答しなくなるようにします。このドキュメントでは、ロード画面をよりスムーズにするために ResourceInteractiveLoader
クラスを使用する代替方法について説明します。
リソースインタラクティブローダー¶
ResourceInteractiveLoader
クラスを使用すると、リソースを段階的にロードできます。 メソッド poll
が呼び出されるたびに、新しいステージがロードされ、制御が呼び出し元に返されます。 通常、各ステージは、メインリソースによってロードされるサブリソースです。 たとえば、10個の画像を読み込むシーンを読み込む場合、各画像は1つのステージになります。
使用法¶
使用法は一般的に以下の通りです
リソースインタラクティブローダーの取得¶
Ref<ResourceInteractiveLoader> ResourceLoader::load_interactive(String p_path);
このメソッドを使用すると、ロード操作の管理に使用するリソースインタラクティブローダーが提供されます。
ポーリング¶
Error ResourceInteractiveLoader::poll();
このメソッドを使用して、ロードの進行を進めます。poll
を呼び出すたびに、リソースの次のステージがロードされます。 各ステージは、イメージやメッシュなどの「アトミック」リソース全体であるため、ロードするのに数フレームかかることに注意してください。
エラーがなければ OK
を返し、読み込みが完了すると ERR_FILE_EOF
を返します。 その他の戻り値は、エラーが発生してロードが停止したことを意味します。
読み込みの進行状況(オプション)¶
読み込みの進行状況を確認するには、次のメソッドを使用します:
int ResourceInteractiveLoader::get_stage_count() const;
int ResourceInteractiveLoader::get_stage() const;
get_stage_count
は、読み込むステージの合計数を返します。 get_stage
は、現在読み込まれているステージを返します。
強制的に完了(オプション)¶
Error ResourceInteractiveLoader::wait();
これ以上手順を実行せずに、現在のフレームのリソース全体をロードする必要がある場合は、このメソッドを使用します。
リソースの取得¶
Ref<Resource> ResourceInteractiveLoader::get_resource();
すべてがうまくいけば、このメソッドを使用して読み込まれたリソースを取得します。
例¶
この例は、新しいシーンをロードする方法を示しています。 例題、シングルトン(自動読み込み) のコンテキストで考察してみましょう。
まず、いくつかの変数を設定し、ゲームのメインシーンで current_scene
を初期化します:
var loader
var wait_frames
var time_max = 100 # msec
var current_scene
func _ready():
var root = get_tree().get_root()
current_scene = root.get_child(root.get_child_count() -1)
関数 goto_scene
は、シーンを切り替える必要があるときにゲームから呼び出されます。インタラクティブなローダーを要求し、_process
コールバックでローダーのポーリングを開始するために set_process(true)
を呼び出します。また、プログレスバーまたはロード画面を表示できる「ロード」アニメーションも開始します。
func goto_scene(path): # Game requests to switch to this scene.
loader = ResourceLoader.load_interactive(path)
if loader == null: # Check for errors.
show_error()
return
set_process(true)
current_scene.queue_free() # Get rid of the old scene.
# Start your "loading..." animation.
get_node("animation").play("loading")
wait_frames = 1
_process
はローダーがポーリングされる場所です。poll
が呼び出され、その呼び出しからの戻り値を処理します。「OK」はポーリングを続けることを意味し、ERR_FILE_EOF
は読み込みが完了したことを意味し、それ以外はエラーが発生したことを意味します。また、ロード画面が表示されるように、1フレームをスキップします( wait_frames
を介して、goto_scene
関数で設定)。
OS.get_ticks_msec
を使用してスレッドをブロックする時間を制御する方法に注意してください。一部のステージは高速でロードされる場合があります。つまり、1つのフレームで poll
を複数回呼び出すことができます。いくつかは time_max
の値よりもはるかに大きくなる可能性があるため、タイミングを正確に制御できないことに注意してください。
func _process(time):
if loader == null:
# no need to process anymore
set_process(false)
return
# Wait for frames to let the "loading" animation show up.
if wait_frames > 0:
wait_frames -= 1
return
var t = OS.get_ticks_msec()
# Use "time_max" to control for how long we block this thread.
while OS.get_ticks_msec() < t + time_max:
# Poll your loader.
var err = loader.poll()
if err == ERR_FILE_EOF: # Finished loading.
var resource = loader.get_resource()
loader = null
set_new_scene(resource)
break
elif err == OK:
update_progress()
else: # Error during loading.
show_error()
loader = null
break
いくつかの追加のヘルパー関数。 update_progress
はプログレスバーを更新するか、一時停止中のアニメーションも更新できます(アニメーションはロードプロセス全体を最初から最後まで表します)。set_new_scene
は新しくロードされたシーンをツリーに置きます。 ロードされるシーンなので、ローダーから取得したリソースで instance()
を呼び出す必要があります。
func update_progress():
var progress = float(loader.get_stage()) / loader.get_stage_count()
# Update your progress bar?
get_node("progress").set_progress(progress)
# ...or update a progress animation?
var length = get_node("animation").get_current_animation_length()
# Call this on a paused animation. Use "true" as the second argument to
# force the animation to update.
get_node("animation").seek(progress * length, true)
func set_new_scene(scene_resource):
current_scene = scene_resource.instance()
get_node("/root").add_child(current_scene)
複数のスレッドの使用¶
リソースインタラクティブローダーは、複数のスレッドから使用できます。以下の点に注意してください:
セマフォを使用する¶
スレッドがメインスレッドが新しいリソースを要求するのを待っている間、(ビジーループまたは同様のものの代わりに) セマフォ(Semaphore)
を使用してスリープします。
ポーリング中にメイン スレッドをブロックしない¶
メインスレッドからローダークラスへの呼び出しを許可するミューテックスがある場合、ローダークラスで poll
を呼び出している間はメインスレッドをロックしないでください。リソースの読み込みが完了すると、リソースを取得するためにメイン スレッドをロックする必要がある低レベルの API (VisualServer など) からいくつかのリソースが必要になる場合があります。これは、スレッドがリソースのロードを待機している間にメインスレッドがミューテックスを待機している場合、デッドロックを引き起こす可能性があります。
サンプルクラス¶
スレッドでリソースをロードするためのサンプルクラスはここです: resource_queue.gd
。使用法は次のとおりです:
func start()
スレッドを開始するクラスをインスタンスした後に呼び出します。
func queue_resource(path, p_in_front = false)
リソースをキューに入れます。キューの先頭に置く場合は、オプションの引数 "p_in_front" を使用します。
func cancel_resource(path)
キューからリソースを削除し、行われた読み込みを破棄します。
func is_ready(path)
リソースが完全に読み込まれて取得できる状態にある場合は、true
を返します。
func get_progress(path)
リソースの進行状況を取得します。エラーが発生した場合(たとえば、リソースがキューにない場合)は-1を返します。または、読み込みの進行とともに0.0〜1.0の数値を返します。主に装飾を目的(進行状況バーの更新など)に使用し、is_ready
を使用してリソースが実際に準備できているかどうかを確認します。
func get_resource(path)
完全に読み込まれたリソース、またはエラーの場合は null
を返します。リソースが完全に読み込まれていない場合(is_ready
が false
を返します)、スレッドがブロックされ、読み込みが終了します。リソースがキューにない場合は ResourceLoader::load
を呼び出して、通常どおりに読み込んで返します。
例:¶
# Initialize.
queue = preload("res://resource_queue.gd").new()
queue.start()
# Suppose your game starts with a 10 second cutscene, during which the user
# can't interact with the game.
# For that time, we know they won't use the pause menu, so we can queue it
# to load during the cutscene:
queue.queue_resource("res://pause_menu.tres")
start_cutscene()
# Later, when the user presses the pause button for the first time:
pause_menu = queue.get_resource("res://pause_menu.tres").instance()
pause_menu.show()
# When you need a new scene:
queue.queue_resource("res://level_1.tscn", true)
# Use "true" as the second argument to put it at the front of the queue,
# pausing the load of any other resource.
# To check progress.
if queue.is_ready("res://level_1.tscn"):
show_new_level(queue.get_resource("res://level_1.tscn"))
else:
update_progress(queue.get_progress("res://level_1.tscn"))
# When the user walks away from the trigger zone in your Metroidvania game:
queue.cancel_resource("res://zone_2.tscn")
Note: this code, in its current form, is not tested in real world scenarios. If you run into any issues, ask for help in one of Godot's community channels.