diff options
-rw-r--r-- | core/io/multiplayer_api.cpp | 4 | ||||
-rw-r--r-- | core/templates/pair.h | 5 | ||||
-rw-r--r-- | doc/classes/CharacterBody2D.xml | 3 | ||||
-rw-r--r-- | doc/classes/CollisionObject2D.xml | 18 | ||||
-rw-r--r-- | doc/classes/StaticBody2D.xml | 3 | ||||
-rw-r--r-- | doc/classes/StaticBody3D.xml | 3 | ||||
-rw-r--r-- | modules/enet/doc_classes/ENetMultiplayerPeer.xml | 9 | ||||
-rw-r--r-- | modules/enet/enet_multiplayer_peer.h | 2 | ||||
-rw-r--r-- | scene/2d/collision_object_2d.cpp | 16 | ||||
-rw-r--r-- | scene/2d/collision_object_2d.h | 5 | ||||
-rw-r--r-- | scene/2d/physics_body_2d.cpp | 167 | ||||
-rw-r--r-- | scene/2d/physics_body_2d.h | 19 | ||||
-rw-r--r-- | scene/3d/collision_object_3d.cpp | 12 | ||||
-rw-r--r-- | scene/3d/collision_object_3d.h | 5 | ||||
-rw-r--r-- | scene/3d/physics_body_3d.cpp | 146 | ||||
-rw-r--r-- | scene/3d/physics_body_3d.h | 14 | ||||
-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 |
20 files changed, 384 insertions, 145 deletions
diff --git a/core/io/multiplayer_api.cpp b/core/io/multiplayer_api.cpp index 564397c88c..9f92388196 100644 --- a/core/io/multiplayer_api.cpp +++ b/core/io/multiplayer_api.cpp @@ -1068,9 +1068,7 @@ int MultiplayerAPI::get_network_unique_id() const { } bool MultiplayerAPI::is_network_server() const { - // XXX Maybe fail silently? Maybe should actually return true to make development of both local and online multiplayer easier? - ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), false, "No network peer is assigned. I can't be a server."); - return network_peer->is_server(); + return network_peer.is_valid() && network_peer->is_server(); } void MultiplayerAPI::set_refuse_new_network_connections(bool p_refuse) { diff --git a/core/templates/pair.h b/core/templates/pair.h index 31706b6ecb..e30ee8bc56 100644 --- a/core/templates/pair.h +++ b/core/templates/pair.h @@ -62,7 +62,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/CharacterBody2D.xml b/doc/classes/CharacterBody2D.xml index b2d8aba174..e184e7dc82 100644 --- a/doc/classes/CharacterBody2D.xml +++ b/doc/classes/CharacterBody2D.xml @@ -110,9 +110,6 @@ <member name="max_slides" type="int" setter="set_max_slides" getter="get_max_slides" default="4"> Maximum number of times the body can change direction before it stops when calling [method move_and_slide]. </member> - <member name="motion/sync_to_physics" type="bool" setter="set_sync_to_physics" getter="is_sync_to_physics_enabled" default="false"> - If [code]true[/code], the body's movement will be synchronized to the physics frame. This is useful when animating movement via [AnimationPlayer], for example on moving platforms. Do [b]not[/b] use together with [method move_and_slide] or [method PhysicsBody2D.move_and_collide] functions. - </member> <member name="snap" type="Vector2" setter="set_snap" getter="get_snap" default="Vector2(0, 0)"> When set to a value different from [code]Vector2(0, 0)[/code], the body is kept attached to slopes when calling [method move_and_slide]. As long as the [code]snap[/code] vector is in contact with the ground, the body will remain attached to the surface. This means you must disable snap in order to jump, for example. You can do this by setting [code]snap[/code] to [code]Vector2(0, 0)[/code]. diff --git a/doc/classes/CollisionObject2D.xml b/doc/classes/CollisionObject2D.xml index e96124c9eb..b0afbb201c 100644 --- a/doc/classes/CollisionObject2D.xml +++ b/doc/classes/CollisionObject2D.xml @@ -287,12 +287,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/doc/classes/StaticBody2D.xml b/doc/classes/StaticBody2D.xml index 85915a53fe..326bf58e22 100644 --- a/doc/classes/StaticBody2D.xml +++ b/doc/classes/StaticBody2D.xml @@ -29,6 +29,9 @@ The physics material override for the body. If a material is assigned to this property, it will be used instead of any other physics material, such as an inherited one. </member> + <member name="sync_to_physics" type="bool" setter="set_sync_to_physics" getter="is_sync_to_physics_enabled" default="false"> + If [code]true[/code] and [member kinematic_motion] is enabled, the body's movement will be synchronized to the physics frame. This is useful when animating movement via [AnimationPlayer], for example on moving platforms. Do [b]not[/b] use together with [method PhysicsBody2D.move_and_collide]. + </member> </members> <constants> </constants> diff --git a/doc/classes/StaticBody3D.xml b/doc/classes/StaticBody3D.xml index f83d440f10..69c123002f 100644 --- a/doc/classes/StaticBody3D.xml +++ b/doc/classes/StaticBody3D.xml @@ -32,6 +32,9 @@ The physics material override for the body. If a material is assigned to this property, it will be used instead of any other physics material, such as an inherited one. </member> + <member name="sync_to_physics" type="bool" setter="set_sync_to_physics" getter="is_sync_to_physics_enabled" default="false"> + If [code]true[/code] and [member kinematic_motion] is enabled, the body's movement will be synchronized to the physics frame. This is useful when animating movement via [AnimationPlayer], for example on moving platforms. Do [b]not[/b] use together with [method PhysicsBody3D.move_and_collide]. + </member> </members> <constants> </constants> diff --git a/modules/enet/doc_classes/ENetMultiplayerPeer.xml b/modules/enet/doc_classes/ENetMultiplayerPeer.xml index 5b2c72dce4..30dec5987b 100644 --- a/modules/enet/doc_classes/ENetMultiplayerPeer.xml +++ b/modules/enet/doc_classes/ENetMultiplayerPeer.xml @@ -155,8 +155,9 @@ <member name="channel_count" type="int" setter="set_channel_count" getter="get_channel_count" default="3"> The number of channels to be used by ENet. Channels are used to separate different kinds of data. In reliable or ordered mode, for example, the packet delivery order is ensured on a per-channel basis. This is done to combat latency and reduces ordering restrictions on packets. The delivery status of a packet in one channel won't stall the delivery of other packets in another channel. </member> - <member name="compression_mode" type="int" setter="set_compression_mode" getter="get_compression_mode" enum="ENetMultiplayerPeer.CompressionMode" default="0"> + <member name="compression_mode" type="int" setter="set_compression_mode" getter="get_compression_mode" enum="ENetMultiplayerPeer.CompressionMode" default="1"> The compression method used for network packets. These have different tradeoffs of compression speed versus bandwidth, you may need to test which one works best for your use case if you use compression at all. + [b]Note:[/b] Most games' network design involve sending many small packets frequently (smaller than 4 KB each). If in doubt, it is recommended to keep the default compression algorithm as it works best on these small packets. </member> <member name="dtls_verify" type="bool" setter="set_dtls_verify_enabled" getter="is_dtls_verify_enabled" default="true"> Enable or disable certificate verification when [member use_dtls] [code]true[/code]. @@ -176,10 +177,10 @@ </members> <constants> <constant name="COMPRESS_NONE" value="0" enum="CompressionMode"> - No compression. This uses the most bandwidth, but has the upside of requiring the fewest CPU resources. + No compression. This uses the most bandwidth, but has the upside of requiring the fewest CPU resources. This option may also be used to make network debugging using tools like Wireshark easier. </constant> <constant name="COMPRESS_RANGE_CODER" value="1" enum="CompressionMode"> - ENet's built-in range encoding. + ENet's built-in range encoding. Works well on small packets, but is not the most efficient algorithm on packets larger than 4 KB. </constant> <constant name="COMPRESS_FASTLZ" value="2" enum="CompressionMode"> [url=http://fastlz.org/]FastLZ[/url] compression. This option uses less CPU resources compared to [constant COMPRESS_ZLIB], at the expense of using more bandwidth. @@ -188,7 +189,7 @@ [url=https://www.zlib.net/]Zlib[/url] compression. This option uses less bandwidth compared to [constant COMPRESS_FASTLZ], at the expense of using more CPU resources. </constant> <constant name="COMPRESS_ZSTD" value="4" enum="CompressionMode"> - [url=https://facebook.github.io/zstd/]Zstandard[/url] compression. + [url=https://facebook.github.io/zstd/]Zstandard[/url] compression. Note that this algorithm is not very efficient on packets smaller than 4 KB. Therefore, it's recommended to use other compression algorithms in most cases. </constant> </constants> </class> diff --git a/modules/enet/enet_multiplayer_peer.h b/modules/enet/enet_multiplayer_peer.h index e6d45eb16a..63f2ec5870 100644 --- a/modules/enet/enet_multiplayer_peer.h +++ b/modules/enet/enet_multiplayer_peer.h @@ -90,7 +90,7 @@ private: int channel = 0; }; - CompressionMode compression_mode = COMPRESS_NONE; + CompressionMode compression_mode = COMPRESS_RANGE_CODER; List<Packet> incoming_packets; diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp index 93d154bb01..3ba3a4eec5 100644 --- a/scene/2d/collision_object_2d.cpp +++ b/scene/2d/collision_object_2d.cpp @@ -498,6 +498,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; } @@ -584,6 +598,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_PROPERTY(PropertyInfo(Variant::INT, "disable_mode", PROPERTY_HINT_ENUM, "Remove,MakeStatic,KeepActive"), "set_disable_mode", "get_disable_mode"); diff --git a/scene/2d/collision_object_2d.h b/scene/2d/collision_object_2d.h index 7a71affbb5..eca53eecfc 100644 --- a/scene/2d/collision_object_2d.h +++ b/scene/2d/collision_object_2d.h @@ -75,7 +75,7 @@ private: int total_subshapes = 0; Map<uint32_t, ShapeData> shapes; - bool only_update_transform_changes = false; //this is used for sync physics in CharacterBody2D + bool only_update_transform_changes = false; // This is used for sync to physics. void _apply_disabled(); void _apply_enabled(); @@ -92,6 +92,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); bool is_only_update_transform_changes_enabled() const; diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index b68f0312fc..5b12da8b57 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -229,6 +229,13 @@ void StaticBody2D::set_kinematic_motion_enabled(bool p_enabled) { set_body_mode(PhysicsServer2D::BODY_MODE_STATIC); } +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + update_configuration_warnings(); + return; + } +#endif + _update_kinematic_motion(); } @@ -236,8 +243,75 @@ bool StaticBody2D::is_kinematic_motion_enabled() const { return kinematic_motion; } +void StaticBody2D::set_sync_to_physics(bool p_enable) { + if (sync_to_physics == p_enable) { + return; + } + + sync_to_physics = p_enable; + +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + update_configuration_warnings(); + return; + } +#endif + + if (kinematic_motion) { + _update_kinematic_motion(); + } +} + +bool StaticBody2D::is_sync_to_physics_enabled() const { + return sync_to_physics; +} + +void StaticBody2D::_direct_state_changed(Object *p_state) { + if (!sync_to_physics) { + return; + } + + PhysicsDirectBodyState2D *state = Object::cast_to<PhysicsDirectBodyState2D>(p_state); + ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState2D object as argument"); + + last_valid_transform = state->get_transform(); + set_notify_local_transform(false); + set_global_transform(last_valid_transform); + set_notify_local_transform(true); +} + +TypedArray<String> StaticBody2D::get_configuration_warnings() const { + TypedArray<String> warnings = PhysicsBody2D::get_configuration_warnings(); + + if (sync_to_physics && !kinematic_motion) { + warnings.push_back(TTR("Sync to physics works only when kinematic motion is enabled.")); + } + + return warnings; +} + void StaticBody2D::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + last_valid_transform = get_global_transform(); + } break; + + case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { + // Used by sync to physics, send the new transform to the physics... + Transform2D new_transform = get_global_transform(); + + real_t delta_time = get_physics_process_delta_time(); + new_transform.translate(constant_linear_velocity * delta_time); + new_transform.set_rotation(new_transform.get_rotation() + constant_angular_velocity * delta_time); + + PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_TRANSFORM, new_transform); + + // ... but then revert changes. + set_notify_local_transform(false); + set_global_transform(last_valid_transform); + set_notify_local_transform(true); + } break; + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { #ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint()) { @@ -247,19 +321,23 @@ void StaticBody2D::_notification(int p_what) { ERR_FAIL_COND(!kinematic_motion); - real_t delta_time = get_physics_process_delta_time(); - Transform2D new_transform = get_global_transform(); + real_t delta_time = get_physics_process_delta_time(); new_transform.translate(constant_linear_velocity * delta_time); new_transform.set_rotation(new_transform.get_rotation() + constant_angular_velocity * delta_time); - PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_TRANSFORM, new_transform); + if (sync_to_physics) { + // Propagate transform change to node. + set_global_transform(new_transform); + } else { + PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_TRANSFORM, new_transform); - // Propagate transform change to node. - set_block_transform_notify(true); - set_global_transform(new_transform); - set_block_transform_notify(false); + // Propagate transform change to node. + set_block_transform_notify(true); + set_global_transform(new_transform); + set_block_transform_notify(false); + } } break; } } @@ -276,10 +354,14 @@ void StaticBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &StaticBody2D::set_physics_material_override); ClassDB::bind_method(D_METHOD("get_physics_material_override"), &StaticBody2D::get_physics_material_override); + ClassDB::bind_method(D_METHOD("set_sync_to_physics", "enable"), &StaticBody2D::set_sync_to_physics); + ClassDB::bind_method(D_METHOD("is_sync_to_physics_enabled"), &StaticBody2D::is_sync_to_physics_enabled); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "constant_linear_velocity"), "set_constant_linear_velocity", "get_constant_linear_velocity"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "constant_angular_velocity"), "set_constant_angular_velocity", "get_constant_angular_velocity"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "kinematic_motion"), "set_kinematic_motion_enabled", "is_kinematic_motion_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync_to_physics"), "set_sync_to_physics", "is_sync_to_physics_enabled"); } StaticBody2D::StaticBody2D() : @@ -303,14 +385,24 @@ void StaticBody2D::_update_kinematic_motion() { } #endif + if (kinematic_motion && sync_to_physics) { + PhysicsServer2D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &StaticBody2D::_direct_state_changed)); + set_only_update_transform_changes(true); + set_notify_local_transform(true); + } else { + PhysicsServer2D::get_singleton()->body_set_force_integration_callback(get_rid(), Callable()); + set_only_update_transform_changes(false); + set_notify_local_transform(false); + } + + bool needs_physics_process = false; if (kinematic_motion) { if (!Math::is_zero_approx(constant_angular_velocity) || !constant_linear_velocity.is_equal_approx(Vector2())) { - set_physics_process_internal(true); - return; + needs_physics_process = true; } } - set_physics_process_internal(false); + set_physics_process_internal(needs_physics_process); } void RigidBody2D::_body_enter_tree(ObjectID p_id) { @@ -1206,45 +1298,6 @@ Ref<KinematicCollision2D> CharacterBody2D::_get_slide_collision(int p_bounce) { return slide_colliders[p_bounce]; } -void CharacterBody2D::set_sync_to_physics(bool p_enable) { - if (sync_to_physics == p_enable) { - return; - } - sync_to_physics = p_enable; - - if (Engine::get_singleton()->is_editor_hint()) { - return; - } - - if (p_enable) { - PhysicsServer2D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &CharacterBody2D::_direct_state_changed)); - set_only_update_transform_changes(true); - set_notify_local_transform(true); - } else { - PhysicsServer2D::get_singleton()->body_set_force_integration_callback(get_rid(), Callable()); - set_only_update_transform_changes(false); - set_notify_local_transform(false); - } -} - -bool CharacterBody2D::is_sync_to_physics_enabled() const { - return sync_to_physics; -} - -void CharacterBody2D::_direct_state_changed(Object *p_state) { - if (!sync_to_physics) { - return; - } - - PhysicsDirectBodyState2D *state = Object::cast_to<PhysicsDirectBodyState2D>(p_state); - ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState2D object as argument"); - - last_valid_transform = state->get_transform(); - set_notify_local_transform(false); - set_global_transform(last_valid_transform); - set_notify_local_transform(true); -} - void CharacterBody2D::set_safe_margin(real_t p_margin) { margin = p_margin; } @@ -1304,8 +1357,6 @@ void CharacterBody2D::set_up_direction(const Vector2 &p_up_direction) { void CharacterBody2D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { - last_valid_transform = get_global_transform(); - // Reset move_and_slide() data. on_floor = false; on_floor_body = RID(); @@ -1314,16 +1365,6 @@ void CharacterBody2D::_notification(int p_what) { motion_results.clear(); floor_velocity = Vector2(); } break; - - case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { - // Used by sync to physics, send the new transform to the physics. - Transform2D new_transform = get_global_transform(); - PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_TRANSFORM, new_transform); - // But then revert changes. - set_notify_local_transform(false); - set_global_transform(last_valid_transform); - set_notify_local_transform(true); - } break; } } @@ -1356,9 +1397,6 @@ void CharacterBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_slide_count"), &CharacterBody2D::get_slide_count); ClassDB::bind_method(D_METHOD("get_slide_collision", "slide_idx"), &CharacterBody2D::_get_slide_collision); - ClassDB::bind_method(D_METHOD("set_sync_to_physics", "enable"), &CharacterBody2D::set_sync_to_physics); - ClassDB::bind_method(D_METHOD("is_sync_to_physics_enabled"), &CharacterBody2D::is_sync_to_physics_enabled); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "linear_velocity"), "set_linear_velocity", "get_linear_velocity"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stop_on_slope"), "set_stop_on_slope_enabled", "is_stop_on_slope_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "infinite_inertia"), "set_infinite_inertia_enabled", "is_infinite_inertia_enabled"); @@ -1367,7 +1405,6 @@ void CharacterBody2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "snap"), "set_snap", "get_snap"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "up_direction"), "set_up_direction", "get_up_direction"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "motion/sync_to_physics"), "set_sync_to_physics", "is_sync_to_physics_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin"); } diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h index 52e432f005..7a319aabc9 100644 --- a/scene/2d/physics_body_2d.h +++ b/scene/2d/physics_body_2d.h @@ -69,6 +69,11 @@ class StaticBody2D : public PhysicsBody2D { Ref<PhysicsMaterial> physics_material_override; bool kinematic_motion = false; + bool sync_to_physics = false; + + Transform2D last_valid_transform; + + void _direct_state_changed(Object *p_state); protected: void _notification(int p_what); @@ -84,6 +89,8 @@ public: Vector2 get_constant_linear_velocity() const; real_t get_constant_angular_velocity() const; + virtual TypedArray<String> get_configuration_warnings() const override; + StaticBody2D(); private: @@ -93,6 +100,9 @@ private: void set_kinematic_motion_enabled(bool p_enabled); bool is_kinematic_motion_enabled() const; + + void set_sync_to_physics(bool p_enable); + bool is_sync_to_physics_enabled() const; }; class RigidBody2D : public PhysicsBody2D { @@ -243,7 +253,7 @@ public: TypedArray<Node2D> get_colliding_bodies() const; //function for script - TypedArray<String> get_configuration_warnings() const override; + virtual TypedArray<String> get_configuration_warnings() const override; RigidBody2D(); ~RigidBody2D(); @@ -276,7 +286,6 @@ private: bool on_floor = false; bool on_ceiling = false; bool on_wall = false; - bool sync_to_physics = false; Vector<PhysicsServer2D::MotionResult> motion_results; Vector<Ref<KinematicCollision2D>> slide_colliders; @@ -285,9 +294,6 @@ private: bool separate_raycast_shapes(PhysicsServer2D::MotionResult &r_result); - Transform2D last_valid_transform; - void _direct_state_changed(Object *p_state); - void set_safe_margin(real_t p_margin); real_t get_safe_margin() const; @@ -329,9 +335,6 @@ public: int get_slide_count() const; PhysicsServer2D::MotionResult get_slide_collision(int p_bounce) const; - void set_sync_to_physics(bool p_enable); - bool is_sync_to_physics_enabled() const; - CharacterBody2D(); ~CharacterBody2D(); }; diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp index 40e3f7c764..dd1f25da68 100644 --- a/scene/3d/collision_object_3d.cpp +++ b/scene/3d/collision_object_3d.cpp @@ -77,6 +77,10 @@ void CollisionObject3D::_notification(int p_what) { } break; case NOTIFICATION_TRANSFORM_CHANGED: { + if (only_update_transform_changes) { + return; + } + if (area) { PhysicsServer3D::get_singleton()->area_set_transform(rid, get_global_transform()); } else { @@ -284,6 +288,14 @@ void CollisionObject3D::set_body_mode(PhysicsServer3D::BodyMode p_mode) { PhysicsServer3D::get_singleton()->body_set_mode(rid, p_mode); } +void CollisionObject3D::set_only_update_transform_changes(bool p_enable) { + only_update_transform_changes = p_enable; +} + +bool CollisionObject3D::is_only_update_transform_changes_enabled() const { + return only_update_transform_changes; +} + void CollisionObject3D::_update_pickable() { if (!is_inside_tree()) { return; diff --git a/scene/3d/collision_object_3d.h b/scene/3d/collision_object_3d.h index a3a890db75..7c30a5cd98 100644 --- a/scene/3d/collision_object_3d.h +++ b/scene/3d/collision_object_3d.h @@ -74,6 +74,8 @@ private: Map<uint32_t, ShapeData> shapes; + bool only_update_transform_changes = false; // This is used for sync to physics. + bool capture_input_on_drag = false; bool ray_pickable = true; @@ -107,6 +109,9 @@ protected: void set_body_mode(PhysicsServer3D::BodyMode p_mode); + void set_only_update_transform_changes(bool p_enable); + bool is_only_update_transform_changes_enabled() const; + public: void set_collision_layer(uint32_t p_layer); uint32_t get_collision_layer() const; diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index 4568145458..760dbdadca 100644 --- a/scene/3d/physics_body_3d.cpp +++ b/scene/3d/physics_body_3d.cpp @@ -243,6 +243,13 @@ void StaticBody3D::set_kinematic_motion_enabled(bool p_enabled) { set_body_mode(PhysicsServer3D::BODY_MODE_STATIC); } +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + update_configuration_warnings(); + return; + } +#endif + _update_kinematic_motion(); } @@ -260,6 +267,57 @@ void StaticBody3D::set_constant_linear_velocity(const Vector3 &p_vel) { } } +void StaticBody3D::set_sync_to_physics(bool p_enable) { + if (sync_to_physics == p_enable) { + return; + } + + sync_to_physics = p_enable; + +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + update_configuration_warnings(); + return; + } +#endif + + if (kinematic_motion) { + _update_kinematic_motion(); + } +} + +bool StaticBody3D::is_sync_to_physics_enabled() const { + return sync_to_physics; +} + +void StaticBody3D::_direct_state_changed(Object *p_state) { + PhysicsDirectBodyState3D *state = Object::cast_to<PhysicsDirectBodyState3D>(p_state); + ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState3D object as argument"); + + linear_velocity = state->get_linear_velocity(); + angular_velocity = state->get_angular_velocity(); + + if (!sync_to_physics) { + return; + } + + last_valid_transform = state->get_transform(); + set_notify_local_transform(false); + set_global_transform(last_valid_transform); + set_notify_local_transform(true); + _on_transform_changed(); +} + +TypedArray<String> StaticBody3D::get_configuration_warnings() const { + TypedArray<String> warnings = PhysicsBody3D::get_configuration_warnings(); + + if (sync_to_physics && !kinematic_motion) { + warnings.push_back(TTR("Sync to physics works only when kinematic motion is enabled.")); + } + + return warnings; +} + void StaticBody3D::set_constant_angular_velocity(const Vector3 &p_vel) { constant_angular_velocity = p_vel; @@ -288,6 +346,34 @@ Vector3 StaticBody3D::get_angular_velocity() const { void StaticBody3D::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + last_valid_transform = get_global_transform(); + } break; + + case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { + // Used by sync to physics, send the new transform to the physics... + Transform3D new_transform = get_global_transform(); + + real_t delta_time = get_physics_process_delta_time(); + new_transform.origin += constant_linear_velocity * delta_time; + + real_t ang_vel = constant_angular_velocity.length(); + if (!Math::is_zero_approx(ang_vel)) { + Vector3 ang_vel_axis = constant_angular_velocity / ang_vel; + Basis rot(ang_vel_axis, ang_vel * delta_time); + new_transform.basis = rot * new_transform.basis; + new_transform.orthonormalize(); + } + + PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_TRANSFORM, new_transform); + + // ... but then revert changes. + set_notify_local_transform(false); + set_global_transform(last_valid_transform); + set_notify_local_transform(true); + _on_transform_changed(); + } break; + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { #ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint()) { @@ -297,9 +383,9 @@ void StaticBody3D::_notification(int p_what) { ERR_FAIL_COND(!kinematic_motion); - real_t delta_time = get_physics_process_delta_time(); - Transform3D new_transform = get_global_transform(); + + real_t delta_time = get_physics_process_delta_time(); new_transform.origin += constant_linear_velocity * delta_time; real_t ang_vel = constant_angular_velocity.length(); @@ -310,13 +396,18 @@ void StaticBody3D::_notification(int p_what) { new_transform.orthonormalize(); } - PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_TRANSFORM, new_transform); + if (sync_to_physics) { + // Propagate transform change to node. + set_global_transform(new_transform); + } else { + PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_TRANSFORM, new_transform); - // Propagate transform change to node. - set_ignore_transform_notification(true); - set_global_transform(new_transform); - set_ignore_transform_notification(false); - _on_transform_changed(); + // Propagate transform change to node. + set_ignore_transform_notification(true); + set_global_transform(new_transform); + set_ignore_transform_notification(false); + _on_transform_changed(); + } } break; } } @@ -333,22 +424,14 @@ void StaticBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &StaticBody3D::set_physics_material_override); ClassDB::bind_method(D_METHOD("get_physics_material_override"), &StaticBody3D::get_physics_material_override); + ClassDB::bind_method(D_METHOD("set_sync_to_physics", "enable"), &StaticBody3D::set_sync_to_physics); + ClassDB::bind_method(D_METHOD("is_sync_to_physics_enabled"), &StaticBody3D::is_sync_to_physics_enabled); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant_linear_velocity"), "set_constant_linear_velocity", "get_constant_linear_velocity"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant_angular_velocity"), "set_constant_angular_velocity", "get_constant_angular_velocity"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "kinematic_motion"), "set_kinematic_motion_enabled", "is_kinematic_motion_enabled"); -} - -void StaticBody3D::_direct_state_changed(Object *p_state) { -#ifdef DEBUG_ENABLED - PhysicsDirectBodyState3D *state = Object::cast_to<PhysicsDirectBodyState3D>(p_state); - ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState3D object as argument"); -#else - PhysicsDirectBodyState3D *state = (PhysicsDirectBodyState3D *)p_state; //trust it -#endif - - linear_velocity = state->get_linear_velocity(); - angular_velocity = state->get_angular_velocity(); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync_to_physics"), "set_sync_to_physics", "is_sync_to_physics_enabled"); } StaticBody3D::StaticBody3D() : @@ -372,18 +455,26 @@ void StaticBody3D::_update_kinematic_motion() { } #endif + if (kinematic_motion && sync_to_physics) { + set_only_update_transform_changes(true); + set_notify_local_transform(true); + } else { + set_only_update_transform_changes(false); + set_notify_local_transform(false); + } + + bool needs_physics_process = false; if (kinematic_motion) { PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &StaticBody3D::_direct_state_changed)); if (!constant_angular_velocity.is_equal_approx(Vector3()) || !constant_linear_velocity.is_equal_approx(Vector3())) { - set_physics_process_internal(true); - return; + needs_physics_process = true; } } else { PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), Callable()); } - set_physics_process_internal(false); + set_physics_process_internal(needs_physics_process); } void RigidBody3D::_body_enter_tree(ObjectID p_id) { @@ -1006,6 +1097,15 @@ void CharacterBody3D::move_and_slide() { } } + Vector3 current_floor_velocity = floor_velocity; + if (on_floor && on_floor_body.is_valid()) { + //this approach makes sure there is less delay between the actual body velocity and the one we saved + PhysicsDirectBodyState3D *bs = PhysicsServer3D::get_singleton()->body_get_direct_state(on_floor_body); + if (bs) { + current_floor_velocity = bs->get_linear_velocity(); + } + } + // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky Vector3 motion = (floor_velocity + linear_velocity) * (Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time()); diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h index 9d45ce3799..0ef9c78f3b 100644 --- a/scene/3d/physics_body_3d.h +++ b/scene/3d/physics_body_3d.h @@ -82,13 +82,16 @@ class StaticBody3D : public PhysicsBody3D { Ref<PhysicsMaterial> physics_material_override; bool kinematic_motion = false; + bool sync_to_physics = false; + + Transform3D last_valid_transform; + + void _direct_state_changed(Object *p_state); protected: void _notification(int p_what); static void _bind_methods(); - void _direct_state_changed(Object *p_state); - public: void set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override); Ref<PhysicsMaterial> get_physics_material_override() const; @@ -102,6 +105,8 @@ public: virtual Vector3 get_linear_velocity() const override; virtual Vector3 get_angular_velocity() const override; + virtual TypedArray<String> get_configuration_warnings() const override; + StaticBody3D(); private: @@ -111,6 +116,9 @@ private: void set_kinematic_motion_enabled(bool p_enabled); bool is_kinematic_motion_enabled() const; + + void set_sync_to_physics(bool p_enable); + bool is_sync_to_physics_enabled() const; }; class RigidBody3D : public PhysicsBody3D { @@ -251,7 +259,7 @@ public: void apply_impulse(const Vector3 &p_impulse, const Vector3 &p_position = Vector3()); void apply_torque_impulse(const Vector3 &p_impulse); - TypedArray<String> get_configuration_warnings() const override; + virtual TypedArray<String> get_configuration_warnings() const override; RigidBody3D(); ~RigidBody3D(); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 9a2e0c5230..b359685d31 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -706,7 +706,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(); @@ -717,6 +716,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) { @@ -728,25 +734,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 0f3bb8707d..59715ebd0e 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" @@ -276,7 +277,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 35f8a506fc..d53d155cce 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"); @@ -169,6 +171,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 01865b0d2f..a141c2b3aa 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; @@ -181,6 +183,8 @@ public: StringName _mouse_enter; StringName _mouse_exit; + StringName _mouse_shape_enter; + StringName _mouse_shape_exit; StringName frame_changed; StringName texture_changed; |