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
相关产品推荐
相关产品推荐

