tutorial_scripting_2 - seraph526/godot-se GitHub Wiki

脚本 (继续)

处理

Godot中的一些行为是通过callbacks或虚函数触发的,所以不需要写一直监测运行的代码.另外,许多事情可以由动画师来完成.

但是,通常还是要在每一帧运行一个脚本的情况.有两种运行模式,空闲处理和固定处理.

空闲处理通过 Node.set_process() 函数激活.激活后, Node.set_process() 每帧都会执行.


func _ready():
	set_process(true)

func _process(delta):
	[dosomething..]

delta参数描述从上次调用_process()到现在过去的时间(以秒为单位的浮点类型) 固定处理类似,唯一的要求是要和物理引擎同步. 简单的测试这点的方法是创建一个只有一个Lable节点的场景,代码如下:

extends Label

var accum=0

func _ready():
	set_process(true)

func _process(delta):
	accum+=delta
	set_text(str(accum))

这会显示一个按秒增加的计算器.

节点可以被添加到组中(每个节点下,你想要多少都可以).这对于管理大场景来说是简单而且有效的功能.有两种方法做这个,第一种方法是通过UI,从组按扭实现:


func _ready():
	add_to_group("enemies")

这种方式,如果玩家潜行到秘密基地,如果被发现,所有的组中的敌人都会发出警报.通过调用SceneMainLoop.call_group():


func _on_discovered():

	get_scene().call_group(0,"guards","player_was_discovered")

上述方法调用组中"guards"的每一位成员的函数"player_was_discovered".或者,通过调用SceneMainLoop.get_nodes_in_group()得到"guards"节点全部列表:


var guards = get_scene().get_nodes_in_group("guards")

关于SceneMainLoop的更多知识点将在后面讲述。

消息系统

Godot有一个消息系统。通常这个系统不会在脚本层使用,因为它太底层了,虚函数提供了大部门接口,但知道它们的存在还是必要的。在你的脚本中填加一个Object._notification()函数:


func _notification(what):
     if (what==NOTIFICATION_READY):
        print("This is the same as overriding _ready()...")
     elif (what==NOTIFICATION_PROCESS):     
        var delta = get_process_time()
        print("This is the same as overriding _process()...")
        

文档class list中列出了所有有接收到的消息。但是,再强调一下,大部分的函数都提供了简单的重写方式。

可重写函数

如前面提到的,最好使用这些函数。节点提供了许多有用的重写函数,如:


func _enter_scene():
   pass # 当节点进入激活的场景时,此函数被调用。此时子节点还没有被加载到当前场景。通常,大部分情况下,最好使用_ready()函数。

func _ready():
   pass # 此在_enter_scene函数后调用,但是它能保证所有的子节点都被正常加载,都能被函数调用

func _exit_scene():
   pass # 当节点退出当前激活场景时,调用此函数。此时,所有子节点已经全部退出当前场景。
   
func _process(delta):
   pass # 当set_process()被激活时,此函数每帧都调用。

func _fixed_process(delta):
   pass # 当set_fixed_process()被激活时,此函数每物理帧调用。

func _paused():
   pass #当游戏暂停时调用,调用后,节点不再接收任何callback进程

func _unpaused():
   pass # 当游戏继续时调用

创建节点

由代码创建节点,只要调用.new()方法,(像其他的基于类的数据类型一样),如:

var s
func _ready():
   s = Sprite.new() # create a new sprite!
   add_child(s) #add it as a child of this node

要删除节点,在场景内或场景外,一定要使用free()

func _someaction():
   s.free() # immediately removes the node from the scene and frees it

当节点释放后,所有的子节点也会被释放。因此,手动删除节点要容易的多。只是释放根节点,所有子节点也会被同时释放。

但是,经常会出现我们删除的节点处于锁定状态,就是说,这个节点正在发出信息或要访问一个函数。这会导致游戏崩溃。运行Godot的debugger模式,经常会抓取到这种情况,并提出警告。

最安全的删除节点的方法是使用queue_free代替free().它将安全的删除处于闲置状态的节点。

func _someaction():
   s.queue_free() # remove the node and delete it while nothing is happening

初始化场景

从代码初始化场景非常简单,有两种方式。第一种是从磁盘加载场景。

var scene = load("res://myscene.scn") # will load when the script is instanced

有些时候,使用preload会更方便,因为,它在解析时执行。

var scene = preload("res://myscene.scn") # will load when parsing the script

但是,场景仍不是一个包含子节点的节点。它是一种打包的特殊资源 PackedScene.要创建实际的节点,要执行PackedScene.instance()函数。它将返回可以加载到场景中的节点树。

var node = scene.instance()
add_child(node)

两步执行的优势是打包的节点可以保持加载和准备使用,所以可以按照需求用来创建更多的实例。这一点特别有用,如,要在场景中快速地实例化几个敌人,子弹等。