Last active
October 27, 2017 12:31
-
-
Save willnationsdev/fcc8ce26a759dc567a2c02f7adc28717 to your computer and use it in GitHub Desktop.
Godot 2.1 Multi-Area2D Left Mouse Sprite-Clicking Guide
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Note that this ultimately is the WRONG way to handle this, but I learned some stuff along the way. | |
# I later discovered that you should just use _unhandled_input and SceneTree.set_input_as_handled, and it'll work perfectly. Figures. | |
extends Area2D | |
# Detect the input that has not been handled by ANY other input handlers | |
# http://docs.godotengine.org/en/stable/learning/features/inputs/inputevent.html | |
func _input_event(viewport, event, shape_idx): | |
# Detect a left mouse click | |
# http://docs.godotengine.org/en/stable/classes/class_inputevent.html | |
if event.type == InputEvent.MOUSE_BUTTON: | |
if event.button_index == BUTTON_LEFT and event.pressed: | |
# Note that the code below using the get_tree().set_input_handled() method won't help us | |
# here because the CollisionObject._input_event(...) notifications are all sent out | |
# as a batch (i.e. they are all receiving the same copy of the event data at once). | |
# Therefore, you cannot prevent future iterations of _input_event from picking up | |
# the event. If you wished to prevent CollisionObject._input_event from triggering | |
# after some other nodes' _input(ev), Control._input_event(ev), or _unhandled_input(ev) | |
# notification, THEN you would want to simply call get_tree().set_input_handled() | |
# (no need to even check is_input_handled() in that case). | |
# if get_tree().is_input_handled(): | |
# get-tree().set_input_handled() | |
# Without the ability to detect it from the input handling side, we must detect draw order | |
# from the arrangement of the nodes in the environment (as best as I can tell). | |
# ^ Would be great if someone could prove me wrong on this point as I'm not 100% sure. | |
# Note that by default, nodes are rendered in the order of their being a child, i.e. | |
# world | |
# - child1 | |
# - child2 | |
# => child1 will be rendered first, then child2 will be rendered on top of child1 (so child2 is visible) | |
# You can confirm this by hiding the visibility (click the "eye" icon next to the node) of the "bottom" node | |
# and observing how "bottom" disappears from view) | |
# You can statically define an ordering at design-time using the Z property on Node2D. | |
# It is NOT runtime-updated with whatever the "most visible" Node2D is. | |
# As such, the "left" and "right" nodes both report a "z" of 0, which is the default. | |
# For the "yleft" and "yright" nodes, we have a YSort node that is manually drawing the nodes | |
# relative to their Y positions (further down the screen, i.e. higher Y, means rendered on top). | |
# I specifically set the "z" of yright to be greater even though it is higher up to illustrate | |
# how its rendering order is "overriding" the YSort behavior because of its "z" value. It shows | |
# up on top of "yleft" despite the fact that "yleft" is positioned at a lower elevation / higher Y value. | |
print(get_parent().get_name(), "'s \"z\": ", get_z()) | |
# Also note how, based on the ordering of this print statement when clicking on overlapped areas, | |
# even though we overrode the default ordering using "z" for the YSort, we still can't guarantee | |
# that the top-visibility node's _input_event function will execute prior to other one's. The | |
# _input_event functions are received in the order of child relationship (yleft's _input_event | |
# triggers before yright's because yleft.get_index() < yright.get_index()). Notice also that nonybottom | |
# triggers before both of them because it's parent "Node1" is processing the event first and cascading | |
# it down to its children prior to allowing the next child, YSort, to begin its cascading input handlers. | |
# This would appear to show an inconsistency in the tree flow between input handling and draw order (?): | |
# Input handling is working in a cascading, children-first model whereas the draw order is happening | |
# level-by-level from shallow to deep, and in child order after that. | |
# The most efficient way to only do something with the top-most node is to figure out which one | |
# it is, and then skip the content for the other nodes by checking for some common comparison value. | |
# It seems as though the nodes are rendered from most shallow to most deep since "bottom" is rendering | |
# on top of "left" and "right even though it comes before them both in child ordering of "world". | |
# This doesn't appear to be the case with the YSort'd "yleft" and "yright" nodes which are rendering | |
# themselves AROUND the non-y-bottom node which rests between them but is not part of the YSort children. | |
# I would attribute this to the YSort taking into account other nodes' Y position in the vicinity, and not | |
# anything related to the "nonybottom" node's "z" value, child-ordering, depth, or anything of that sort. | |
# Get an Array with a list of all areas, NOT INCLUDING THIS ONE, that | |
# also are overlapping with this Area2D | |
var areas = get_overlapping_areas() | |
# Get a list that has all of the areas we are examining | |
areas.append(self) | |
# A variable for the area that we actually want to have DO something, i.e. the top-most visible | |
# one that we have clicked on. | |
var desired_area = null | |
# Cycle through each area in this list | |
for area in areas: | |
print(area.get_parent().get_name()) | |
# Here, you have to decide how you want to compare all of the areas | |
# You could compare their get_z() values if you have set them in the editor | |
# You could compare their depth in the tree by doing something like... | |
# get_script().get_path().replace("res://","").split("/").size() | |
# ^ not sure if that's quite right, but you get the idea. | |
# What might be the simplest way to handle it is to rely on the input handlers of Control nodes | |
# to catch what the top-most click is for you, but for that you would need an invisible TextureButton that follows | |
# your Sprite around and uses the same texture as a mask somehow (so that clicking right outside | |
# of the Sprite's Texture doesn't result in an input handler callback). | |
# If it's a non-moving image, you can just use a TextureButton for this and use set_click_mask(BitMap mask) to | |
# define the region that should be clickable, then set the alpha of the texture to be 0 so that it is not visible, | |
# but will still pick up clicks (I'm not 100% sure if setting hidden/visible/visibility/whatever to not visible will | |
# allow clicks to still be picked up). | |
# This doesn't appear to be perfectly possible for animated images without some sort of streaming texture to use as | |
# a bitmask (not even sure how that would work). | |
# Regardless Godot 2.1 doesn't appear to have any visual streaming textures like Godot 3.0 does. | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[gd_scene load_steps=4 format=1] | |
[ext_resource path="res://icon.png" type="Texture" id=1] | |
[ext_resource path="res://area2d_click.gd" type="Script" id=2] | |
[sub_resource type="RectangleShape2D" id=1] | |
custom_solver_bias = 0.0 | |
extents = Vector2( 32, 32 ) | |
[node name="world" type="Node2D"] | |
[node name="left_btn" type="Button" parent="."] | |
focus/ignore_mouse = false | |
focus/stop_mouse = true | |
size_flags/horizontal = 2 | |
size_flags/vertical = 2 | |
margin/left = 197.0 | |
margin/top = 205.0 | |
margin/right = 326.0 | |
margin/bottom = 277.0 | |
toggle_mode = false | |
enabled_focus_mode = 2 | |
shortcut = null | |
text = "LEFT" | |
flat = false | |
[node name="left_child_btn" type="Button" parent="left_btn"] | |
focus/ignore_mouse = false | |
focus/stop_mouse = true | |
size_flags/horizontal = 2 | |
size_flags/vertical = 2 | |
margin/left = -22.0 | |
margin/top = 11.0 | |
margin/right = 44.0 | |
margin/bottom = 119.0 | |
toggle_mode = false | |
enabled_focus_mode = 2 | |
shortcut = null | |
text = "LCHILD" | |
flat = false | |
[node name="right_btn" type="Button" parent="."] | |
focus/ignore_mouse = false | |
focus/stop_mouse = true | |
size_flags/horizontal = 2 | |
size_flags/vertical = 2 | |
margin/left = 107.0 | |
margin/top = 163.0 | |
margin/right = 236.0 | |
margin/bottom = 235.0 | |
toggle_mode = false | |
enabled_focus_mode = 2 | |
shortcut = null | |
text = "RIGHT" | |
flat = false | |
[node name="right_child_btn" type="Button" parent="right_btn"] | |
focus/ignore_mouse = false | |
focus/stop_mouse = true | |
size_flags/horizontal = 2 | |
size_flags/vertical = 2 | |
margin/left = 113.0 | |
margin/top = -42.0 | |
margin/right = 179.0 | |
margin/bottom = 66.0 | |
toggle_mode = false | |
enabled_focus_mode = 2 | |
shortcut = null | |
text = "RCHILD" | |
flat = false | |
[node name="Node" type="Node" parent="."] | |
[node name="bottom" type="Node2D" parent="Node"] | |
editor/display_folded = true | |
transform/pos = Vector2( 495, 219 ) | |
[node name="Sprite" type="Sprite" parent="Node/bottom"] | |
transform/rot = 180.0 | |
texture = ExtResource( 1 ) | |
[node name="Area2D" type="Area2D" parent="Node/bottom"] | |
input/pickable = true | |
shapes/0/shape = SubResource( 1 ) | |
shapes/0/transform = Matrix32( 1, 0, 0, 1, 0, 0 ) | |
shapes/0/trigger = false | |
gravity_vec = Vector2( 0, 1 ) | |
gravity = 98.0 | |
linear_damp = 0.1 | |
angular_damp = 1.0 | |
script/script = ExtResource( 2 ) | |
[node name="CollisionShape2D" type="CollisionShape2D" parent="Node/bottom/Area2D"] | |
shape = SubResource( 1 ) | |
trigger = false | |
_update_shape_index = 0 | |
[node name="right" type="Node2D" parent="."] | |
editor/display_folded = true | |
transform/pos = Vector2( 510, 204 ) | |
[node name="Sprite" type="Sprite" parent="right"] | |
transform/rot = -90.0 | |
texture = ExtResource( 1 ) | |
[node name="Area2D" type="Area2D" parent="right"] | |
input/pickable = true | |
shapes/0/shape = SubResource( 1 ) | |
shapes/0/transform = Matrix32( 1, 0, 0, 1, 0, 0 ) | |
shapes/0/trigger = false | |
gravity_vec = Vector2( 0, 1 ) | |
gravity = 98.0 | |
linear_damp = 0.1 | |
angular_damp = 1.0 | |
script/script = ExtResource( 2 ) | |
[node name="CollisionShape2D" type="CollisionShape2D" parent="right/Area2D"] | |
shape = SubResource( 1 ) | |
trigger = false | |
_update_shape_index = 0 | |
[node name="left" type="Node2D" parent="."] | |
editor/display_folded = true | |
transform/pos = Vector2( 478, 237 ) | |
[node name="Sprite" type="Sprite" parent="left"] | |
transform/rot = 90.0 | |
texture = ExtResource( 1 ) | |
[node name="Area2D" type="Area2D" parent="left"] | |
input/pickable = true | |
shapes/0/shape = SubResource( 1 ) | |
shapes/0/transform = Matrix32( 1, 0, 0, 1, 0, 0 ) | |
shapes/0/trigger = false | |
gravity_vec = Vector2( 0, 1 ) | |
gravity = 98.0 | |
linear_damp = 0.1 | |
angular_damp = 1.0 | |
script/script = ExtResource( 2 ) | |
[node name="CollisionShape2D" type="CollisionShape2D" parent="left/Area2D"] | |
shape = SubResource( 1 ) | |
trigger = false | |
_update_shape_index = 0 | |
[node name="Node1" type="Node" parent="."] | |
[node name="nonybottom" type="Node2D" parent="Node1"] | |
editor/display_folded = true | |
transform/pos = Vector2( 795, 215 ) | |
[node name="Sprite" type="Sprite" parent="Node1/nonybottom"] | |
transform/rot = 180.0 | |
texture = ExtResource( 1 ) | |
[node name="Area2D" type="Area2D" parent="Node1/nonybottom"] | |
input/pickable = true | |
shapes/0/shape = SubResource( 1 ) | |
shapes/0/transform = Matrix32( 1, 0, 0, 1, 0, 0 ) | |
shapes/0/trigger = false | |
gravity_vec = Vector2( 0, 1 ) | |
gravity = 98.0 | |
linear_damp = 0.1 | |
angular_damp = 1.0 | |
script/script = ExtResource( 2 ) | |
[node name="CollisionShape2D" type="CollisionShape2D" parent="Node1/nonybottom/Area2D"] | |
shape = SubResource( 1 ) | |
trigger = false | |
_update_shape_index = 0 | |
[node name="YSort" type="YSort" parent="."] | |
sort/enabled = true | |
[node name="yleft" type="Node2D" parent="YSort"] | |
editor/display_folded = true | |
transform/pos = Vector2( 773, 232 ) | |
[node name="Sprite" type="Sprite" parent="YSort/yleft"] | |
transform/rot = 90.0 | |
texture = ExtResource( 1 ) | |
[node name="Area2D" type="Area2D" parent="YSort/yleft"] | |
input/pickable = true | |
shapes/0/shape = SubResource( 1 ) | |
shapes/0/transform = Matrix32( 1, 0, 0, 1, 0, 0 ) | |
shapes/0/trigger = false | |
gravity_vec = Vector2( 0, 1 ) | |
gravity = 98.0 | |
linear_damp = 0.1 | |
angular_damp = 1.0 | |
script/script = ExtResource( 2 ) | |
[node name="CollisionShape2D" type="CollisionShape2D" parent="YSort/yleft/Area2D"] | |
shape = SubResource( 1 ) | |
trigger = false | |
_update_shape_index = 0 | |
[node name="yright" type="Node2D" parent="YSort"] | |
editor/display_folded = true | |
transform/pos = Vector2( 814, 200 ) | |
z/z = 1 | |
[node name="Sprite" type="Sprite" parent="YSort/yright"] | |
transform/rot = -90.0 | |
texture = ExtResource( 1 ) | |
[node name="Area2D" type="Area2D" parent="YSort/yright"] | |
input/pickable = true | |
shapes/0/shape = SubResource( 1 ) | |
shapes/0/transform = Matrix32( 1, 0, 0, 1, 0, 0 ) | |
shapes/0/trigger = false | |
gravity_vec = Vector2( 0, 1 ) | |
gravity = 98.0 | |
linear_damp = 0.1 | |
angular_damp = 1.0 | |
script/script = ExtResource( 2 ) | |
[node name="CollisionShape2D" type="CollisionShape2D" parent="YSort/yright/Area2D"] | |
shape = SubResource( 1 ) | |
trigger = false | |
_update_shape_index = 0 | |
[node name="TextureButton" type="TextureButton" parent="."] | |
focus/ignore_mouse = false | |
focus/stop_mouse = true | |
size_flags/horizontal = 2 | |
size_flags/vertical = 2 | |
margin/left = 351.0 | |
margin/top = 355.0 | |
margin/right = 391.0 | |
margin/bottom = 395.0 | |
toggle_mode = false | |
enabled_focus_mode = 2 | |
shortcut = null | |
params/resize_mode = 0 | |
params/stretch_mode = 0 | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment