148 lines
4.3 KiB
GDScript
148 lines
4.3 KiB
GDScript
extends Sprite2D
|
|
|
|
@onready var tile_group = get_tree().get_first_node_in_group("Tile_Group")
|
|
var mask_viewport: SubViewport
|
|
var mask_texture: ViewportTexture
|
|
var is_queued_for_update := false
|
|
var camera: Camera2D
|
|
var last_camera_position := Vector2.ZERO
|
|
var last_camera_offset := Vector2.ZERO # 新增:跟踪相机偏移量
|
|
|
|
class MaskRenderer extends Node2D:
|
|
var is_queued_for_update := false
|
|
|
|
func _draw():
|
|
var tile_group = get_tree().get_first_node_in_group("Tile_Group")
|
|
if not tile_group or tile_group.tilemaplayers.is_empty():
|
|
return
|
|
|
|
var reference_layer = tile_group.tilemaplayers[0]
|
|
|
|
for info in tile_group.mask_tile_info:
|
|
var base_pos = reference_layer.map_to_local(info.position)
|
|
|
|
for shape in info.shapes:
|
|
match shape.type:
|
|
"polygon":
|
|
var transformed_points = PackedVector2Array()
|
|
for point in shape.points:
|
|
transformed_points.append(base_pos + point)
|
|
draw_colored_polygon(transformed_points, Color.WHITE)
|
|
|
|
"rect":
|
|
var pos = base_pos - shape.size/2
|
|
var rect = Rect2(pos, shape.size)
|
|
draw_rect(rect, Color.WHITE, true)
|
|
|
|
func queue_update():
|
|
if not is_queued_for_update:
|
|
is_queued_for_update = true
|
|
RenderingServer.frame_post_draw.connect(_on_frame_post_draw, CONNECT_ONE_SHOT)
|
|
|
|
func _on_frame_post_draw():
|
|
is_queued_for_update = false
|
|
queue_redraw()
|
|
|
|
func _ready():
|
|
# 设置 SubViewport
|
|
_setup_viewport()
|
|
# 设置着色器
|
|
_setup_shader()
|
|
# 订阅信号
|
|
tile_group.mask_tiles_updated.connect(_on_mask_tiles_updated)
|
|
# 寻找并跟踪相机
|
|
_find_and_track_camera()
|
|
|
|
await RenderingServer.frame_post_draw
|
|
_update_viewport_for_camera()
|
|
material.set_shader_parameter("mask_texture", mask_viewport.get_texture())
|
|
|
|
func _setup_viewport():
|
|
mask_viewport = SubViewport.new()
|
|
mask_viewport.transparent_bg = true
|
|
mask_viewport.render_target_update_mode = SubViewport.UPDATE_ALWAYS
|
|
add_child(mask_viewport)
|
|
|
|
var mask_renderer = MaskRenderer.new()
|
|
mask_viewport.add_child(mask_renderer)
|
|
|
|
func _setup_shader():
|
|
var shader_material = ShaderMaterial.new()
|
|
shader_material.shader = preload("res://Char/Char_Components/Blood/blood_mask.gdshader")
|
|
material = shader_material
|
|
|
|
func _find_and_track_camera():
|
|
# 寻找场景中的相机
|
|
var level = get_tree().get_first_node_in_group("Level")
|
|
if level:
|
|
camera = level.get_node_or_null("Player/Camera2D")
|
|
if camera:
|
|
last_camera_position = camera.get_screen_center_position()
|
|
|
|
func _process(_delta):
|
|
if not camera:
|
|
return
|
|
|
|
var current_pos = camera.get_screen_center_position()
|
|
var current_offset = camera.offset
|
|
|
|
# 检查相机位置或偏移量是否发生变化
|
|
if current_pos != last_camera_position or current_offset != last_camera_offset:
|
|
last_camera_position = current_pos
|
|
last_camera_offset = current_offset
|
|
_update_viewport_for_camera()
|
|
|
|
func _update_viewport_for_camera():
|
|
if not camera:
|
|
return
|
|
|
|
# 获取相机的视口大小和缩放
|
|
var viewport_rect = get_viewport_rect()
|
|
var zoom = camera.zoom
|
|
var screen_size = viewport_rect.size
|
|
|
|
# 计算实际需要的视口大小(考虑缩放)
|
|
var actual_size = screen_size / zoom
|
|
|
|
# 更新 SubViewport 大小
|
|
mask_viewport.size = actual_size
|
|
|
|
# 获取相机的全局位置和偏移量
|
|
var camera_global_pos = camera.get_screen_center_position()
|
|
var camera_offset = camera.offset
|
|
|
|
# 计算实际的相机中心位置(考虑偏移量)
|
|
var effective_camera_center = camera_global_pos + camera_offset * zoom
|
|
|
|
# 计算视口的左上角位置
|
|
var top_left = effective_camera_center - (actual_size / 2)
|
|
|
|
# 更新 MaskRenderer 的位置以对齐相机
|
|
if mask_viewport.get_child(0) is MaskRenderer:
|
|
var mask_renderer = mask_viewport.get_child(0)
|
|
mask_renderer.position = -top_left
|
|
|
|
# 触发一次更新
|
|
_update_mask()
|
|
|
|
func _update_mask():
|
|
if mask_viewport and mask_viewport.get_child(0) is MaskRenderer:
|
|
var mask_renderer = mask_viewport.get_child(0) as MaskRenderer
|
|
mask_renderer.queue_update()
|
|
|
|
func _on_mask_tiles_updated():
|
|
if not is_queued_for_update:
|
|
is_queued_for_update = true
|
|
RenderingServer.frame_post_draw.connect(_update_mask_deferred, CONNECT_ONE_SHOT)
|
|
|
|
func _update_mask_deferred():
|
|
is_queued_for_update = false
|
|
_update_mask()
|
|
|
|
func cleanup():
|
|
if tile_group and tile_group.mask_tiles_updated.is_connected(_on_mask_tiles_updated):
|
|
tile_group.mask_tiles_updated.disconnect(_on_mask_tiles_updated)
|
|
|
|
func _exit_tree():
|
|
cleanup()
|