game/godot

Godot3 - I/O Background loading

C/H 2018. 11. 16. 17:26

Background loading

When switching the main scene of your game (e.g. going to a new level), you might want to show a loading screen with some indication that progress is being made. The main load method (ResourceLoader::load or just load from GDScript) blocks your thread while the resource is being loaded, so it’s not good. This document discusses the ResourceInteractiveLoader class for smoother load screens.
게임의 주요 장면을 전환 할 때 (예 : 새로운 단계로 진행하는 경우) 진행 상황이 표시되는 로딩 화면을 표시하고자 할 수 있습니다. GDScript에서 주로드 방법 (ResourceLoader::load 또는 load)은 리소스가 로드되는 동안 스레드를 차단하므로 좋지 않습니다. 이 문서에서는 더 부드러운 로드 스크린을 위한 ResourceInteractiveLoader 클래스에 대해 설명합니다.

ResourceInteractiveLoader

The ResourceInteractiveLoader class allows you to load a resource in stages. Every time the method poll is called, a new stage is loaded, and control is returned to the caller. Each stage is generally a sub-resource that is loaded by the main resource. For example, if you’re loading a scene that loads 10 images, each image will be one stage.
ResourceInteractiveLoader 클래스를 사용하면 리소스를 단계적으로 로드 할 수 있습니다. poll 메서드가 호출 될 때마다 새로운 스테이지가 로드되고 컨트롤이 호출자에게 반환됩니다. 각 단계는 일반적으로 주 리소스에 의해 로드되는 하위 리소스입니다. 예를 들어 10 개의 이미지를 로드하는 장면을 로드하는 경우 각 이미지는 하나의 스테이지가됩니다.

Usage

Usage is generally as follows
사용법은 일반적으로 다음과 같습니다

Obtaining a ResourceInteractiveLoader

Ref<ResourceInteractiveLoader> ResourceLoader::load_interactive(String p_path);

This method will give you a ResourceInteractiveLoader that you will use to manage the load operation.
이 메서드는 로드 작업을 관리하는데 사용할 ResourceInteractiveLoader를 제공합니다.

Polling

Error ResourceInteractiveLoader::poll();

Use this method to advance the progress of the load. Each call to poll will load the next stage of your resource. Keep in mind that each stage is one entire “atomic” resource, such as an image, or a mesh, so it will take several frames to load.
이 방법을 사용하여 로드 진행률을 향상시킵니다. poll을 호출 할 때마다 리소스의 다음 단계가 로드됩니다. 각 스테이지는 이미지 또는 메쉬와 같은 전체 "원자"리소스이므로 로드하는데 여러 프레임이 소요됩니다.

Returns OK on no errors, ERR_FILE_EOF when loading is finished. Any other return value means there was an error and loading has stopped.
로드가 끝나면 ERR_FILE_EOF를 반환하고 오류가없는 경우 OK를 반환합니다. 다른 반환값은 오류가 있었고 로드가 중지되었음을 의미합니다.

Load progress (optional)

To query the progress of the load, use the following methods:
로드 진행률을 쿼리하려면 다음 방법을 사용하십시오.

int ResourceInteractiveLoader::get_stage_count() const;
	int ResourceInteractiveLoader::get_stage() const;

get_stage_count returns the total number of stages to load. get_stage returns the current stage being loaded.
get_stage_count는 로드 할 총 스테이지 수를 반환합니다. get_stage는 로드되는 현재 스테이지를 반환합니다.

Forcing completion (optional)

Error ResourceInteractiveLoader::wait();

Use this method if you need to load the entire resource in the current frame, without any more steps.
더 이상 단계 없이 현재 프레임에서 전체 자원을 로드해야하는 경우 이 메소드를 사용하십시오.

Obtaining the resource

Ref<Resource> ResourceInteractiveLoader::get_resource();

If everything goes well, use this method to retrieve your loaded resource.
모든 것이 잘 진행되면 이 메소드를 사용하여 로드 된 자원을 검색하십시오.

Example

This example demostrates how to load a new scene. Consider it in the context of the Singletons (AutoLoad) example.
이 예제는 새로운 장면을 로드하는 방법을 설명합니다. 싱글톤 (자동로드) 예입니다.

First we setup some variables and initialize the current_scene with the main scene of the game:
먼저 일부 변수를 설정하고 게임의 기본 장면으로 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)

The function goto_scene is called from the game when the scene needs to be switched. It requests an interactive loader, and calls set_process(true) to start polling the loader in the _process callback. It also starts a “loading” animation, which can show a progress bar or loading screen, etc.
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 is where the loader is polled. poll is called, and then we deal with the return value from that call. OK means keep polling, ERR_FILE_EOF means load is done, anything else means there was an error. Also note we skip one frame (via wait_frames, set on the goto_scene function) to allow the loading screen to show up.
_process는 로더가 폴링되는 위치입니다. poll이 호출된 다음, 해당 호출의 반환값을 처리합니다. OK는 폴링을 유지한다는 의미이며, ERR_FILE_EOF는 로드가 완료되었음을 의미합니다. 또한 로드 화면이 표시되도록 한 프레임을 건너 뜁니다 (goto_scene 함수에 설정된 wait_frames를 통해).

Note how we use OS.get_ticks_msec to control how long we block the thread. Some stages might load fast, which means we might be able to cram more than one call to poll in one frame, some might take way more than your value for time_max, so keep in mind we won’t have precise control over the timings.
우리가 OS.get_ticks_msec를 사용하여 스레드를 차단하는 시간을 제어하는 방법에 유의하십시오. 일부 스테이지는 빠르게 로드 될 수 있습니다. 즉, 한 프레임에서 poll 호출을 한 번 이상 수행 할 수 있고 일부는 time_max에 대한 값보다 더 많이 걸릴 수 있습니다. 타이밍을 정확하게 제어 할 수는 없습니다.

func _process(time):
    if loader == null:
        # no need to process anymore
        set_process(false)
        return

    if wait_frames > 0: # wait for frames to let the "loading" animation to show up
        wait_frames -= 1
        return

    var t = OS.get_ticks_msec()
    while OS.get_ticks_msec() < t + time_max: # use "time_max" to control how much time we block this thread

        # poll your loader
        var err = loader.poll()

        if err == ERR_FILE_EOF: # load finished
            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

Some extra helper functions. update_progress updates a progress bar, or can also update a paused animation (the animation represents the entire load process from beginning to end). set_new_scene puts the newly loaded scene on the tree. Because it’s a scene being loaded, instance() needs to be called on the resource obtained from the loader.
일부 추가 도움 기능. 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 len = get_node("animation").get_current_animation_length()

    # call this on a paused animation. use "true" as the second parameter to force the animation to update
    get_node("animation").seek(progress * len, true)

func set_new_scene(scene_resource):
    current_scene = scene_resource.instance()
    get_node("/root").add_child(current_scene)

Using multiple threads

ResourceInteractiveLoader can be used from multiple threads. A couple of things to keep in mind if you attempt it:
ResourceInteractiveLoader는 여러 스레드에서 사용할 수 있습니다. 시도 할 때 명심해야 할 몇 가지 :

Use a Semaphore

While your thread waits for the main thread to request a new resource, use a Semaphore to sleep (instead of a busy loop or anything similar).
스레드는 주 스레드가 새 리소스를 요청할 때까지 기다리는 동안 Busy 루프나 이와 유사한 것 대신에 세마포를 사용하여 절전 모드로 전환하십시오.

Not blocking main thread during the polling

If you have a mutex to allow calls from the main thread to your loader class, don’t lock it while you call poll on the loader. When a resource is finished loading, it might require some resources from the low level APIs (VisualServer, etc), which might need to lock the main thread to acquire them. This might cause a deadlock if the main thread is waiting for your mutex while your thread is waiting to load a resource.
기본 스레드에서 로더 클래스로 호출 할 수있는 뮤텍스가있는 경우 로더에서 폴링을 호출하는 동안 이를 잠그지 마십시오. 리소스로드가 끝나면 저수준 API (VisualServer 등)의 리소스가 필요할 수 있습니다. 이 리소스는 주 스레드를 잠궈서 이를 가져와야 할 수 있습니다. 스레드가 리소스를 로드하기 위해 대기하는 동안 주 스레드가 뮤텍스를 기다리는 경우 교착 상태가 발생할 수 있습니다.

Example class

You can find an example class for loading resources in threads here: resource_queue.gd. Usage is as follows:

func start()

Call after you instance the class to start the thread.
스레드를 시작하기 위해 클래스를 인스턴스화 한 후 호출하십시오.

func queue_resource(path, p_in_front = false)

Queue a resource. Use optional parameter “p_in_front” to put it in front of the queue.
리소스를 대기열에 둡니다. 선택적 매개 변수 "p_in_front"를 사용하여 큐 앞에 놓습니다.

func cancel_resource(path)

Remove a resource from the queue, discarding any loading done.
대기열(큐)에서 자원을 제거하고 완료된 로드를 삭제합니다.

func is_ready(path)

Returns true if a resource is done loading and ready to be retrieved.
자원의 로드 완료되고 받을 준비가 끝난 경우에 true를 리턴합니다.

func get_progress(path)

Get the progress of a resource. Returns -1 on error (for example if the resource is not on the queue), or a number between 0.0 and 1.0 with the progress of the load. Use mostly for cosmetic purposes (updating progress bars, etc), use is_ready to find out if a resource is actually ready.
리소스의 진행 상황을 가져옵니다. 오류가 발생하면 -1을 반환하고 (예:리소스가 대기열에없는 경우) 또는 로드 진행률에 따라 0.0과 1.0 사이의 숫자를 반환합니다. 대부분 코스메틱 목적으로 사용하고 (진행 막대를 업데이트하는 등), 리소스가 실제로 준비되었는지 알아보기 위해 is_ready를 사용하십시오.

func get_resource(path)

Returns the fully loaded resource, or null on error. If the resource is not done loading (is_ready returns false), it will block your thread and finish the load. If the resource is not on the queue, it will call ResourceLoader::load to load it normally and return it.
완전히 로드된 자원 또는 오류시 null을 리턴합니다. 로드가 완료되지 않은 리소스(is_ready가 false를 반환하면)는 스레드를 차단하고 로드를 완료합니다. 리소스가 대기열에 없다면 ResourceLoader::load를 호출하여 정상적으로 로드하고 리턴합니다.

Example:

# 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.
# 사용자가 게임과 상호 작용할 수 없는 10 초의 컷씬으로 게임이 시작된다고 가정합니다.
# For that time we know they won't use the pause menu, so we can queue it to load during the cutscene:
# 그 시간 동안 우리는 일시 중지 메뉴를 사용하지 않을 것이므로 cutscene 동안 로드하도록 대기열에 넣을 수 있습니다.
queue.queue_resource("res://pause_menu.tres")
start_curscene()

# 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 parameter to put it at the front
												 # of the queue, pausing the load of any other resource
												 # 두 번째 매개 변수로 "true"를 사용하여 큐의 맨 앞에 놓고 다른 리소스의 로드를 일시 중지합니다.												 

# 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_process("res://level_1.tscn"))

# when the user walks away from the trigger zone in your Metroidvania game:
# 사용자가 Metroidvania 게임의 방아쇠 영역을 떠날 때 :
queue.cancel_resource("res://zone_2.tscn")

Note: this code in its current form is not tested in real world scenarios. Ask punto on IRC (#godotengine on irc.freenode.net) for help.
참고: 현재 양식의 이 코드는 실제 시나리오에서는 테스트되지 않습니다. IRC의 punto (irc.freenode.net의 #godotengine)에게 도움을 요청하십시오.

반응형

'game > godot' 카테고리의 다른 글

Godot3 - I/O Saving games  (0) 2018.11.19
Godot3 - I/O Data paths  (0) 2018.11.17
Godot3 API - Singletons (AutoLoad)  (0) 2018.11.15
Godot 3.0.6 compile Success Process  (0) 2018.11.15
Godot3 API - Customizing the Web export HTML page  (0) 2018.11.14