xiandie/scene/entity/ux/刮刮乐.gd

113 lines
3.0 KiB
GDScript3
Raw Normal View History

@tool
extends TextureRect
2025-02-07 12:52:15 +00:00
signal shaven(progress: float)
2025-06-17 03:39:55 +00:00
# points inside radius 9 circle(必须>5)
var brush_points := generate_brush_points(9)
var area_size := Vector2.ZERO
var image: Image
2025-06-17 03:39:55 +00:00
# bit_mask is the bottom
var bit_mask := BitMap.new()
var total_pixels := 0.0
2025-02-07 12:52:15 +00:00
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()
2025-06-17 03:39:55 +00:00
# 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))
2025-06-17 03:39:55 +00:00
# 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
2025-06-17 03:39:55 +00:00
var last_pos := Vector2(-1, -1)
func _shave() -> void:
if not mouse_pressing:
return
var mouse_pos := get_local_mouse_position()
2025-06-17 03:39:55 +00:00
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
2025-06-17 03:39:55 +00:00
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
2025-02-07 12:52:15 +00:00
# 可以刮出区域
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()