Godot 基本和进阶语法注意事项(长期更新)

多重继承

Godot 目前不支持多重继承。如果你有一个entity类继承自node,然后你有一个plant类,既要继承entity,又要有Sprite类的功能,这是不行的,只能通过增加child node,或使用singleton来实现。

Add a GDScript Trait system. · Issue #23101 · godotengine/godot (github.com)


Inner Class

类的逻辑分组:如果一个类只对另一个类作用,那么将它嵌入那个另一个类并将两者保持在一起是合乎逻辑的,嵌套这样的"帮助类"使得它们的包更加简化。

内部类extend可以和外部类不一致,如外部extends Spatial, 内部extends Sprite

godot中内部类不可访问外部类成员变量,这会造成cyclic dependence循环引用,可能在4.0会得到更好的解决

想要继承内部类,extends "somefile.gd".SomeInnerClass

不使用extends时,内部类是默认继承reference的,自带内存管理


类的初始化Init

如果覆写了_init()函数,则可以在new()时传入参数并初始化

extends Node2D

func _ready():
var p = Creature.new("eagle")
print (p.name)
var q = Creature.new()
print(q.name)

class Creature extends Resource:
var name = ""
func _init(_name = "creature"):
name = _name
带有参数的_init()在绑定到scene时,使用instance()不可以传入参数(_init参数需要默认值,否则可能报错),packedscene也没有new()函数

如果使用继承,则子类默认以空参数先执行父类_init()


继承和函数重写override

内置函数如_process(delta), _physics_process(delta),会自动调用继承父级的同名函数,顺序是先调用子级,再调用父级

非内置函数(_init, enter_tree等)调用时,不会自动调用父类的同名函数,如

 #creature.gd

extend Node
class_name Creature
func move():
    print("walk")

#bird.gd

extend Creature
func move():
    .move()
    print("chirp")

输出:chirp

如果想要同时输出"walk",则需要加上.move()来手动调用父类函数,调用顺序取决于语句位置。


重写的函数参数的数量和类型要相一致


export var导出变量与singleton(autoload)

export关键字可以允许在编辑器内编辑变量,但要注意--更改的变量是跟随节点的。例如

#npc.gd

export var npc_name = "nobody"

将npc.gd连接到NPC1节点上,更改npc_name = "Lucia",新建另一个节点然后连接npc.gd后npc_name仍然是默认值 "nobody"。当你将 npc.gd设置为autoload时也不例外。因为godot中singleton并不是真的单例,你仍然可以生成它们的多个副本,不同的是你可以直接使用单例名称直接获取singleton节点,不同的singleton并不共享任何变量。

所以通过将单例设置为tool后,你仍然不能在编辑器内通过editor plugin直接手动改变export var。因为此时编辑器内的singleton并不是游戏运行时的singleton。解决办法是通过ResourceSaver保存singleton并直接覆盖原文件,这样更改的export var存储到了singleton文件本身,运行游戏的时候就是更改后的结果了

(编辑器内可以直接获取singleton的节点,即时没有使用tool关键字。即不运行游戏也会自动加载autoload内的节点/script)

示例:配合editor plugin直接测试当前正在编辑的关卡

#Game is autoload/singleton

func _on_Test_pressed():

var current_level:String = get_tree().edited_scene_root.filename

var level = current_level.get_base_dir().trim_prefix("res://level/")

var room_index = int(current_level.get_file().trim_prefix("Room"))

Game.test_respawn_point = [level, room_index, 0]

var ps = PackedScene.new()

ps.pack(Game)

ResourceSaver.save("res://gamecore/GameConfig.tscn", ps)

Editor.get_editor_interface().play_main_scene()

overload重载的简化代替

godot 不具有重载功能,但是可以通过添加参数默认值来实现类似的行为

func add(x, y, z = 0):
    return (x + y + z)

add(5, 6)#return 11
add(1, 2, 3)#return 6
注意此种方式输入的参数位置不能调换,不能省略中间的参数如add(x, z)是不可以的
也可以使用函数当作默认的输入参数

func add(a, b, c=get_children()):
    print(c)
    print(a+b)

回调和信号

signal和callback区别是signal可以在线程内任意时刻触发并执行,而回调是结合上下文的,只能在代码固定位置触发。

Tricks

  • 使用owner代替 get_node("../../..'')获取packedscene根节点
  • 使用meta记录跟随节点的信息,而省略使用script

Comments

Popular posts from this blog

活在当下-从尾巴摇狗的荒谬中解放自我

Metroidvania银河恶魔城游戏开发指南(一)

折衷主义建筑