functiongdscript

Is there a way to define an helper function inside another one?


This code raises a parsing error:

func foo():
 func bar():
   pass
 pass

Error pasing expression, misplaced: func


Is there a special keyword or other trick to define an inner function?


Solution

  • Not in Godot 3.x. This is supported in Godot 4.0, with the given syntax. This was the proposal: "Add lambda functions in GDScript".

    Addendum: I should mention that the convention is that whatever stars with underscore ("_") is to be considered private. And accessing them externally is at your own risk. This approach has worked well for Python for a long time.

    There are some things you can do…


    "Private" field

    As you would know, you can use setget to have getters and setters which will run every time you access a field externally, or using self. Well, you can write getters and setter that just fail.

    For example:

    var _value setget noset, noget
    
    func noset(_new_value):
        push_error("Invalid access.")
    
    func noget():
        push_error("Invalid access.")
    

    This results in an error when used from another script. Also results in an error when used from the same script with self._value, but not using _value from the same script (without self). Thus we have a "private" field.

    Similarly, by only specifying the setter, we can have a readonly property:

    var value setget noset
    
    func noset(_new_value):
        push_error("Invalid access.")
    

    "Private" method

    Following the same idea, we can store a token in a private field. And use it as access check. We will use a new object, so that it is impossible match it by chance. This is similar to having a monitor (as understood in threading), holding the lock, and doing try_enter… Except without the threading part.

    var _token setget noset, noget
    
    func noset(_new_value):
        push_error("Invalid access.")
    
    func noget():
        push_error("Invalid access.")
    
    func _method(token):
        if token != _token: # try enter
            push_error("Invalid access") # did fail to enter
            return
    
        # did enter
        pass
    
    func _init():
        _token = ClassDB.instance("Object") # hold lock
    

    From the same script, you have access to _token. Thus, you can call the method like this:

    _method(_token)
    

    However, _token is "private", and passing anything else will result in an error. Thus, it will not be possible to use the method from another script.

    This is an improved version of a comment by me2beats. On the proposal "Add public/private access modifiers to GDScript and autocompletion improvements".


    Evaluate code

    Something else you can do is create a script in from code (think "eval"). This is done with the GDScript class.

    You can create your script like this:

    var script = GDScript.new()
    script.source_code = "func run():print('hello world')" # create code at runtime
    script.reload()
    

    Instantiate it like this:

    var script_instance = script.new()
    

    And, of course, you could hold the instance in a "private" field. You might also be interested in FuncRef.

    Then use whatever you declared in there, in this case the run function:

    script_instance.call("run")
    

    See call, get, set.


    If you don't to create code at runtime, you might instead load a script with load. Or use an Inner class.

    If you don't need a whole script, you might use Expression instead.