Godot游戏引擎:GraphEdit和GraphNode教程(三)——GraphEdit事件处理

Godot教程——GraphEdit和GraphNode

上文:
Godot游戏引擎:GraphEdit和GraphNode教程(一)—— GraphNode
Godot游戏引擎:GraphEdit和GraphNode教程(二)——GraphEdit
这期开始,会稍微困难一些。

承接上文,我们在做完连接槽和删除槽的功能之后,接着着手实现其他功能。

GraphEdit事件处理

节点被选中事件

顾名思义,当一个节点被选中时触发的事件。
在这里插入图片描述

extends GraphEdit


var selected_node : GraphNode


func _on_GraphEdit_connection_request(from, from_slot, to, to_slot):
   connect_node(from, from_slot, to, to_slot)


func _on_GraphEdit_disconnection_request(from, from_slot, to, to_slot):
   disconnect_node(from, from_slot, to, to_slot)


func _on_GraphEdit_node_selected(node):
   selected_node = node
   print(selected_node.name)

创建一个变量来储存被选中的节点。为了试验,让我们把它的名字print出来。在这里插入图片描述
让我们试着运行一下,结果是我们想要的样子。
在这里插入图片描述

拷贝节点事件

这个事件只在拷贝节点的时候被触发(按下Ctrl + D),让我们来处理这个事件。

func _on_GraphEdit_duplicate_nodes_request():
	var i = selected_node.duplicate()
	self.add_child(i)

上面的代码能够做的事情是把选中的节点拷贝一份,然后添加到自己下面。接下来让我们试一试。
在这里插入图片描述
可以发现,我选中GraphNode2拷贝了多次,明显比右边的要更实,这说明确实拷贝出来了。但是他们都叠在一起了,当你移动它的时候,所有叠在一起的节点都被你移动了。这该怎么办呢?

让我们思考一会。

当你尝试取消选中之后再拖动节点的时候,你发现它被拖出来了。这说明了它们被一起拖动的原因是它们都被选中了,被选中的节点会一起移动

那么我们对上面的代码进行一下修改。

func _on_GraphEdit_duplicate_nodes_request():
		var i = selected_node.duplicate()
		selected_node.selected = false # 取消原件的选中
		self.add_child(i)
		set_selected(i) # 对拷贝后的节点进行选中

添加了两行代码,让我们试试效果。
在这里插入图片描述拷贝之后可以直接拖出来,不用再点空位了。

但是还有一个问题。当我们什么也没有选中的时候,按下Ctrl + D会发生什么?

在这里插入图片描述很显然,我们会报错。因为这个时候,我们的selected_node的值是null,不能调用duplicate()方法。所以我们再次修改一下:

func _on_GraphEdit_duplicate_nodes_request():
	if selected_node != null: # 如果有选中的GraphNode
		var i = selected_node.duplicate()
		selected_node.selected = false # 取消原件的选中
		self.add_child(i)
		set_selected(i) # 对拷贝后的节点进行选中

删除节点事件

这个事件其实并不应该在这里提到,因为它是GraphNode的部分,但是还是在这里讲了吧。

在这里插入图片描述

这些GraphNode的属性中,当我们打开Show_Close属性之后,这个节点的头上多了一个×。

在这里插入图片描述

就像这样。但是我们按下×的时候并没有任何反应,这是因为我们没有对关闭的事件进行处理。
在GraphNode的下面补充一个信号Close_request()
在这里插入图片描述

extends GraphNode


func _ready():
	set_slot(0,true, TYPE_INT, Color.wheat, true, TYPE_INT, Color.wheat)


func _on_GraphNode_close_request():
	self.queue_free()

添加了一行代码。这样我们按下×时,这个节点就可以被删除了。这时你可能就要问了:那么GraphEdit的信号delete_nodes_request()有什么用呢?让我们来查看它的帮助文档。在这里插入图片描述文档里说,当GraphEdit的所有子节点中任意一个GraphNode被试图删除的时候会触发这个信号。那么我们需要用它来处理什么呢?

让我们思考一下这个问题:如果一个GraphNode被选中之后,我把它删掉了,之后按下拷贝会发生什么?

在这里插入图片描述
答案很显然,我们不能拷贝一个刚刚被释放的物体。那么我们怎么办呢?我们把GraphEdit的delete_nodes_request()连接上。

func _on_GraphEdit_delete_nodes_request():
	selected_node = null

就这么简单,我们完成了。只要我们一有节点被删除,就不选中任何节点。这样就不会出现…问题???在这里插入图片描述可是结果仍然报错。那么我在这个方法上添加一个print()函数,看看它什么时候被调用。

func _on_GraphEdit_delete_nodes_request():
	print("Delete_nodes_request")
	selected_node = null

我们发现,当GraphNode拥有焦点并且按下Delete键的时候,会触发这个信号。那么我们想要的单个选择的删除应该怎么做呢?答案是:——当它的×被按下的时候,手动触发它父级的delete_nodes_request()来清除目前的选择。

func _on_GraphNode_close_request():
	if get_parent() is GraphEdit:
		get_parent().emit_signal("delete_nodes_request")
	self.queue_free()

这样。就避免了删除之后拷贝了个寂寞的错误。什么?你问按下Delete键之后却没有反应?为了一劳永逸,我们把GraphNode的queue_free()挪到它的GraphEdit上去,让GraphNode只管发送请求。这个时候,我们发现:delete_nodes_request()这个信号没有提供参数,那么我们就不知道是那些GraphNode节点被选中了。这可怎么办呢?

先思考一会。

我们再次来到GraphNode,看看它的属性列表。在这里插入图片描述

我们看到了Selected属性,很明显这个就是我们要找的属性。我们要做的事情就变成了把所有selected属性为true的节点全部收集起来


func _on_GraphEdit_delete_nodes_request():
	print("Delete_nodes_request")
	for i in range(get_child_count()):
		if get_child(i) is GraphNode:
			if get_child(i).selected:
				get_child(i).queue_free()
	selected_node = null


func _on_GraphNode_close_request():
	if get_parent() is GraphEdit:
		get_parent().emit_signal("delete_nodes_request")

我们再次重写了GraphEdit的删除节点方法和GraphEdit的关闭方法。现在,所有的删除GraphNode的请求全部移交到GraphEdit的这个方法来解决了。

当我们再次运行的时候,删除一个没有问题,框选删除也没有问题…等等!
在这里插入图片描述当我们选中的和要删除的不是同一个节点的时候,会出现问题。
在这里插入图片描述按下×之后,选中的节点被删掉了,而想删掉的节点却还在。这怎么办呢?于是我们对GraphNode的关闭事件又做出了些更改。

func _on_GraphNode_close_request():
	if get_parent() is GraphEdit:
		get_parent().set_selected(self) # 选中自己
		get_parent().emit_signal("delete_nodes_request")

这样就完美了。当按下×的时候,其他的节点不会被选中,只有自己会被安安心心的选中删除;当按下delete的时候,GraphNode的close_request()不会被调用,被选中的东西统统会被删除。

接下来,让我们整理一下之前的代码。我们使用了一个for循环来获取所有被多选的节点。那么为了方便复用,我们把这个步骤剥离出来,单独封装成一个方法。

func get_week_selected_nodes():
	var will_return : Array
	for i in range(get_child_count()):
		if get_child(i) is GraphNode:
			if get_child(i).selected:
				will_return.append(get_child(i))
	return will_return

注解:这里我管它叫做week_selected的原因是多选并不触发GraphEdit的node_selected()事件。


现在GraphEdit整体的代码就是这样子了:

extends GraphEdit


var selected_node : GraphNode


func _on_GraphEdit_connection_request(from, from_slot, to, to_slot):
	connect_node(from, from_slot, to, to_slot)


func _on_GraphEdit_disconnection_request(from, from_slot, to, to_slot):
	disconnect_node(from, from_slot, to, to_slot)


func _on_GraphEdit_node_selected(node):
	selected_node = node
	print(selected_node.name)


func _on_GraphEdit_duplicate_nodes_request():
	if selected_node != null:
		var i = selected_node.duplicate()
		selected_node.selected = false # 取消原件的选中
		self.add_child(i)
		set_selected(i) # 对拷贝后的节点进行选中


func _on_GraphEdit_delete_nodes_request():
	print("Delete_nodes_request")
	var week_selected_nodes = get_week_selected_nodes()
	for i in range(week_selected_nodes.size()):
		week_selected_nodes[i].queue_free()
	selected_node = null

func get_week_selected_nodes():
	var will_return : Array
	for i in range(get_child_count()):
		if get_child(i) is GraphNode:
			if get_child(i).selected:
				will_return.append(get_child(i))
	return will_return

小憩一下

休息与工作的关系,正如眼睑与眼睛的关系。——泰戈尔

让我们站起来活动一会,看看周围的世界。喝口水,放松下心情。


进行修改

休息完了,我们继续。先来修改一下之前拷贝节点事件的处理,我们想要既可以框选多个节点并拷贝,又可以仅仅选择一个节点拷贝。那么,我们开工。

func _on_GraphEdit_duplicate_nodes_request():
	var week_selected_nodes = get_week_selected_nodes()
	for i in range(week_selected_nodes.size()): 
		var p = week_selected_nodes[i].duplicate()
		week_selected_nodes[i].selected = false # 这步的原因和之前需要取消原件选中的原因一样。
		self.add_child(p)

注解:这里不需要像之前那样先判断“是否一个都没有选中”是因为如果一个节点都没有被选中,那么week_selected_node.size()的返回值就是0,那么for循环会执行0次,也就是不执行。

在这里插入图片描述可以多选拷贝了。

复制节点事件

在这里插入图片描述
这个事件是在按下Ctrl + C之后触发的事件。我们分析一下需求:当我们按下Ctrl + C复制的时候,我们需要有一个变量来存储所有选中的节点。等到按下Ctrl + V粘贴的时候,再把这个存储的东西全部粘贴出来,这个机制就叫做剪贴板

注解:复制粘贴和我这里提到的“拷贝”的区别在于复制粘贴可以复制一次粘贴N次,而拷贝只是生成了一份,再次按下Ctrl + D的时候生成的是新选中的那些(如果没做其他操作的话是刚刚拷贝出来的东西)的复制品。

既然分析完了需求,让我们开始着手实现。先创建一个Array,叫做copied_nodes,用来存储复制的节点们。然后只有一句话:

func _on_GraphEdit_copy_nodes_request():
	copied_nodes = get_week_selected_nodes()

这个复制功能就完成了。

粘贴节点事件

在这里插入图片描述
接着让我们来处理粘贴节点事件。话不多说,上代码。

func _on_GraphEdit_paste_nodes_request():
	for i in range(copied_nodes.size()):
		var p = copied_nodes[i].duplicate()
		copied_nodes[i].selected = false # 与之前同理,取消原来(被复制)那些节点的选择。
		self.add_child(p)

完成了。

小结

让我们回顾一下,这期我们完善了GraphEdit的主要事件处理,现在我把成品的代码放在这里。

GraphEdit

extends GraphEdit


var selected_node : GraphNode
var copied_nodes : Array

func _on_GraphEdit_connection_request(from, from_slot, to, to_slot):
	connect_node(from, from_slot, to, to_slot)


func _on_GraphEdit_disconnection_request(from, from_slot, to, to_slot):
	disconnect_node(from, from_slot, to, to_slot)


func _on_GraphEdit_node_selected(node):
	selected_node = node
	print(selected_node.name)


func _on_GraphEdit_duplicate_nodes_request():
#	if selected_node != null:
#		var i = selected_node.duplicate()
#		selected_node.selected = false # 取消原件的选中
#		self.add_child(i)
#		set_selected(i) # 对拷贝后的节点进行选中
	
	var week_selected_nodes = get_week_selected_nodes()
	for i in range(week_selected_nodes.size()):
		var p = week_selected_nodes[i].duplicate()
		week_selected_nodes[i].selected = false # 这步的原因和之前需要取消原件选中的原因一样。
		self.add_child(p)
	


func _on_GraphEdit_delete_nodes_request():
	print("Delete_nodes_request")
	var week_selected_nodes = get_week_selected_nodes()
	for i in range(week_selected_nodes.size()):
		week_selected_nodes[i].queue_free()
	selected_node = null

func get_week_selected_nodes():
	var will_return : Array
	for i in range(get_child_count()):
		if get_child(i) is GraphNode:
			if get_child(i).selected:
				will_return.append(get_child(i))
	return will_return


func _on_GraphEdit_copy_nodes_request():
	copied_nodes = get_week_selected_nodes()


func _on_GraphEdit_paste_nodes_request():
	for i in range(copied_nodes.size()):
		var p = copied_nodes[i].duplicate()
		copied_nodes[i].selected = false # 与之前同理
		self.add_child(p)

GraphNode

extends GraphNode


func _ready():
	set_slot(0,true, TYPE_INT, Color.wheat, true, TYPE_INT, Color.wheat)


func _on_GraphNode_close_request():
	if get_parent() is GraphEdit:
		get_parent().set_selected(self)
		get_parent().emit_signal("delete_nodes_request")

各位下期再见。

发布了3 篇原创文章 · 获赞 2 · 访问量 166

猜你喜欢

转载自blog.csdn.net/weixin_44222082/article/details/105133239