Godot 인터페이스

기능을 위해 다른 오브젝트에 의존하는 스크립트가 필요한 경우가 종종 있습니다. 이 과정에는 2부분이 있습니다:

  1. 아마도 기능을 갖는 오브젝트에 대한 참조를 얻기.

  2. 오브젝트에서 데이터 또는 논리에 접근하기.

이 튜토리얼의 나머지 부분에서는 이 모든 작업을 하는 다양한 방법을 간략하게 설명합니다.

오브젝트 참조 얻기

모든 :ref:`Object <class_Object>에 있어, 오브젝트를 참조하는 가장 기본이 되는 방식은 획득한 다른 인스턴스로부터 존재하는 오브젝트로 참조를 가져오는 것이다.

var obj = node.object # Property access.
var obj = node.get_object() # Method access.

Reference 오브젝트에게도 같은 원리가 적용됩니다. 사용자들은 종종 NodeResource를 이런 방식으로 연결하지만, 대체 조치가 가능합니다.

속성(property)이나 메서드 액세스 대신 로드 액세스로 리소스에 접근할 수 있습니다.

var preres = preload(path) # Load resource during scene load
var res = load(path) # Load resource when program reaches statement

# Note that users load scenes and scripts, by convention, with PascalCase
# names (like typenames), often into constants.
const MyScene : = preload("my_scene.tscn") as PackedScene # Static load
const MyScript : = preload("my_script.gd") as Script

# This type's value varies, i.e. it is a variable, so it uses snake_case.
export(Script) var script_type: Script

# If need an "export const var" (which doesn't exist), use a conditional
# setter for a tool script that checks if it's executing in the editor.
tool # Must place at top of file.

# Must configure from the editor, defaults to null.
export(Script) var const_script setget set_const_script
func set_const_script(value):
    if Engine.is_editor_hint():
        const_script = value

# Warn users if the value hasn't been set.
func _get_configuration_warning():
    if not const_script:
        return "Must initialize property 'const_script'."
    return ""

다음을 명심하십시오:

  1. 언어가 리소스를 로드 할 수 있는 방법은 여러가지가 존재합니다.

  2. 오브젝트가 데이터에 액세스하는 방법을 설계할 때 리소스를 레퍼런스 형식으로 전달하는 것이 가능함을 염두에 두십시오.

  3. 리소스를 로딩할 때 내부적으로 엔진이 관리하는 캐싱된 리소스 인스턴스를 가져온다는 사실을 기억해두십시오. 새 오브젝트를 생성하려면 이미 존재하면 레퍼런스를 duplicate <class_Resource_method_duplicate>`하거나 ``new()` 를 사용해 처음부터 인스턴스화 시켜야 합니다.

노드도 마찬가지로 다른 방법으로 액세스가 가능한데, 씬트리를 이용하는 방법입니다.

extends Node

# Slow.
func dynamic_lookup_with_dynamic_nodepath():
    print(get_node("Child"))

# Faster. GDScript only.
func dynamic_lookup_with_cached_nodepath():
    print($Child)

# Fastest. Doesn't break if node moves later.
# Note that `onready` keyword is GDScript only.
# Other languages must do...
#     var child
#     func _ready():
#         child = get_node("Child")
onready var child = $Child
func lookup_and_cache_for_future_access():
    print(child)

# Delegate reference assignment to an external source.
# Con: need to perform a validation check.
# Pro: node makes no requirements of its external structure.
#      'prop' can come from anywhere.
var prop
func call_me_after_prop_is_initialized_by_parent():
    # Validate prop in one of three ways.

    # Fail with no notification.
    if not prop:
        return

    # Fail with an error message.
    if not prop:
        printerr("'prop' wasn't initialized")
        return

    # Fail and terminate.
    # Note: Scripts run from a release export template don't
    # run `assert` statements.
    assert(prop, "'prop' wasn't initialized")

# Use an autoload.
# Dangerous for typical nodes, but useful for true singleton nodes
# that manage their own data and don't interfere with other objects.
func reference_a_global_autoloaded_variable():
    print(globals)
    print(globals.prop)
    print(globals.my_getter())

오브젝트에서 데이터나 로직에 액세스하기

Godot의 스크립팅 API는 덕 타이핑 되어 있습니다. 따라서 스크립트가 동작을 수행할 때 Godot은 동작의 지원을 타입 을 통해 확인하는 것이 아닌 오브젝트가 개별 메소드를 구현 하는지를 확인합니다.

예를 들어서, CanvasItem <class_CanvasItem>`는 ``visible` 속성(property)를 갖고 있습니다. 스크립팅 API에게 노출된 모든 속성(property)들은 이름으로 바인딩 된 setter와 getter 쌍과 같습니다. :ref:`CanvasItem.visible <class_CanvasItem_property_visible>`에 액세스가 발생하면 Godot은 다음과 같은 동작을 수행합니다:

  • 오브젝트에 스크립트가 붙어있다면 스크립트를 통해 속성(property)의 값을 지정하려 시도합니다. 따라서 스크립트가 속성(property)의 setter 메소드를 오버라이드 해서 부모 오브젝트의 속성(property)를 오버라이드 할 수 있도록 합니다.

  • 만약 스크립트에게 그 속성(property)가 없다면 스크립트는 ClassDB에 HashMap lookup을 수행해 CanvasItem 클래스와 이를 상속하는 모든 타입에서 "visible" 속성(property)을 찾고 만약 발견한다면 연결된 setter나 getter를 호출합니다. HashMap에 관한 자세한 정보는 data preferences 문서를 참고하십시오.

  • 만약 찾지 못한다면 유저가"script" 또는 "meta" 속성(property)에 액세스하는 것을 원하는지 명시적으로 묻습니다.

  • If not, it checks for a _set/_get implementation (depending on type of access) in the CanvasItem and its inherited types. These methods can execute logic that gives the impression that the Object has a property. This is also the case with the _get_property_list method.

    • Note that this happens even for non-legal symbol names such as in the case of TileSet's "1/tile_name" property. This refers to the name of the tile with ID 1, i.e. TileSet.tile_get_name(1).

As a result, this duck-typed system can locate a property either in the script, the object's class, or any class that object inherits, but only for things which extend Object.

Godot provides a variety of options for performing runtime checks on these accesses:

  • A duck-typed property access. These will property check (as described above). If the operation isn't supported by the object, execution will halt.

    # All Objects have duck-typed get, set, and call wrapper methods.
    get_parent().set("visible", false)
    
    # Using a symbol accessor, rather than a string in the method call,
    # will implicitly call the `set` method which, in turn, calls the
    # setter method bound to the property through the property lookup
    # sequence.
    get_parent().visible = false
    
    # Note that if one defines a _set and _get that describe a property's
    # existence, but the property isn't recognized in any _get_property_list
    # method, then the set() and get() methods will work, but the symbol
    # access will claim it can't find the property.
    
  • A method check. In the case of CanvasItem.visible, one can access the methods, set_visible and is_visible like any other method.

    var child = get_child(0)
    
    # Dynamic lookup.
    child.call("set_visible", false)
    
    # Symbol-based dynamic lookup.
    # GDScript aliases this into a 'call' method behind the scenes.
    child.set_visible(false)
    
    # Dynamic lookup, checks for method existence first.
    if child.has_method("set_visible"):
        child.set_visible(false)
    
    # Cast check, followed by dynamic lookup.
    # Useful when you make multiple "safe" calls knowing that the class
    # implements them all. No need for repeated checks.
    # Tricky if one executes a cast check for a user-defined type as it
    # forces more dependencies.
    if child is CanvasItem:
        child.set_visible(false)
        child.show_on_top = true
    
    # If one does not wish to fail these checks without notifying users,
    # one can use an assert instead. These will trigger runtime errors
    # immediately if not true.
    assert(child.has_method("set_visible"))
    assert(child.is_in_group("offer"))
    assert(child is CanvasItem)
    
    # Can also use object labels to imply an interface, i.e. assume it
    # implements certain methods.
    # There are two types, both of which only exist for Nodes: Names and
    # Groups.
    
    # Assuming...
    # A "Quest" object exists and 1) that it can "complete" or "fail" and
    # that it will have text available before and after each state...
    
    # 1. Use a name.
    var quest = $Quest
    print(quest.text)
    quest.complete() # or quest.fail()
    print(quest.text) # implied new text content
    
    # 2. Use a group.
    for a_child in get_children():
        if a_child.is_in_group("quest"):
            print(quest.text)
            quest.complete() # or quest.fail()
            print(quest.text) # implied new text content
    
    # Note that these interfaces are project-specific conventions the team
    # defines (which means documentation! But maybe worth it?).
    # Any script that conforms to the documented "interface" of the name or
    # group can fill in for it.
    
  • Outsource the access to a FuncRef. These may be useful in cases where one needs the max level of freedom from dependencies. In this case, one relies on an external context to setup the method.

# child.gd
extends Node
var fn = null

func my_method():
    if fn:
        fn.call_func()

# parent.gd
extends Node

onready var child = $Child

func _ready():
    child.fn = funcref(self, "print_me")
    child.my_method()

func print_me():
    print(name)

These strategies contribute to Godot's flexible design. Between them, users have a breadth of tools to meet their specific needs.