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

113 lines
3.0 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

@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()