You are on page 1of 6
2010212019 [Guid How to click a sprite godat © BB = Search r/godot Posts 6 (eeoreantoore Posted by u/beamer159 1 yearago [Guide] How to click a sprite Clicking a Sprite Let's say you have a sprite and you want it to do something when you click on it. Unfortunately, the Sprite node does not have any capability to detect a click. Fortunately, another node can detect clicks: The Area2D node. Instead of just using a Sprite node, add an Area2D node and make the Sprite node a child, like this. You will notice a warning on the Area2D node about not having any children shapes. Although Area2D nodes can detect mouse clicks, they need shapes to define the area they are monitoring. Add a CollisionShape2D as a child to the Area2D node, like this. You will notice another warning, this time on the CollisionShape2D node. You need to specify the type and size of the shape this node will represent. In the Inspector, for the Shape property, click on "null" and choose New RectangleShape2D from the dropdown menu. Now, to set the shape's size, click on "RectangleShape2D" where "null" used to be and find the Extents property. Set the x and y values to half the image's height. This image is 64x64, so I set both values to 32, like this. Finally, the node structure is complete. We are about halfway done. All that’s left is to write the code to do the detecting. Area2D detects mouse clicks via the function _input_event(...). However, this function also detects many other kinds of events, so we will have to weed the non-clicks out. Attach a script to the Area2D node and write the following: extends Area2D func _input_event(viewport, event, shape_idx): if event is InputEventNouseButton \ and event. button_index == BUTTON_LEFT \ and event. is_pressed() self .on_click() func on_click() print("CLick") This function checks to see if event is a "left mouse button pressed" event. If the event is validated by this check, we know it is a mouse click so we can handle it accordingly, in this case calling a function. For clarity, rename the root Area2D node to something more descriptive (I named it Clickable) and save the scene. Now let's try it out! tps wwe comgadeUcammentsi7xw:22Iguide_haw.te_clek_a_sprital 18 2010212019 [Guid How to click a sprite godat © BB = Search r/godot program should output your message whenever the sprite is clicked The (Hard) Coded Way For those who prefer coding over using the GUI, you can add the following to your source files to build the scenes from scratch. Note that you still need the root nodes to be an Area2D and a Node for the Clickable and Test scenes, respectively. In the Clickable scene's source file, add a _ready() function with the follo\ fune _ready() var sprite = Sprite.new() sprite.texture = load(“res://icon.png") # could use preload self.add_child(sprite) var rectangle_shape = RectangleShapezD.new() rectangle_shape.extents = Vector2(32, 32) var collision_shape = CollisionShape2D.new() collision_shape.shape = rectangle _shape self .add_child(collision_shape) In the test scene, add a script to the Node and create a_ready() function with the following: fune _ready() var clickable = load(“res://Clickable.tscn") # could use preload var clickable_instance = clickable. instance() clickable_instance.position = Vector2(100, 100) self add_child(clickable_instance) Clicking the Top Sprite Our current solution is well and good, but what if multiple clickable sprites overlap each other? Well let's find out, Add three more instances of the Clickable scene and give each instance a descriptive name. Rearrange the sprites so that they overlap each other while also being able to distinguish them. You will notice that the topmost sprite (the bottom one) is also the last child of the root node. Sprites are printed in order, from first child to last. We will use this later. ‘Open your Clickable scene's Area2D script and add the following line at the top: extends Area2D # already in the file var message Also, change the on_click() function to output this message: func on_clicked() print (message) We will use this message to distinguish the different Clickable scenes. Now, for the test scene's root Node, add a script and add the following to the top: tps wwe comgadeUcammentsi7xw:22Iguide_haw.te_clek_a_sprital 26 2010212019 [Guid How to click a sprite godat © BB = Search r/godot unt eauy var Ciitnsuie Lop ~ geL_nouey Guitnabierup 7 onready var clickable_right = get_node("ClickableRight") onready var clickable_left = get_node("ClickableLeft") onready var clickable_bottom = get_node("ClickableBottom") And in the _ready() function, add the following: clickable_top.message = "Top" clickable_right.message = "Right" clickable_left.message = "Left" clickable_bottom.message = "Bottom" We are finally ready to test this thing out. Run the program and click around the sprites. You will notice two things. First, clicking where multiple sprites overlap performs every one of those sprites’ ‘on_click() functions. Second, the order that the sprites print is consistent but not in their expected order (e.g, the last child of the root Node may not print first or last). Depending on your requirements, this behavior might be fine for you, However, if you need to determine the topmost sprite that was clicked, here is how you can do it. The strategy is to store all of the sprites that were clicked in a container. Then, after all of the sprites are in the container, determine which one is last child of the root Node. Finally, perform the on_clicked() function just for this last child, First, in the test scene's Node script, add the following line to the top: onready var clickable_bottom = get_node("ClickableBottom") # already in the file var clicked = [] This variable is the array that will hold all of the clicked sprites. Next, in the Clickable scene's Area2D script, change the input_event(...) to the following: func _input_event(viewport, event, shape_idx) if event is InputEventMousegutton \ and event button_index == BUTTON LEFT \ and event.is_pressed() get_parent().clicked.append(self) get_parent().set_process( true) Now, when a click is confirmed, instead of calling the on_clicked() function, this Clickable node will be added to the test scene's clicked array. The test scene's _process(...) function will also be enabled. However, that script does not have a_process(...) function defined yet, so let's add one. In the test scene's Node script, add the following function: func _process(delta) set_process(false) var topmost_clicked = clicked. front() for i in clicked if i.get_index() > topmost_clicked.get_index() ‘topmost_clicked = i clicked.clear() topmost_clicked.on_clicked() tps ww red comgadeUcammentsi7xw:22Iguide_haw.te_clek_a_sprital 6 2010212019 [Guid How to click a sprite godat © BB = Search r/godot corresponds to the place that node is within its parent. Therefore, the largest index is the topmost sprite. Afterwards, the topmost node’s on_clicked() function is called There is just one more line of code to add to finish this. When a _process(...) function is added to a script, it is enabled by default. We want to disable it initially, so add the following line to the _ready() function: set_process(false) This ensures that the _process(..) function is only called when sprites are clicked. @10Comments A Share ++ 97% Upvoted ADVERTISEMENT ‘Gy This thread Is archived New comments cannot be posted and votes cannot be cast SORT BY BEST ~ A willnationsdev Godot contributor 4 points - year ago - edited yearago ¥ | Let's say you have a sprite and you want it to do something when you click on it. Unfortunately, the Sprite node does not have any capability to detect a click. Um...actually, you can just do this? extends Sprite func _unhandled_input(event) if event is InputEventWouseButton and event.pressed and not event.is_echo var pos = position + offset - ( (texture.get_size() / 2.0) if centere if Rect2(pos, texture.get_size()).has_point(event.position): # added print(‘test’) get_tree().set_input_as_handled() # if you don't want subsequent If you need to increase the priority of a particular callback, then you can just do it in the _input(event) method rather than the _unhandled_input(event) method. Usually, if something sounds really complicated, it's because there is a better way to go about doing it. Hopefully this can help to simplify your code (or let me know if this solution doesn't do what you are looking for) Edit: fixed a bug that /w/beamer59 pointed out to me Edit 2: fixed another bug that /u/beamer159 pointed out to me. hitps ww red comigadeUcammentsi7xw:22Iguide_haw.te_clek_a_sprital 2010212019 [Guid How to click a sprite godat © BB = Search r/godot The main problem with this solution is that _unhandled_event(...) is called for any mouse click anywhere in the game window, not just when a Sprite is clicked. For something like this to work, you need to write additional code to check that the mouse is over the Sprite, This work is done for you with Area2D's_input_event(... function. However, I like your use of the "is" keyword. I did not know about that. I can eliminate the action that I created in my demo. Thanks. Share Report Save 4 willnationsdev Godot contributor 1 point ~ 1 year ago W Ah, somehow I missed that. Hmm... Share Report Save 4 wilinationsdev Godot contributor 1 point ~ 1 year ago Not sure if a mention in an edit actually pings you, but anyway, | fixed the error you pointed out. Share Report Save 4 beamerts9 ‘point » 1 year ago One more potential issue I see in your solution: It will only work if the Sprite is not centered. If the Sprite is centered, as is default, the clickable area will be offset. You either must make sure that the Sprite is not centered, or you must account for this offset in your calculation. Share Report Save A willoationsdev Godot contributor 1 point - year ago fixed Ido think it would be a good idea for people to more easily get the shifted Rect2 ofa Sprite, after centered and offset have both been applied to the transformation. We should make a PR for that. XD Going through this whole process just to test whether you've clicked on it shouldn't really be necessary. Share Report Save A beameris9 # 2points » 1 year ago Your solution looks good now. Definitely a viable alternative to my design, Share Report Save A willnationsdev Godot contributor 3points » 1yearago - edited 11 months ago Ok, Imade a PR. Hopefully this can be more easily taken care of in 3.1 and onwards. ‘Assuming this change is made, you would then be able to do this with: extends Sprite func _unhandled_input(event ) if event is InputEventMouseButton and event.pressed and not event.is_echo if get_rect().has_point (event.position) hitps awn red comigadeUcammentsi7xw:22Iguide_haw_to_clek_a_sprital 5i6 2010212019 [Guid How to click a sprite godat © BB = Search r/godot Edit: Don't you just love open source? “Hey, this is really difficult. Why does it have to be this complicated?" "Boom. Not complicated anymore. You're welcome." XD (granted, most people don't work off the master branch like me, but...) Share Report Save Madman3001 2points - 1 year ago Nice guide, thanks! Share Report Save > dandmed ‘1 point - 11 months ago Thad this saved, but hadn't had a chance to look at it. Just want to send my appreciations for this guide, it's very usefull Share Report Save > tps ww red comgadeUcammentsi7xw:22Iguide_haw_te_clek_a_sprital

You might also like