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

97 lines
2.5 KiB
GDScript3
Raw Normal View History

2025-02-07 12:52:15 +00:00
@tool
extends TextureRect
signal shaven(progress: float)
@export var area_size := Vector2(40, 20):
set(val):
area_size = val
custom_minimum_size = val
if is_node_ready():
_init_bit_mask()
@export var debug_reload := false:
set(val):
debug_reload = false
if is_node_ready():
_init_bit_mask()
# points inside radius 5 circle
var brush_points := generate_brush_points(5)
var bit_mask: BitMap
func _ready() -> void:
# init bit mask
_init_bit_mask()
# a solid circle brush, center = (0, 0)
func generate_brush_points(radius: int) -> PackedVector2Array:
var points := PackedVector2Array()
# Check each pixel in square area
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))
return points
func _init_bit_mask() -> void:
if not bit_mask:
bit_mask = BitMap.new()
bit_mask.create(area_size)
texture = ImageTexture.create_from_image(bit_mask.convert_to_image())
func _shave(mouse_pos: Vector2) -> void:
if not mouse_pressing:
return
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
bit_mask.set_bitv(pos, true)
updated = true
texture.update(bit_mask.convert_to_image())
if updated:
_send_signal()
func _send_signal() -> void:
var total = area_size.x * area_size.y
var true_bit_count = bit_mask.get_true_bit_count()
var progress = float(true_bit_count) / float(total)
shaven.emit(progress)
var mouse_pressing = false
# 可以刮出区域
func _gui_input(event: InputEvent) -> void:
# 通过鼠标点击拖动刮开区域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(event.position)
accept_event()
elif event.button_index == MOUSE_BUTTON_LEFT and not event.pressed:
mouse_pressing = false
accept_event()
elif event is InputEventMouseMotion and mouse_pressing:
_shave(event.position)
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()