@tool extends TextureRect signal shaven(progress: float) # points inside radius 9 circle(必须>5) var brush_points := generate_brush_points(9) var area_size := Vector2.ZERO var image: Image # bit_mask is the bottom var bit_mask := BitMap.new() var total_pixels := 0.0 func _ready() -> void: if not texture: printerr("texture not found") return area_size = texture.get_size() image = texture.get_image() texture = ImageTexture.create_from_image(image) bit_mask.create_from_image_alpha(image) total_pixels = bit_mask.get_true_bit_count() # a solid circle brush, center = (0, 0) func generate_brush_points(radius: int) -> PackedVector2Array: var points := PackedVector2Array() # Top points for x in range(-radius, radius + 1): for y in range(-radius, radius + 1): # If point is within circle radius if x * x + y * y <= radius * radius: points.append(Vector2(x, y)) # mid points radius = int(radius / 3.0) for x in range(-radius , radius + 1): for y in range(-radius, radius + 1): # If point is within circle radius if x * x + y * y <= radius * radius: points.append(Vector2(x, y)) # bottom points points.append(Vector2(0, 0)) points.append(Vector2(1, 0)) points.append(Vector2(0, 1)) points.append(Vector2(1, 1)) return points var last_pos := Vector2(-1, -1) func _shave() -> void: if not mouse_pressing: return var mouse_pos := get_local_mouse_position() if last_pos == mouse_pos: return last_pos = mouse_pos var updated = false for point in brush_points: var pos = mouse_pos + point if pos.x < 0 or pos.x >= area_size.x or pos.y < 0 or pos.y >= area_size.y: continue if bit_mask.get_bitv(pos): updated = true var color = image.get_pixelv(pos) as Color color.a = clampf(color.a - 0.15, 0.0, 1.0) if color.a == 0: bit_mask.set_bitv(pos, false) image.set_pixelv(pos, color) if updated: (texture as ImageTexture).update(image) _send_signal() func _send_signal() -> void: var true_bit_count = bit_mask.get_true_bit_count() var progress = 1.0 - float(true_bit_count) / total_pixels shaven.emit(progress) var mouse_pressing = false # 可以刮出区域 func _gui_input(event: InputEvent) -> void: if not is_visible_in_tree(): return # 通过鼠标点击拖动,刮开区域,set_bit 一个 brush_size 的圆形区域为 true # 每次更新后,bit_mask 会更新到 texture 上 # 通过 shaven 信号,通知外部,刮开了多少区域 if event is InputEventMouseButton: if event.button_index == MOUSE_BUTTON_LEFT and event.pressed: # 先设置 mouse_pressing 为 true mouse_pressing = true # 刮开区域 _shave() elif event.button_index == MOUSE_BUTTON_LEFT and not event.pressed: mouse_pressing = false accept_event() elif event is InputEventMouseMotion and mouse_pressing: _shave() accept_event() # elif event is InputEventScreenTouch: # if event.pressed: # mouse_pressing = true # _shave(event.position) # accept_event() # elif not event.pressed: # mouse_pressing = false # accept_event()