97 lines
2.5 KiB
GDScript3
97 lines
2.5 KiB
GDScript3
|
@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()
|