How do I tell when a Line2D has been clicked or moused over

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By TheSecurityDev

The title explains it, but how would I go about checking if a Line2D has been clicked or moused over. (If I can do mouse over, then I can check if clicked also). I tried adding an Area2D as a child, with a CollisionShape2D as a child of that, and then setting the shape to be a SegmentShape2D and connecting the signals for on_mouse_entered, but that wouldn’t work. So I tried using a RectangleShape2D instead, and now the signals work, but I’m having trouble getting it to change shape to fit the Line2D. I’m just wondering if there’s an easier way to do this. I haven’t found much information online about this, so maybe it’s not a common thing, but you should be able to do it somehow.

:bust_in_silhouette: Reply From: Zylann

I think Segment shapes are not clickable anyways because they have no thickness. They are mostly used for platforms, maybe.
Maybe you could use multiple rectangle shapes (since they ave a thickness and you can position them the way you want), or even a CollisionPolygon roughly matching the line?
Another way would be to use line intersection algorithms but it gets quite involved for scripting. A shortcut might exist depending on what your line is (short and thick? Long and thin? Is it a f(x) statistics drawing? Is it a path generated from a Curve2D? Can collisions afford to be slightly different?)

That’s what I was thinking about the Segement shape, so that’s why I tried a rectangle. I guess I’ll try to use a CollisionPolygon.

My lines are quite thin, and can be any length. I am making a circuit simulator, to simulate logic gates, make your own computer, etc, and I want to be able to select the wire connections to delete them. It will be difficult to even select one, so I want the mouse detection area to be slightly wider than the wire, and I want to maybe change the color when the mouse passes over. I also want to be able to drag a rectangle to be able to select multiple components.

TheSecurityDev | 2019-12-02 23:02

:bust_in_silhouette: Reply From: TheSecurityDev

Okay, was able to get it working using a Rectangle Shape. I put the shape at the midpoint of the line (since that’s where the origin of the rectangle is), calculate extents x and y, and then set the rotation of the rectangle. The problem I was having was start_pos.angle_to(end_pos) wasn’t returning the correct angle, but I tried angle_to_point and now it works :slight_smile:

I think they should make this easier to do, though.

:bust_in_silhouette: Reply From: rpjsf

I’m not sure if you’re still in need of a solution here, but I have figured out a way to do this without needing to create a larger collision area than the line itself (which is also difficult to get correct if the line can be at any arbitrary angle).

I am not sure if this solution is performant as I’ve not tested in volume whatsoever, but it does seem to work quite well in limited testing.

The solution is to first create a StaticBody2D/RayCast2D combo to define a ray from one end of the line (set to the ray’s .position property) to the other (set to the ray’s .cast_to property). Store this RayCast2D node together with the Line2D node it is associated with in a member variable for later lookup. Be sure to exclude any nodes that may collide with the line but you don’t care about with the .add_exception() of RayCast2D.

Then, create a StaticBody2D/CollisionPolygon2D combo that will define the “mouse collision pointer”. I set this area to be a size of 4px by 4px, but you can tweak it according to your requirements (this enables the desired “mouse detection area to be slightly wider than the wire” behavior).

Store the collision polygon as a member variable and create a function to update this collision polygon’s position to match the mouse position, and call it from _physics_process() to ensure this collision polygon is always centered at the mouse’s current position.

Finally, in _physics_process() add a loop that iterates the aforementioned ray+line list, checking each ray’s is_colliding(). You can even extend this data structure to store previous state, therefore allowing “entry” & “exit” event detection over the line’s ray, allowing you to change its color (etc.) as you mentioned was desirable and is done here.

I attempted to distill the working code into a smaller example here but that turned out to be a challenge, so I just went ahead and pushed the project in which I implemented this to Github here (hence where all the above links came from), and also deployed a runnable version here. I hope it helps!