Godot 4中基于相邻实例修改纹理的2D农场模拟器问题
2D农场模拟器相邻地块纹理融合问题
我正在制作一款2D农场模拟器,实例化植物时初始采用1格纹理;当在某地块相邻位置创建新的植物实例时,希望切换该地块的Atlas Texture图集纹理,让两者视觉融合更自然。我尝试遍历"Dirt"组内的所有实例,检测周边位置后按需修改纹理,但用var instances = get_tree().get_nodes_in_group("Dirt")循环检测的方法没成功。
以下是我的"Dirt"实例代码:
extends Area2D const Corn = preload("res://assets/Objects/Farming/Corn/corn_growth2.tscn") var timer = Timer.new() var has_seed = false func _on_input_event(viewport, event, shape_idx): if !has_seed: if Input.is_action_pressed("plant_action"): var Corn1 = Corn.instantiate() Corn1.position = Vector2(0,0) self.add_child(Corn1) has_seed = true func cambiarRegion(x: float, y: float) -> void: var texture_atlas: AtlasTexture = $Sprite2D.texture var region_size: Vector2 = Vector2(16, 16) var new_region_rect = Rect2(Vector2(x, y), region_size) texture_atlas.region = new_region_rect $Sprite2D.texture = texture_atlas
(注:附有循环逻辑及实例状态的截图)
问题排查与解决方案
1. 核心问题分析
你当前的代码缺少种植完成后的相邻地块检测逻辑,且直接修改共享的AtlasTexture会导致所有使用该图集的地块纹理同步变化,这是循环检测失效的主要原因。
2. 完整修改方案
步骤1:添加网格坐标属性
给Dirt实例添加网格坐标,用于精准定位相邻地块(假设每个地块尺寸为16x16像素):
extends Area2D const Corn = preload("res://assets/Objects/Farming/Corn/corn_growth2.tscn") var timer = Timer.new() var has_seed = false # 记录当前地块的网格坐标(实例化时自动计算) var grid_pos: Vector2 func _ready(): # 通过全局位置转换为网格坐标 grid_pos = global_position / 16 add_to_group("Dirt")
步骤2:修复种植触发逻辑
避免长按重复种植,改为检测鼠标点击事件:
func _on_input_event(viewport, event, shape_idx): # 仅在鼠标左键按下且未种植时触发 if !has_seed and event is InputEventMouseButton and event.pressed and event.button_index == BUTTON_LEFT: if Input.is_action_pressed("plant_action"): var Corn1 = Corn.instantiate() Corn1.position = Vector2(0,0) self.add_child(Corn1) has_seed = true # 种植完成后立即更新当前及周边地块纹理 update_surrounding_textures()
步骤3:实现相邻检测与纹理更新
添加检测周边地块并更新纹理的逻辑:
func update_surrounding_textures(): # 先更新当前地块纹理 update_self_texture() # 遍历上下左右四个相邻方向 var directions = [Vector2(0, -1), Vector2(0, 1), Vector2(-1, 0), Vector2(1, 0)] for dir in directions: var target_grid = grid_pos + dir # 查找对应网格位置的Dirt实例 for dirt in get_tree().get_nodes_in_group("Dirt"): if dirt.grid_pos == target_grid and dirt.has_seed: dirt.update_self_texture() func update_self_texture(): # 检测四个方向是否有已种植的地块 var left_has = check_neighbor(Vector2(-1, 0)) var right_has = check_neighbor(Vector2(1, 0)) var up_has = check_neighbor(Vector2(0, -1)) var down_has = check_neighbor(Vector2(0, 1)) # 根据相邻情况选择Atlas纹理区域(需根据你的图集排版调整坐标) var atlas_x = 0 var atlas_y = 0 # 示例逻辑:单独地块用(0,0),左右都有则用(1,0),仅左边有则用(2,0),仅右边有则用(3,0) if left_has and right_has: atlas_x = 1 elif left_has: atlas_x = 2 elif right_has: atlas_x = 3 # 上下方向的纹理逻辑可按需扩展 cambiarRegion(atlas_x * 16, atlas_y * 16) func check_neighbor(dir: Vector2) -> bool: # 检测指定方向是否有已种植的Dirt实例 var target_grid = grid_pos + dir for dirt in get_tree().get_nodes_in_group("Dirt"): if dirt.grid_pos == target_grid and dirt.has_seed: return true return false
步骤4:修复AtlasTexture修改逻辑
直接修改原图集会影响所有共享实例,需创建副本后再修改:
func cambiarRegion(x: float, y: float) -> void: var texture_atlas: AtlasTexture = $Sprite2D.texture var region_size: Vector2 = Vector2(16, 16) var new_region_rect = Rect2(Vector2(x, y), region_size) # 创建图集副本避免影响其他实例 var new_atlas = texture_atlas.duplicate() new_atlas.region = new_region_rect $Sprite2D.texture = new_atlas
关键修复点
- 避免共享图集污染:必须使用
duplicate()创建AtlasTexture副本,否则修改会同步到所有使用该图集的地块。 - 精准网格定位:用网格坐标替代全局位置比较,避免浮点误差导致的相邻检测失败。
- 主动触发更新:种植完成后立即触发纹理更新,无需被动等待循环检测。
内容的提问来源于stack exchange,提问作者Ricardo Arcesio Villanueva
相关产品推荐
相关产品推荐

