Godot 引擎实例化多个对象

Godot Engine Multiple Objects Instanced

我在实例化对象时遇到问题。

一方面,对于一个场景中的一个射弹,我有以下脚本:

extends KinematicBody2D

var speed = 200
var life_time = 2
var life_spawn = 0

func _physics_process(delta):
    var collision = move_and_collide(Vector2.UP * delta * speed)
    life_spawn += delta
    if life_spawn > life_time:
        queue_free()
    pass

另一方面,我让玩家在其他场景中使用以下脚本:

extends KinematicBody2D

func _physics_process(delta):
    if Input.is_action_pressed("ui_accept"):
        createLaser()
    pass

func createLaser():
    var laser = preload("res://scenes/space_ship/laser/Laser.tscn")
    var left_fired_laser = laser.instance()
    var right_fired_laser = laser.instance()
    var left_cannon = get_node("Cannons/left_cannon").get_global_position()
    var right_cannon = get_node("Cannons/right_cannon").get_global_position()
    left_fired_laser.position = Vector2(left_cannon.x, left_cannon.y)
    get_parent().call_deferred("add_child", left_fired_laser)
    right_fired_laser.position = Vector2(right_cannon.x, right_cannon.y)
    get_parent().call_deferred("add_child", right_fired_laser)
    pass

问题是对象被实例化了很多次。即使我放了一个 yield() 函数。如果我放置该函数,该对象将等待实例化,但无论如何它都会实例化很多次。

您想要实例化多少个实例?

你说:

The problem is that the object is instantiated a lot of times.

但是多少次算很多?

我会回答想到的案例。此外,其中一些可以组合。

如果这个答案没有涵盖您想要的……您将需要更具体。除此之外,希望您可以考虑这些工具来解决问题:

  • is_action_pressedis_action_just_pressed 之间选择。
  • 使用Timers(实际对象或临时用delta)。
  • 信号(包括但不限于实际Timers的timeout信号)。

说到信号,下面的所有方法都不需要对实例有特殊的了解(除了它是一些 Node2D)。您可以向实例添加自定义信号并连接到它们以了解何时可以创建更多实例。

您可以做的另一件事是保留对实例的引用以询问它们(例如,您可以使用 is_instance_valid)。但是,我在下面介绍了一种使用 tree_exited 信号的方法,我认为这种方法更通用。

您还可以利用 AnimationPlayer 调用方法跟踪。例如,如果您需要在动画的特定帧上添加实例。


每个输入一次

只要按下输入,此代码就会实例化每个物理帧:

func _physics_process(delta:float) -> void:
    if Input.is_action_pressed("ui_accept"):
        createLaser()

如果你只想实例化输入被按下时的第一个物理帧,使用is_action_just_pressed:

func _physics_process(delta:float) -> void:
    if Input.is_action_just_pressed("ui_accept"):
        createLaser()

偶尔一次

我们可以使用与子弹寿命相同的临时计时器策略:

var instance_period:float = 10.0
var instance_elapsed:float = 0.0

func _physics_process(delta:float) -> void:
    instance_elapsed += delta
    if Input.is_action_pressed("ui_accept") and instance_elapsed > instance_period:
        createLaser()
        instance_elapsed = 0.0

只有一次

如果你只想要一个,我们可以持有一个布尔变量来知道我们是否已经实例化:

var did_instance:bool = false

func _physics_process(delta:float) -> void:
    if Input.is_action_pressed("ui_accept") and not did_instance:
        createLaser()
        did_instance = true

只有固定次数

您可以使用整数倒计时:

export var yet_to_instance:int = 10

func _physics_process(delta:float) -> void:
    if Input.is_action_pressed("ui_accept") and yet_to_instance > 0:
        createLaser()
        yet_to_instance -= 1

我将其设为导出变量,因此您可以从检查器面板对其进行编辑。

*此方法与“每次输入一次”(即使用 is_action_just_pressed)结合得很好。当数字为 1 时,您也可以将“Only one ever ever”视为一种特殊情况。


只充一次电

这是一种将临时计时器与固定次数的想法相结合的特殊方法:

var recharge_period:float = 10.0
var recharge_elapsed:float = 0.0

export var max_to_instance:int = 10
onready var yet_to_instance:int = max_to_instance

func _physics_process(delta:float) -> void:
    if Input.is_action_pressed("ui_accept") and yet_to_instance > 0:
        createLaser()
        yet_to_instance -= 1
        # recharge_elapsed = 0.0

    recharge_elapsed += delta
    if recharge_elapsed > recharge_period:
        if yet_to_instance < max_to_instance:
            yet_to_instance += 1

        recharge_elapsed = 0.0

这样一来,您可以创建的实例数量就会增加到最大值。如果你想在有输入时防止该数字增加,你可以取消注释 # instance_elapsed = 0.0您可以将其视为自动重新加载。

此方法与“每次输入一次”(即使用 is_action_just_pressed)结合得很好。或者使用“每隔一段时间”(即临时时间)来限制实例率。


最多活的数量

我们将连接到 tree_exited 信号以更新我们的计数:

const laser = preload("res://scenes/space_ship/laser/Laser.tscn")

export var max_to_instance:int = 20

func _physics_process(delta:float) -> void:
    if Input.is_action_pressed("ui_accept") and max_to_instance > 0:
        createLaser()

func createLaser() -> void:
    createLaserFrom($Cannons/left_cannon)
    createLaserFrom($Cannons/right_cannon)

func createLaserFrom(cannon:Node2D) -> void:
    var fired_laser = laser.instance()

    max_to_instance -= 1
    fire_laser.connect("tree_exited", self, "laser_exited")

    get_parent().add_child(fired_laser)
    fired_laser.global_position = cannon.global_position

func laser_exited() -> void:
    max_to_instance += 1

此方法与“每次输入一次”(即使用 is_action_just_pressed)结合得很好。或者使用“每隔一段时间”(即临时时间)来限制实例率。