diff options
-rw-r--r-- | core/templates/pair.h | 5 | ||||
-rw-r--r-- | doc/classes/CollisionObject2D.xml | 18 | ||||
-rw-r--r-- | scene/2d/collision_object_2d.cpp | 16 | ||||
-rw-r--r-- | scene/2d/collision_object_2d.h | 3 | ||||
-rw-r--r-- | scene/main/viewport.cpp | 84 | ||||
-rw-r--r-- | scene/main/viewport.h | 6 | ||||
-rw-r--r-- | scene/scene_string_names.cpp | 4 | ||||
-rw-r--r-- | scene/scene_string_names.h | 4 |
8 files changed, 106 insertions, 34 deletions
diff --git a/core/templates/pair.h b/core/templates/pair.h index bc1a764694..658a65c826 100644 --- a/core/templates/pair.h +++ b/core/templates/pair.h @@ -60,7 +60,10 @@ bool operator!=(const Pair<F, S> &pair, const Pair<F, S> &other) { template <class F, class S> struct PairSort { bool operator()(const Pair<F, S> &A, const Pair<F, S> &B) const { - return A.first < B.first; + if (A.first != B.first) { + return A.first < B.first; + } + return A.second < B.second; } }; diff --git a/doc/classes/CollisionObject2D.xml b/doc/classes/CollisionObject2D.xml index e8f7a59e4c..c51a3a3120 100644 --- a/doc/classes/CollisionObject2D.xml +++ b/doc/classes/CollisionObject2D.xml @@ -234,12 +234,26 @@ </signal> <signal name="mouse_entered"> <description> - Emitted when the mouse pointer enters any of this object's shapes. Requires [member input_pickable] to be [code]true[/code] and at least one [code]collision_layer[/code] bit to be set. + Emitted when the mouse pointer enters any of this object's shapes. Requires [member input_pickable] to be [code]true[/code] and at least one [member collision_layer] bit to be set. Note that moving between different shapes within a single [CollisionObject2D] won't cause this signal to be emitted. </description> </signal> <signal name="mouse_exited"> <description> - Emitted when the mouse pointer exits all this object's shapes. Requires [member input_pickable] to be [code]true[/code] and at least one [code]collision_layer[/code] bit to be set. + Emitted when the mouse pointer exits all this object's shapes. Requires [member input_pickable] to be [code]true[/code] and at least one [member collision_layer] bit to be set. Note that moving between different shapes within a single [CollisionObject2D] won't cause this signal to be emitted. + </description> + </signal> + <signal name="mouse_shape_entered"> + <argument index="0" name="shape_idx" type="int"> + </argument> + <description> + Emitted when the mouse pointer enters any of this object's shapes or moves from one shape to another. [code]shape_idx[/code] is the child index of the newly entered [Shape2D]. Requires [member input_pickable] to be [code]true[/code] and at least one [member collision_layer] bit to be set. + </description> + </signal> + <signal name="mouse_shape_exited"> + <argument index="0" name="shape_idx" type="int"> + </argument> + <description> + Emitted when the mouse pointer exits any of this object's shapes. [code]shape_idx[/code] is the child index of the exited [Shape2D]. Requires [member input_pickable] to be [code]true[/code] and at least one [member collision_layer] bit to be set. </description> </signal> </signals> diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp index c83ed36917..6f29a19d30 100644 --- a/scene/2d/collision_object_2d.cpp +++ b/scene/2d/collision_object_2d.cpp @@ -346,6 +346,20 @@ void CollisionObject2D::_mouse_exit() { emit_signal(SceneStringNames::get_singleton()->mouse_exited); } +void CollisionObject2D::_mouse_shape_enter(int p_shape) { + if (get_script_instance()) { + get_script_instance()->call(SceneStringNames::get_singleton()->_mouse_shape_enter, p_shape); + } + emit_signal(SceneStringNames::get_singleton()->mouse_shape_entered, p_shape); +} + +void CollisionObject2D::_mouse_shape_exit(int p_shape) { + if (get_script_instance()) { + get_script_instance()->call(SceneStringNames::get_singleton()->_mouse_shape_exit, p_shape); + } + emit_signal(SceneStringNames::get_singleton()->mouse_shape_exited, p_shape); +} + void CollisionObject2D::set_only_update_transform_changes(bool p_enable) { only_update_transform_changes = p_enable; } @@ -406,6 +420,8 @@ void CollisionObject2D::_bind_methods() { ADD_SIGNAL(MethodInfo("input_event", PropertyInfo(Variant::OBJECT, "viewport", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), PropertyInfo(Variant::INT, "shape_idx"))); ADD_SIGNAL(MethodInfo("mouse_entered")); ADD_SIGNAL(MethodInfo("mouse_exited")); + ADD_SIGNAL(MethodInfo("mouse_shape_entered", PropertyInfo(Variant::INT, "shape_idx"))); + ADD_SIGNAL(MethodInfo("mouse_shape_exited", PropertyInfo(Variant::INT, "shape_idx"))); ADD_GROUP("Pickable", "input_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "input_pickable"), "set_pickable", "is_pickable"); diff --git a/scene/2d/collision_object_2d.h b/scene/2d/collision_object_2d.h index e82b61d441..9f7df35172 100644 --- a/scene/2d/collision_object_2d.h +++ b/scene/2d/collision_object_2d.h @@ -73,6 +73,9 @@ protected: void _mouse_enter(); void _mouse_exit(); + void _mouse_shape_enter(int p_shape); + void _mouse_shape_exit(int p_shape); + void set_only_update_transform_changes(bool p_enable); public: diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index c6fe1117d1..38d2a35efb 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -724,7 +724,6 @@ void Viewport::_process_picking() { bool send_event = true; if (is_mouse) { Map<ObjectID, uint64_t>::Element *F = physics_2d_mouseover.find(res[i].collider_id); - if (!F) { physics_2d_mouseover.insert(res[i].collider_id, frame); co->_mouse_enter(); @@ -735,6 +734,13 @@ void Viewport::_process_picking() { send_event = false; } } + Map<Pair<ObjectID, int>, uint64_t, PairSort<ObjectID, int>>::Element *SF = physics_2d_shape_mouseover.find(Pair(res[i].collider_id, res[i].shape)); + if (!SF) { + physics_2d_shape_mouseover.insert(Pair(res[i].collider_id, res[i].shape), frame); + co->_mouse_shape_enter(res[i].shape); + } else { + SF->get() = frame; + } } if (send_event) { @@ -746,25 +752,7 @@ void Viewport::_process_picking() { } if (is_mouse) { - List<Map<ObjectID, uint64_t>::Element *> to_erase; - - for (Map<ObjectID, uint64_t>::Element *E = physics_2d_mouseover.front(); E; E = E->next()) { - if (E->get() != frame) { - Object *o = ObjectDB::get_instance(E->key()); - if (o) { - CollisionObject2D *co = Object::cast_to<CollisionObject2D>(o); - if (co) { - co->_mouse_exit(); - } - } - to_erase.push_back(E); - } - } - - while (to_erase.size()) { - physics_2d_mouseover.erase(to_erase.front()->get()); - to_erase.pop_front(); - } + _cleanup_mouseover_colliders(false, false, frame); } } @@ -2607,20 +2595,41 @@ void Viewport::_drop_mouse_focus() { void Viewport::_drop_physics_mouseover(bool p_paused_only) { physics_has_last_mousepos = false; + _cleanup_mouseover_colliders(true, p_paused_only); + +#ifndef _3D_DISABLED + if (physics_object_over.is_valid()) { + CollisionObject3D *co = Object::cast_to<CollisionObject3D>(ObjectDB::get_instance(physics_object_over)); + if (co) { + if (!(p_paused_only && co->can_process())) { + co->_mouse_exit(); + physics_object_over = ObjectID(); + physics_object_capture = ObjectID(); + } + } + } +#endif +} + +void Viewport::_cleanup_mouseover_colliders(bool p_clean_all_frames, bool p_paused_only, uint64_t p_frame_reference) { List<Map<ObjectID, uint64_t>::Element *> to_erase; for (Map<ObjectID, uint64_t>::Element *E = physics_2d_mouseover.front(); E; E = E->next()) { + if (!p_clean_all_frames && E->get() == p_frame_reference) { + continue; + } + Object *o = ObjectDB::get_instance(E->key()); if (o) { CollisionObject2D *co = Object::cast_to<CollisionObject2D>(o); if (co) { - if (p_paused_only && co->can_process()) { + if (p_clean_all_frames && p_paused_only && co->can_process()) { continue; } co->_mouse_exit(); - to_erase.push_back(E); } } + to_erase.push_back(E); } while (to_erase.size()) { @@ -2628,18 +2637,31 @@ void Viewport::_drop_physics_mouseover(bool p_paused_only) { to_erase.pop_front(); } -#ifndef _3D_DISABLED - if (physics_object_over.is_valid()) { - CollisionObject3D *co = Object::cast_to<CollisionObject3D>(ObjectDB::get_instance(physics_object_over)); - if (co) { - if (!(p_paused_only && co->can_process())) { - co->_mouse_exit(); - physics_object_over = ObjectID(); - physics_object_capture = ObjectID(); + // Per-shape + List<Map<Pair<ObjectID, int>, uint64_t, PairSort<ObjectID, int>>::Element *> shapes_to_erase; + + for (Map<Pair<ObjectID, int>, uint64_t, PairSort<ObjectID, int>>::Element *E = physics_2d_shape_mouseover.front(); E; E = E->next()) { + if (!p_clean_all_frames && E->get() == p_frame_reference) { + continue; + } + + Object *o = ObjectDB::get_instance(E->key().first); + if (o) { + CollisionObject2D *co = Object::cast_to<CollisionObject2D>(o); + if (co) { + if (p_clean_all_frames && p_paused_only && co->can_process()) { + continue; + } + co->_mouse_shape_exit(E->key().second); } } + shapes_to_erase.push_back(E); + } + + while (shapes_to_erase.size()) { + physics_2d_shape_mouseover.erase(shapes_to_erase.front()->get()); + shapes_to_erase.pop_front(); } -#endif } Control *Viewport::_gui_get_focus_owner() { diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 0f11e6fb19..7d6a2a10d0 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -32,6 +32,7 @@ #define VIEWPORT_H #include "core/math/transform_2d.h" +#include "core/templates/pair.h" #include "scene/main/node.h" #include "scene/resources/texture.h" #include "scene/resources/world_2d.h" @@ -271,7 +272,12 @@ private: bool handle_input_locally = true; bool local_input_handled = false; + // Collider to frame Map<ObjectID, uint64_t> physics_2d_mouseover; + // Collider & shape to frame + Map<Pair<ObjectID, int>, uint64_t, PairSort<ObjectID, int>> physics_2d_shape_mouseover; + // Cleans up colliders corresponding to old frames or all of them. + void _cleanup_mouseover_colliders(bool p_clean_all_frames, bool p_paused_only, uint64_t p_frame_reference = 0); Ref<World2D> world_2d; Ref<World3D> world_3d; diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp index 892802c103..fb07ec996a 100644 --- a/scene/scene_string_names.cpp +++ b/scene/scene_string_names.cpp @@ -66,6 +66,8 @@ SceneStringNames::SceneStringNames() { mouse_entered = StaticCString::create("mouse_entered"); mouse_exited = StaticCString::create("mouse_exited"); + mouse_shape_entered = StaticCString::create("mouse_shape_entered"); + mouse_shape_exited = StaticCString::create("mouse_shape_exited"); focus_entered = StaticCString::create("focus_entered"); focus_exited = StaticCString::create("focus_exited"); @@ -170,6 +172,8 @@ SceneStringNames::SceneStringNames() { _mouse_enter = StaticCString::create("_mouse_enter"); _mouse_exit = StaticCString::create("_mouse_exit"); + _mouse_shape_enter = StaticCString::create("_mouse_shape_enter"); + _mouse_shape_exit = StaticCString::create("_mouse_shape_exit"); _pressed = StaticCString::create("_pressed"); _toggled = StaticCString::create("_toggled"); diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h index 655e49c6f9..30fdd96bb6 100644 --- a/scene/scene_string_names.h +++ b/scene/scene_string_names.h @@ -84,6 +84,8 @@ public: StringName mouse_entered; StringName mouse_exited; + StringName mouse_shape_entered; + StringName mouse_shape_exited; StringName focus_entered; StringName focus_exited; @@ -182,6 +184,8 @@ public: StringName _mouse_enter; StringName _mouse_exit; + StringName _mouse_shape_enter; + StringName _mouse_shape_exit; StringName frame_changed; |