Is this the right/best approach for tracing a sprite? (Letter Tracing Game)

Godot Version

3.5.3.stable.official

Question

Hello, so I’m currently working on making a very basic tracing game for kids. They will click and drag to trace the object and then once the object is 75% filled, it autofills the rest(replaces the sprite with a colored in version). Since I recently just started using Godot I want to make sure I’m going about this the right way and not over complicating my scene.

Scene Structure:

Parent: Node2D
Child of Node2D: Sprite of the object that will be traced
Child of Node2D: Colored in version of traced Sprite
Child of Node2D: Area2D with a CollisionPolygon2D child that outlines the object being traced.
Child of Node2D: Area2D with a CollisionPolygon2D child that outlines the area where tracing should not be allowed. Example use case would be the inner triangle of the letter A for example

Scene Script:

extends Node2D

var drawing = false
var within_bounds = false
var line_points = []
var current_line = null

func _ready():
# Clear existing points from Line2D
$TraceLine.clear_points()

func _input(event):
if event is InputEventMouseButton:
if event.button_index == BUTTON_LEFT:
drawing = event.pressed

if drawing and within_bounds:
start_line(event.position)
else:
finish_line()

func _process(_delta):
if drawing and within_bounds:
update_line(get_global_mouse_position())

func start_line(position):
line_points.clear()
line_points.append(position)

current_line = Line2D.new()
current_line.width = 75
current_line.joint_mode= Line2D.LINE_JOINT_BEVEL
current_line.begin_cap_mode = Line2D.LINE_CAP_ROUND
current_line.end_cap_mode = Line2D.LINE_CAP_ROUND
current_line.points = line_points
add_child(current_line)
#$TraceLine.points = line_points

func update_line(position):
if line_points.size() > 0 and current_line:
line_points.append(position)
current_line.points = line_points
#$TraceLine.points = line_points

func finish_line():
drawing = false
current_line = null
line_points.clear()

func _on_UnallowedArea_mouse_entered():
within_bounds = false
print("Mouse is outside the collision polygon")

func _on_UnallowedArea_mouse_exited():
within_bounds = true
print("Mouse is inside the collision polygon")

func _on_AllowedArea_mouse_entered():
within_bounds = true
print("Mouse is inside the collision polygon")

func _on_AllowedArea_mouse_exited():
within_bounds = false
print("Mouse is outside the collision polygon")

Issues:


If a user clicks near the edge of the letter and starts tracing, due to the width of Line2D, it expands past the sprite. Issue will occur no matter the width if the user starts at the edge


If a user starts within the sprite then moves out and back in, the line continues

With all that explained I have a few questions:

  • What would the best approach be to check if the sprite has been filled X%?

  • In regards to my 2 image issues, I feel like this might be a kill 2 birds with 1 stone scenario. Are there any nodes/other tools I can use to keep the tracing within the Sprite or does this all have to be done through scripting? If it needs to be done through scripting, any ideas on how to keep the Line2D within the sprite or should I use a different node to trace with? There is a CollisionPolygon2D that outlines edge the sprite if that helps

You can setup an invisible mask with a sprite2d to hide anything outside the letter.

Need to end the line when the mouse leaves and start a new one when it comes back in.

This could be tricky. One option is to place everything in a subviewport. As you draw you can get_image from the viewport and test the color and count the pixels. This will potentially be slow, but accurate.

Another is to have hidden quantized area blocks that as you draw over they signal that they were drawn. Example: 10 blocks, each representing 10%.

I’m sure that could be other ways. Like a compute shader version of the first option to speed up processing.