从Unity到Godot:用Signal去实例化

本文参考了《Instancing with signals》

前言

在Unity中,运行时实例化GameObject,如果不显式指定它的父节点,系统会默认把它放到场景的顶层,而在Godot中,你则必须显式指定每一个节点的父节点。指定谁作父节点? 这个问题貌似不难回答,但是要兼顾到开发及测试过程中节点结构的各种不确定性,实现起来就需要点技巧了,比如《Instancing with signals》中给的这个例子:

一个会旋转的角色Player发射出的子弹,应该以谁作父节点?下图所示效果
在这里插入图片描述

  • 方法一:以Player作父节点?
    打出去的子弹会继承Player的旋转属性,显然是不对的
    在这里插入图片描述
  • 方法二:以Player的父节点作父节点?

示意代码

var bullet_instance = Bullet.instance()
get_parent().add_child(bullet_instance)

这种做法貌似是没问题的,但别忘了,Godot的每一个场景都是可以单独运行的,尤其是在开发测试时,如果你想单独测试Player场景, 由于get_parent()找不到父节点而必然导致崩溃。还有就是在开发过程中如果节点树的结构发生变化,你无法保证Player的父节点永远适合做Bullet 的父节点。

那么如何在不改变代码的情况下,灵活地指定父节点呢?

  • 方法三: 拗口令:Signal让适合作父节点的节点去作父节点(这里信号要在编辑器中链接)

Godot 文档里提供了一个这样的小技巧,听起来拗口,实则很容易理解,它体现了一种依赖倒置的策略,即:想发射子弹的时候,让Player发出 shoot信号,而让当时适合做Bullet父节点的节点接收信号并实例化Bullet,然后把它作为自己的子节点。
示意代码如下:

Player的代码

extends Sprite

signal shoot(bullet, direction, location)

var Bullet = preload("res://Bullet.tscn")

func _input(event):
    if event is InputEventMouseButton:
        if event.button_index == BUTTON_LEFT and event.pressed:
            emit_signal("shoot", Bullet, rotation, position)

func _process(delta):
    look_at(get_global_mouse_position())

信号接收者处理shoot信号的代码

func _on_Player_shoot(Bullet, direction, location):
    var b = Bullet.instance()
    add_child(b)
    b.rotation = direction
    b.position = location
    b.velocity = b.velocity.rotated(direction)

这样,各种场合下都不需要因为更换Bullet的父节点而修改代码了,更加符合开闭原则

总结
凡事有利有弊,文档中提供的这种方法须在编辑器中连接信号,所以测试完毕后千万不要忘了断开连接。

发布了261 篇原创文章 · 获赞 134 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/hello_tute/article/details/103643214