Godot 4.1报错:为Nil类型设置texture传入AtlasTexture问题排查
Godot Inventory系统报错:Invalid set index 'texture' (on base: 'Nil')
问题场景
在Godot开发Inventory系统,架构包含:
- Slot场景:单个物品槽的UI与逻辑
- Slot Block场景:承载Slot网格的容器
- Inventory Screen场景:带Tab容器的库存主界面
单独运行Slot Block场景无异常,但运行Inventory Screen场景时,Slot脚本触发错误:Invalid set index 'texture' (on base: 'Nil') with value of type 'AtlasTexture'。测试库存含20个SlotData槽位,其中两个配置物品数据,报错发生在第一个有数据的槽位(口粮)。
相关代码
Slot场景脚本
extends PanelContainer @onready var item_icon: TextureRect = $MarginContainer/ItemIcon @onready var quantity_label: Label = $QuantityLabel func set_slot_data(slot_data: SlotData) -> void: var item_data = slot_data.item_data item_icon.texture = item_data.item_icon tooltip_text = "%s\n%s" % [item_data.name, item_data.description] if slot_data.quantity > 1: quantity_label.text = "x%s" % slot_data.quantity quantity_label.show()
Slot Block场景脚本
extends MarginContainer const SLOT = preload("res://inventory/slot.tscn") func _ready() -> void: pass func set_slots_block_data(slot_datas: Array[SlotData], item_type_filter: ItemData.ITEMTYPE) -> void: var passed_inv_data: Array[SlotData] = slot_datas #optional item type filter if item_type_filter and item_type_filter != ItemData.ITEMTYPE.NULL: match item_type_filter: ItemData.ITEMTYPE.EQUIPPABLE: passed_inv_data = passed_inv_data.filter(func(x): return x.item_data.item_type == ItemData.ITEMTYPE.EQUIPPABLE) ItemData.ITEMTYPE.FOOD: passed_inv_data = passed_inv_data.filter(func(x): return x.item_data.item_type == ItemData.ITEMTYPE.FOOD) ItemData.ITEMTYPE.USABLE: passed_inv_data = passed_inv_data.filter(func(x): return x.item_data.item_type == ItemData.ITEMTYPE.USABLE) ItemData.ITEMTYPE.QUEST: passed_inv_data = passed_inv_data.filter(func(x): return x.item_data.item_type == ItemData.ITEMTYPE.QUEST) ItemData.ITEMTYPE.MISC: passed_inv_data = passed_inv_data.filter(func(x): return x.item_data.item_type == ItemData.ITEMTYPE.MISC) populate_item_grid(passed_inv_data) func populate_item_grid(slot_datas: Array[SlotData]) -> void: var item_grid = $"ScrollContainer/MarginContainer/ItemGrid" var children = item_grid.get_children() if children: for child in children: child.queue_free() for slot_data in slot_datas: var slot = SLOT.instantiate() slot.set_slot_data(slot_data) item_grid.add_child(slot)
Inventory Screen场景脚本
extends PanelContainer @onready var inventory_tab_container: TabContainer = $HBoxContainer/MarginContainer/VBoxContainer/InventoryTabContainer const SLOTS_BLOCK = preload("res://inventory/inventory_slots_block.tscn") var test_inv = preload("res://test_objects/test_inventory.tres") var slotdatas = test_inv.slot_datas # Called when the node enters the scene tree for the first time. func _ready() -> void: set_inventory_panel(slotdatas) func set_inventory_panel(inv: Array[SlotData]) -> void: var children = inventory_tab_container.get_children() for c in children: self.remove_child(c) c.queue_free() if inv: var inv_panel_all = SLOTS_BLOCK.instantiate() inv_panel_all.set_slots_block_data(inv, ItemData.ITEMTYPE.NULL) inventory_tab_container.add_child(inv_panel_all)
ItemData类
extends Resource class_name ItemData const MAX_VALUE: int = 1000000 #name of the item @export var name: String = "" #description for the item @export_multiline var description: String = "" #value in currency for a single unit of the item @export_range(0,MAX_VALUE) var value: int = 0 #determines max hit points of item @export var max_hp: int = 1 #determines if item is indestructable @export var indestructable: bool = false #item type of item @export var item_type: ITEMTYPE = ITEMTYPE.MISC: set = set_item_type #item icon @export var item_icon: AtlasTexture #determines if an item is stackable @export var stackable: bool = false enum ITEMTYPE { EQUIPPABLE, FOOD, USABLE, QUEST, MISC, NULL, } func set_item_type(new_value: ItemData.ITEMTYPE): item_type = new_value func decrease_value(change: int = 0): var prev_value = value value = value - change func increase_value(change: int = 0): var prev_value = value value = value + change
InventoryData类
extends Resource class_name InventoryData @export var slot_datas: Array[SlotData]
SlotData类
extends Resource class_name SlotData const MAX_STACK_SIZE:int = 99 @export var item_data: ItemData @export_range(1, MAX_STACK_SIZE) var quantity: int = 1: set = set_quantity func set_quantity(value: int) -> void: quantity = value if quantity > 1 and not item_data.stackable: quantity = 1 push_error("%s is not stackable, setting quantity to 1" % item_data.name)
问题原因与修复
核心原因
报错显示item_icon为Nil,因为Godot中@onready变量只有当节点加入场景树后才会完成赋值。但当前代码中,先调用slot.set_slot_data(slot_data),之后才将Slot节点添加到场景树——此时item_icon还未初始化,自然无法设置texture属性。
修复方案
1. 调整Slot节点的添加与数据设置顺序
修改Slot Block脚本中的populate_item_grid函数,先将Slot节点加入场景树,再调用set_slot_data:
func populate_item_grid(slot_datas: Array[SlotData]) -> void: var item_grid = $"ScrollContainer/MarginContainer/ItemGrid" var children = item_grid.get_children() if children: for child in children: child.queue_free() for slot_data in slot_datas: var slot = SLOT.instantiate() item_grid.add_child(slot) # 先加入场景树,触发@onready初始化 slot.set_slot_data(slot_data) # 再设置数据
2. 修正Inventory Screen的子节点移除逻辑
原代码中用self.remove_child(c)移除TabContainer的子节点,这是错误的,应该从inventory_tab_container中移除:
func set_inventory_panel(inv: Array[SlotData]) -> void: var children = inventory_tab_container.get_children() for c in children: inventory_tab_container.remove_child(c) # 从正确的父节点移除 c.queue_free() if inv: var inv_panel_all = SLOTS_BLOCK.instantiate() inventory_tab_container.add_child(inv_panel_all) inv_panel_all.set_slots_block_data(inv, ItemData.ITEMTYPE.NULL)
3. 可选:添加空值防护(增强鲁棒性)
在Slot脚本的set_slot_data中加入空值判断,避免因节点未初始化或数据缺失导致报错:
func set_slot_data(slot_data: SlotData) -> void: # 如果节点未加入场景树,延迟执行数据设置 if not is_inside_tree(): call_deferred("set_slot_data", slot_data) return var item_data = slot_data.item_data # 确保item_data和item_icon都不为空 if item_data and item_icon: item_icon.texture = item_data.item_icon tooltip_text = "%s\n%s" % [item_data.name, item_data.description] # 确保quantity_label不为空 if slot_data.quantity > 1 and quantity_label: quantity_label.text = "x%s" % slot_data.quantity quantity_label.show()
内容的提问来源于stack exchange,提问作者Sarah Coulston
相关产品推荐
相关产品推荐

