summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/templates/pair.h5
-rw-r--r--doc/classes/CollisionObject2D.xml18
-rw-r--r--scene/2d/collision_object_2d.cpp16
-rw-r--r--scene/2d/collision_object_2d.h3
-rw-r--r--scene/main/viewport.cpp84
-rw-r--r--scene/main/viewport.h6
-rw-r--r--scene/scene_string_names.cpp4
-rw-r--r--scene/scene_string_names.h4
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;