You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Godot 4实现道具系统抽象时调用非静态函数'aquire'报错求助

问题描述

我正在开发一款银河恶魔城(metroidvania)游戏,尝试实现一个道具系统,该系统可接收包含特定函数的脚本,在特定事件触发时调用这些函数。

基类脚本powerup_class.gd代码如下:

extends Node
class_name Powerup

var player

func aquire():
    pass

func in_air():
    pass

func on_jump():
    pass

func update(_delta):
    pass

创建道具时,我编写了如下子类脚本:

extends Powerup

func aquire():
    print("Aquired!")

为让玩家拥有这些道具,我在玩家脚本中使用了以下代码:

@export var powerups : Array[Script] = []

# in _ready() for testing
for i in powerups:
    i.aquire()

# in _process()
for i in powerups:
    i.update(delta)

运行后出现如下错误:

E 0:00:01:0537   player.gd:85 @ _ready(): Can't call non-static function 'aquire' in script.
  <C++ Error>    Condition "!E->value->is_static()" is true. Returning: Variant()
  <C++ Source>   modules/gdscript/gdscript.cpp:868 @ callp()
  <Stack Trace>  player.gd:85 @ _ready()

将所有函数改为静态函数后可以运行,但这样无法修改非静态变量,这会带来问题。请问如何修改才能正常调用脚本中的函数?如果推荐的方式是全部使用静态函数也可以,但我认为这并非良策,是不是我对Godot的抽象实现方式理解有误?


解决方案

核心问题是你直接调用了Script资源的非静态方法——Script是类的定义而非实例,非静态方法必须绑定到类的实例(即对象)才能调用,因为这类方法需要访问实例的成员变量(比如你的player)。

以下是正确的实现方式:

1. 调整玩家脚本的实例化逻辑

将存储脚本的变量与存储实例的变量分离,在_ready()中实例化每个道具脚本并绑定玩家引用:

@export var powerup_scripts : Array[Script] = [] # 存储道具脚本资源
var powerups : Array[Powerup] = [] # 存储实例化后的道具对象

func _ready():
    for script in powerup_scripts:
        # 创建道具实例
        var powerup = script.new()
        # 将当前玩家实例赋值给道具的player变量
        powerup.player = self
        powerups.append(powerup)
        # 调用获取方法
        powerup.aquire()

func _process(delta):
    for powerup in powerups:
        powerup.update(delta)

2. 保留非静态的道具设计

不需要将函数改为静态,你的基类设计完全合理——道具需要维护自身状态、与玩家交互,这些都依赖非静态变量和方法,静态函数会彻底切断道具与实例状态的关联,完全不符合银河恶魔城道具系统的需求。

3. 可选:用节点化方式管理道具

如果道具需要处理节点树交互(比如信号、生命周期函数),可以将实例化的道具添加为玩家节点的子节点,这样道具可以自行处理_process等逻辑,无需玩家脚本手动调用:

@export var powerup_scripts : Array[Script] = []
var powerups : Array[Powerup] = []

func _ready():
    for script in powerup_scripts:
        # 用instantiate创建节点实例(适合继承Node的类)
        var powerup = script.instantiate()
        powerup.player = self
        add_child(powerup)
        powerups.append(powerup)
        powerup.aquire()

此时道具子类可以直接重写_process方法,玩家脚本无需再遍历调用update。

内容的提问来源于stack exchange,提问作者SealProgrammer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 00:05:00