diff options
Diffstat (limited to 'scene/2d')
-rw-r--r-- | scene/2d/animated_sprite_2d.cpp | 5 | ||||
-rw-r--r-- | scene/2d/area_2d.cpp | 28 | ||||
-rw-r--r-- | scene/2d/audio_stream_player_2d.cpp | 2 | ||||
-rw-r--r-- | scene/2d/collision_object_2d.cpp | 24 | ||||
-rw-r--r-- | scene/2d/gpu_particles_2d.cpp | 4 | ||||
-rw-r--r-- | scene/2d/joint_2d.cpp (renamed from scene/2d/joints_2d.cpp) | 9 | ||||
-rw-r--r-- | scene/2d/joint_2d.h (renamed from scene/2d/joints_2d.h) | 8 | ||||
-rw-r--r-- | scene/2d/parallax_layer.cpp | 4 | ||||
-rw-r--r-- | scene/2d/physical_bone_2d.cpp | 22 | ||||
-rw-r--r-- | scene/2d/physical_bone_2d.h | 4 | ||||
-rw-r--r-- | scene/2d/physics_body_2d.cpp | 379 | ||||
-rw-r--r-- | scene/2d/physics_body_2d.h | 72 | ||||
-rw-r--r-- | scene/2d/tile_map.cpp | 477 | ||||
-rw-r--r-- | scene/2d/tile_map.h | 18 |
14 files changed, 649 insertions, 407 deletions
diff --git a/scene/2d/animated_sprite_2d.cpp b/scene/2d/animated_sprite_2d.cpp index 96a3134691..fad4784d51 100644 --- a/scene/2d/animated_sprite_2d.cpp +++ b/scene/2d/animated_sprite_2d.cpp @@ -94,7 +94,7 @@ Rect2 AnimatedSprite2D::_get_rect() const { Point2 ofs = offset; if (centered) { - ofs -= Size2(s) / 2; + ofs -= s / 2; } if (s == Size2(0, 0)) { @@ -228,8 +228,7 @@ void AnimatedSprite2D::_notification(int p_what) { RID ci = get_canvas_item(); - Size2i s; - s = texture->get_size(); + Size2 s = texture->get_size(); Point2 ofs = offset; if (centered) { ofs -= s / 2; diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp index e497ea48c4..fff9c47d4d 100644 --- a/scene/2d/area_2d.cpp +++ b/scene/2d/area_2d.cpp @@ -300,8 +300,8 @@ void Area2D::_clear_monitoring() { body_map.clear(); //disconnect all monitored stuff - for (Map<ObjectID, BodyState>::Element *E = bmcopy.front(); E; E = E->next()) { - Object *obj = ObjectDB::get_instance(E->key()); + for (const KeyValue<ObjectID, BodyState> &E : bmcopy) { + Object *obj = ObjectDB::get_instance(E.key); Node *node = Object::cast_to<Node>(obj); if (!node) { //node may have been deleted in previous frame or at other legitimate point @@ -311,12 +311,12 @@ void Area2D::_clear_monitoring() { node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area2D::_body_enter_tree)); node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area2D::_body_exit_tree)); - if (!E->get().in_tree) { + if (!E.value.in_tree) { continue; } - for (int i = 0; i < E->get().shapes.size(); i++) { - emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E->get().rid, node, E->get().shapes[i].body_shape, E->get().shapes[i].area_shape); + for (int i = 0; i < E.value.shapes.size(); i++) { + emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E.value.rid, node, E.value.shapes[i].body_shape, E.value.shapes[i].area_shape); } emit_signal(SceneStringNames::get_singleton()->body_exited, obj); @@ -328,8 +328,8 @@ void Area2D::_clear_monitoring() { area_map.clear(); //disconnect all monitored stuff - for (Map<ObjectID, AreaState>::Element *E = bmcopy.front(); E; E = E->next()) { - Object *obj = ObjectDB::get_instance(E->key()); + for (const KeyValue<ObjectID, AreaState> &E : bmcopy) { + Object *obj = ObjectDB::get_instance(E.key); Node *node = Object::cast_to<Node>(obj); if (!node) { //node may have been deleted in previous frame or at other legitimate point @@ -339,12 +339,12 @@ void Area2D::_clear_monitoring() { node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area2D::_area_enter_tree)); node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area2D::_area_exit_tree)); - if (!E->get().in_tree) { + if (!E.value.in_tree) { continue; } - for (int i = 0; i < E->get().shapes.size(); i++) { - emit_signal(SceneStringNames::get_singleton()->area_shape_exited, E->get().rid, node, E->get().shapes[i].area_shape, E->get().shapes[i].self_shape); + for (int i = 0; i < E.value.shapes.size(); i++) { + emit_signal(SceneStringNames::get_singleton()->area_shape_exited, E.value.rid, node, E.value.shapes[i].area_shape, E.value.shapes[i].self_shape); } emit_signal(SceneStringNames::get_singleton()->area_exited, obj); @@ -404,8 +404,8 @@ TypedArray<Node2D> Area2D::get_overlapping_bodies() const { TypedArray<Node2D> ret; ret.resize(body_map.size()); int idx = 0; - for (const Map<ObjectID, BodyState>::Element *E = body_map.front(); E; E = E->next()) { - Object *obj = ObjectDB::get_instance(E->key()); + for (const KeyValue<ObjectID, BodyState> &E : body_map) { + Object *obj = ObjectDB::get_instance(E.key); if (!obj) { ret.resize(ret.size() - 1); //ops } else { @@ -421,8 +421,8 @@ TypedArray<Area2D> Area2D::get_overlapping_areas() const { TypedArray<Area2D> ret; ret.resize(area_map.size()); int idx = 0; - for (const Map<ObjectID, AreaState>::Element *E = area_map.front(); E; E = E->next()) { - Object *obj = ObjectDB::get_instance(E->key()); + for (const KeyValue<ObjectID, AreaState> &E : area_map) { + Object *obj = ObjectDB::get_instance(E.key); if (!obj) { ret.resize(ret.size() - 1); //ops } else { diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp index bddc342c1a..18a50ae098 100644 --- a/scene/2d/audio_stream_player_2d.cpp +++ b/scene/2d/audio_stream_player_2d.cpp @@ -68,7 +68,7 @@ void AudioStreamPlayer2D::_notification(int p_what) { active.set(); Ref<AudioStreamPlayback> new_playback = stream->instance_playback(); ERR_FAIL_COND_MSG(new_playback.is_null(), "Failed to instantiate playback."); - AudioServer::get_singleton()->start_playback_stream(new_playback, _get_actual_bus(), volume_vector, setplay.get()); + AudioServer::get_singleton()->start_playback_stream(new_playback, _get_actual_bus(), volume_vector, setplay.get(), pitch_scale); stream_playbacks.push_back(new_playback); setplay.set(-1); } diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp index 5d3a538f60..6916f832d0 100644 --- a/scene/2d/collision_object_2d.cpp +++ b/scene/2d/collision_object_2d.cpp @@ -342,15 +342,15 @@ real_t CollisionObject2D::get_shape_owner_one_way_collision_margin(uint32_t p_ow } void CollisionObject2D::get_shape_owners(List<uint32_t> *r_owners) { - for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) { - r_owners->push_back(E->key()); + for (const KeyValue<uint32_t, ShapeData> &E : shapes) { + r_owners->push_back(E.key); } } Array CollisionObject2D::_get_shape_owners() { Array ret; - for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) { - ret.push_back(E->key()); + for (const KeyValue<uint32_t, ShapeData> &E : shapes) { + ret.push_back(E.key); } return ret; @@ -434,10 +434,10 @@ void CollisionObject2D::shape_owner_remove_shape(uint32_t p_owner, int p_shape) shapes[p_owner].shapes.remove(p_shape); - for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) { - for (int i = 0; i < E->get().shapes.size(); i++) { - if (E->get().shapes[i].index > index_to_remove) { - E->get().shapes.write[i].index -= 1; + for (KeyValue<uint32_t, ShapeData> &E : shapes) { + for (int i = 0; i < E.value.shapes.size(); i++) { + if (E.value.shapes[i].index > index_to_remove) { + E.value.shapes.write[i].index -= 1; } } } @@ -456,10 +456,10 @@ void CollisionObject2D::shape_owner_clear_shapes(uint32_t p_owner) { uint32_t CollisionObject2D::shape_find_owner(int p_shape_index) const { ERR_FAIL_INDEX_V(p_shape_index, total_subshapes, 0); - for (const Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) { - for (int i = 0; i < E->get().shapes.size(); i++) { - if (E->get().shapes[i].index == p_shape_index) { - return E->key(); + for (const KeyValue<uint32_t, ShapeData> &E : shapes) { + for (int i = 0; i < E.value.shapes.size(); i++) { + if (E.value.shapes[i].index == p_shape_index) { + return E.key; } } } diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp index 5bce705dd5..3637594e1b 100644 --- a/scene/2d/gpu_particles_2d.cpp +++ b/scene/2d/gpu_particles_2d.cpp @@ -163,8 +163,8 @@ void GPUParticles2D::set_trail_sections(int p_sections) { } void GPUParticles2D::set_trail_section_subdivisions(int p_subdivisions) { - ERR_FAIL_COND(trail_section_subdivisions < 1); - ERR_FAIL_COND(trail_section_subdivisions > 1024); + ERR_FAIL_COND(p_subdivisions < 1); + ERR_FAIL_COND(p_subdivisions > 1024); trail_section_subdivisions = p_subdivisions; update(); diff --git a/scene/2d/joints_2d.cpp b/scene/2d/joint_2d.cpp index 4a6606256e..8a528151cf 100644 --- a/scene/2d/joints_2d.cpp +++ b/scene/2d/joint_2d.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* joints_2d.cpp */ +/* joint_2d.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "joints_2d.h" +#include "joint_2d.h" #include "physics_body_2d.h" #include "scene/scene_string_names.h" @@ -74,6 +74,8 @@ void Joint2D::_update_joint(bool p_only_free) { PhysicsBody2D *body_a = Object::cast_to<PhysicsBody2D>(node_a); PhysicsBody2D *body_b = Object::cast_to<PhysicsBody2D>(node_b); + bool valid = false; + if (node_a && !body_a && node_b && !body_b) { warning = TTR("Node A and Node B must be PhysicsBody2Ds"); } else if (node_a && !body_a) { @@ -86,11 +88,12 @@ void Joint2D::_update_joint(bool p_only_free) { warning = TTR("Node A and Node B must be different PhysicsBody2Ds"); } else { warning = String(); + valid = true; } update_configuration_warnings(); - if (!warning.is_empty()) { + if (!valid) { PhysicsServer2D::get_singleton()->joint_clear(joint); return; } diff --git a/scene/2d/joints_2d.h b/scene/2d/joint_2d.h index dc5a08f815..0c3956e463 100644 --- a/scene/2d/joints_2d.h +++ b/scene/2d/joint_2d.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* joints_2d.h */ +/* joint_2d.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef JOINTS_2D_H -#define JOINTS_2D_H +#ifndef JOINT_2D_H +#define JOINT_2D_H #include "node_2d.h" @@ -148,4 +148,4 @@ public: DampedSpringJoint2D(); }; -#endif // JOINTS_2D_H +#endif // JOINT_2D_H diff --git a/scene/2d/parallax_layer.cpp b/scene/2d/parallax_layer.cpp index 67e35cc7a3..797e2e59cb 100644 --- a/scene/2d/parallax_layer.cpp +++ b/scene/2d/parallax_layer.cpp @@ -123,12 +123,12 @@ void ParallaxLayer::set_base_offset_and_scale(const Point2 &p_offset, real_t p_s Point2 new_ofs = (screen_offset + (p_offset - screen_offset) * motion_scale) + motion_offset * p_scale + orig_offset * p_scale; if (mirroring.x) { - double den = mirroring.x * p_scale; + real_t den = mirroring.x * p_scale; new_ofs.x -= den * ceil(new_ofs.x / den); } if (mirroring.y) { - double den = mirroring.y * p_scale; + real_t den = mirroring.y * p_scale; new_ofs.y -= den * ceil(new_ofs.y / den); } diff --git a/scene/2d/physical_bone_2d.cpp b/scene/2d/physical_bone_2d.cpp index c4b2608812..c1b0bc35dd 100644 --- a/scene/2d/physical_bone_2d.cpp +++ b/scene/2d/physical_bone_2d.cpp @@ -30,6 +30,8 @@ #include "physical_bone_2d.h" +#include "scene/2d/joint_2d.h" + void PhysicalBone2D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { @@ -150,27 +152,15 @@ void PhysicalBone2D::_start_physics_simulation() { return; } - // Reset to Bone2D position + // Reset to Bone2D position. _position_at_bone2d(); - // Apply the layers and masks + // Apply the layers and masks. PhysicsServer2D::get_singleton()->body_set_collision_layer(get_rid(), get_collision_layer()); PhysicsServer2D::get_singleton()->body_set_collision_mask(get_rid(), get_collision_mask()); - // Apply the correct mode - RigidDynamicBody2D::Mode rigid_mode = get_mode(); - if (rigid_mode == RigidDynamicBody2D::MODE_STATIC) { - set_body_mode(PhysicsServer2D::BODY_MODE_STATIC); - } else if (rigid_mode == RigidDynamicBody2D::MODE_DYNAMIC) { - set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC); - } else if (rigid_mode == RigidDynamicBody2D::MODE_KINEMATIC) { - set_body_mode(PhysicsServer2D::BODY_MODE_KINEMATIC); - } else if (rigid_mode == RigidDynamicBody2D::MODE_DYNAMIC_LOCKED) { - set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC_LOCKED); - } else { - // Default to Dynamic. - set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC); - } + // Apply the correct mode. + _apply_body_mode(); _internal_simulate_physics = true; set_physics_process_internal(true); diff --git a/scene/2d/physical_bone_2d.h b/scene/2d/physical_bone_2d.h index a250d0aadd..8b41f75c3e 100644 --- a/scene/2d/physical_bone_2d.h +++ b/scene/2d/physical_bone_2d.h @@ -31,11 +31,11 @@ #ifndef PHYSICAL_BONE_2D_H #define PHYSICAL_BONE_2D_H -#include "scene/2d/joints_2d.h" #include "scene/2d/physics_body_2d.h" - #include "scene/2d/skeleton_2d.h" +class Joint2D; + class PhysicalBone2D : public RigidDynamicBody2D { GDCLASS(PhysicalBone2D, RigidDynamicBody2D); diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index 1ca5a9c0b6..41288d646f 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -34,8 +34,8 @@ #include "scene/scene_string_names.h" void PhysicsBody2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "test_only", "safe_margin"), &PhysicsBody2D::_move, DEFVAL(false), DEFVAL(0.08)); - ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec", "collision", "safe_margin"), &PhysicsBody2D::test_move, DEFVAL(Variant()), DEFVAL(0.08)); + ClassDB::bind_method(D_METHOD("move_and_collide", "linear_velocity", "test_only", "safe_margin"), &PhysicsBody2D::_move, DEFVAL(false), DEFVAL(0.08)); + ClassDB::bind_method(D_METHOD("test_move", "from", "linear_velocity", "collision", "safe_margin"), &PhysicsBody2D::test_move, DEFVAL(Variant()), DEFVAL(0.08)); ClassDB::bind_method(D_METHOD("get_collision_exceptions"), &PhysicsBody2D::get_collision_exceptions); ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &PhysicsBody2D::add_collision_exception_with); @@ -54,10 +54,14 @@ PhysicsBody2D::~PhysicsBody2D() { } } -Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_motion, bool p_test_only, real_t p_margin) { - PhysicsServer2D::MotionResult result; +Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_linear_velocity, bool p_test_only, real_t p_margin) { + // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky. + double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time(); - if (move_and_collide(p_motion, result, p_margin, p_test_only)) { + PhysicsServer2D::MotionParameters parameters(get_global_transform(), p_linear_velocity * delta, p_margin); + + PhysicsServer2D::MotionResult result; + if (move_and_collide(parameters, result, p_test_only)) { // Create a new instance when the cached reference is invalid or still in use in script. if (motion_cache.is_null() || motion_cache->reference_get_count() > 1) { motion_cache.instantiate(); @@ -71,18 +75,18 @@ Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_motion, bool p_t return Ref<KinematicCollision2D>(); } -bool PhysicsBody2D::move_and_collide(const Vector2 &p_motion, PhysicsServer2D::MotionResult &r_result, real_t p_margin, bool p_test_only, bool p_cancel_sliding, bool p_collide_separation_ray, const Set<RID> &p_exclude) { +bool PhysicsBody2D::move_and_collide(const PhysicsServer2D::MotionParameters &p_parameters, PhysicsServer2D::MotionResult &r_result, bool p_test_only, bool p_cancel_sliding) { if (is_only_update_transform_changes_enabled()) { ERR_PRINT("Move functions do not work together with 'sync to physics' option. Please read the documentation."); } - Transform2D gt = get_global_transform(); - bool colliding = PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_margin, &r_result, p_collide_separation_ray, p_exclude); + + bool colliding = PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), p_parameters, &r_result); // Restore direction of motion to be along original motion, // in order to avoid sliding due to recovery, // but only if collision depth is low enough to avoid tunneling. if (p_cancel_sliding) { - real_t motion_length = p_motion.length(); + real_t motion_length = p_parameters.motion.length(); real_t precision = 0.001; if (colliding) { @@ -90,7 +94,7 @@ bool PhysicsBody2D::move_and_collide(const Vector2 &p_motion, PhysicsServer2D::M // so even in normal resting cases the depth can be a bit more than the margin. precision += motion_length * (r_result.collision_unsafe_fraction - r_result.collision_safe_fraction); - if (r_result.collision_depth > (real_t)p_margin + precision) { + if (r_result.collision_depth > p_parameters.margin + precision) { p_cancel_sliding = false; } } @@ -99,7 +103,7 @@ bool PhysicsBody2D::move_and_collide(const Vector2 &p_motion, PhysicsServer2D::M // When motion is null, recovery is the resulting motion. Vector2 motion_normal; if (motion_length > CMP_EPSILON) { - motion_normal = p_motion / motion_length; + motion_normal = p_parameters.motion / motion_length; } // Check depth of recovery. @@ -108,15 +112,16 @@ bool PhysicsBody2D::move_and_collide(const Vector2 &p_motion, PhysicsServer2D::M real_t recovery_length = recovery.length(); // Fixes cases where canceling slide causes the motion to go too deep into the ground, // because we're only taking rest information into account and not general recovery. - if (recovery_length < (real_t)p_margin + precision) { + if (recovery_length < p_parameters.margin + precision) { // Apply adjustment to motion. r_result.travel = motion_normal * projected_length; - r_result.remainder = p_motion - r_result.travel; + r_result.remainder = p_parameters.motion - r_result.travel; } } } if (!p_test_only) { + Transform2D gt = p_parameters.from; gt.elements[2] += r_result.travel; set_global_transform(gt); } @@ -124,7 +129,7 @@ bool PhysicsBody2D::move_and_collide(const Vector2 &p_motion, PhysicsServer2D::M return colliding; } -bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_motion, const Ref<KinematicCollision2D> &r_collision, real_t p_margin) { +bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_linear_velocity, const Ref<KinematicCollision2D> &r_collision, real_t p_margin) { ERR_FAIL_COND_V(!is_inside_tree(), false); PhysicsServer2D::MotionResult *r = nullptr; @@ -133,7 +138,12 @@ bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_motion r = const_cast<PhysicsServer2D::MotionResult *>(&r_collision->result); } - return PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), p_from, p_motion, p_margin, r); + // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky. + double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time(); + + PhysicsServer2D::MotionParameters parameters(p_from, p_linear_velocity * delta, p_margin); + + return PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), parameters, r); } TypedArray<PhysicsBody2D> PhysicsBody2D::get_collision_exceptions() { @@ -282,6 +292,12 @@ void AnimatableBody2D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { last_valid_transform = get_global_transform(); + _update_kinematic_motion(); + } break; + + case NOTIFICATION_EXIT_TREE: { + set_only_update_transform_changes(false); + set_notify_local_transform(false); } break; case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { @@ -307,7 +323,6 @@ void AnimatableBody2D::_bind_methods() { AnimatableBody2D::AnimatableBody2D() : StaticBody2D(PhysicsServer2D::BODY_MODE_KINEMATIC) { - _update_kinematic_motion(); } void RigidDynamicBody2D::_body_enter_tree(ObjectID p_id) { @@ -430,7 +445,7 @@ void RigidDynamicBody2D::_body_state_changed_callback(void *p_instance, PhysicsD void RigidDynamicBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) { set_block_transform_notify(true); // don't want notify (would feedback loop) - if (mode != MODE_KINEMATIC) { + if (!freeze || freeze_mode != FREEZE_MODE_KINEMATIC) { set_global_transform(p_state->get_transform()); } @@ -451,9 +466,9 @@ void RigidDynamicBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) //untag all int rc = 0; - for (Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.front(); E; E = E->next()) { - for (int i = 0; i < E->get().shapes.size(); i++) { - E->get().shapes[i].tagged = false; + for (KeyValue<ObjectID, BodyState> &E : contact_monitor->body_map) { + for (int i = 0; i < E.value.shapes.size(); i++) { + E.value.shapes[i].tagged = false; rc++; } } @@ -497,12 +512,12 @@ void RigidDynamicBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) //put the ones to remove - for (Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.front(); E; E = E->next()) { - for (int i = 0; i < E->get().shapes.size(); i++) { - if (!E->get().shapes[i].tagged) { - toremove[toremove_count].rid = E->get().rid; - toremove[toremove_count].body_id = E->key(); - toremove[toremove_count].pair = E->get().shapes[i]; + for (const KeyValue<ObjectID, BodyState> &E : contact_monitor->body_map) { + for (int i = 0; i < E.value.shapes.size(); i++) { + if (!E.value.shapes[i].tagged) { + toremove[toremove_count].rid = E.value.rid; + toremove[toremove_count].body_id = E.key; + toremove[toremove_count].pair = E.value.shapes[i]; toremove_count++; } } @@ -524,29 +539,60 @@ void RigidDynamicBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) } } -void RigidDynamicBody2D::set_mode(Mode p_mode) { - mode = p_mode; - switch (p_mode) { - case MODE_DYNAMIC: { - set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC); - } break; - case MODE_STATIC: { - set_body_mode(PhysicsServer2D::BODY_MODE_STATIC); +void RigidDynamicBody2D::_apply_body_mode() { + if (freeze) { + switch (freeze_mode) { + case FREEZE_MODE_STATIC: { + set_body_mode(PhysicsServer2D::BODY_MODE_STATIC); + } break; + case FREEZE_MODE_KINEMATIC: { + set_body_mode(PhysicsServer2D::BODY_MODE_KINEMATIC); + } break; + } + } else if (lock_rotation) { + set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC_LINEAR); + } else { + set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC); + } +} - } break; - case MODE_KINEMATIC: { - set_body_mode(PhysicsServer2D::BODY_MODE_KINEMATIC); +void RigidDynamicBody2D::set_lock_rotation_enabled(bool p_lock_rotation) { + if (p_lock_rotation == lock_rotation) { + return; + } - } break; - case MODE_DYNAMIC_LOCKED: { - set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC_LOCKED); + lock_rotation = p_lock_rotation; + _apply_body_mode(); +} - } break; +bool RigidDynamicBody2D::is_lock_rotation_enabled() const { + return lock_rotation; +} + +void RigidDynamicBody2D::set_freeze_enabled(bool p_freeze) { + if (p_freeze == freeze) { + return; + } + + freeze = p_freeze; + _apply_body_mode(); +} + +bool RigidDynamicBody2D::is_freeze_enabled() const { + return freeze; +} + +void RigidDynamicBody2D::set_freeze_mode(FreezeMode p_freeze_mode) { + if (p_freeze_mode == freeze_mode) { + return; } + + freeze_mode = p_freeze_mode; + _apply_body_mode(); } -RigidDynamicBody2D::Mode RigidDynamicBody2D::get_mode() const { - return mode; +RigidDynamicBody2D::FreezeMode RigidDynamicBody2D::get_freeze_mode() const { + return freeze_mode; } void RigidDynamicBody2D::set_mass(real_t p_mass) { @@ -778,8 +824,8 @@ TypedArray<Node2D> RigidDynamicBody2D::get_colliding_bodies() const { TypedArray<Node2D> ret; ret.resize(contact_monitor->body_map.size()); int idx = 0; - for (const Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.front(); E; E = E->next()) { - Object *obj = ObjectDB::get_instance(E->key()); + for (const KeyValue<ObjectID, BodyState> &E : contact_monitor->body_map) { + Object *obj = ObjectDB::get_instance(E.key); if (!obj) { ret.resize(ret.size() - 1); //ops } else { @@ -798,9 +844,9 @@ void RigidDynamicBody2D::set_contact_monitor(bool p_enabled) { if (!p_enabled) { ERR_FAIL_COND_MSG(contact_monitor->locked, "Can't disable contact monitoring during in/out callback. Use call_deferred(\"set_contact_monitor\", false) instead."); - for (Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.front(); E; E = E->next()) { + for (const KeyValue<ObjectID, BodyState> &E : contact_monitor->body_map) { //clean up mess - Object *obj = ObjectDB::get_instance(E->key()); + Object *obj = ObjectDB::get_instance(E.key); Node *node = Object::cast_to<Node>(obj); if (node) { @@ -844,17 +890,14 @@ TypedArray<String> RigidDynamicBody2D::get_configuration_warnings() const { TypedArray<String> warnings = CollisionObject2D::get_configuration_warnings(); - if ((get_mode() == MODE_DYNAMIC || get_mode() == MODE_DYNAMIC_LOCKED) && (ABS(t.elements[0].length() - 1.0) > 0.05 || ABS(t.elements[1].length() - 1.0) > 0.05)) { - warnings.push_back(TTR("Size changes to RigidDynamicBody2D (in dynamic modes) will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); + if (ABS(t.elements[0].length() - 1.0) > 0.05 || ABS(t.elements[1].length() - 1.0) > 0.05) { + warnings.push_back(TTR("Size changes to RigidDynamicBody2D will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); } return warnings; } void RigidDynamicBody2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_mode", "mode"), &RigidDynamicBody2D::set_mode); - ClassDB::bind_method(D_METHOD("get_mode"), &RigidDynamicBody2D::get_mode); - ClassDB::bind_method(D_METHOD("set_mass", "mass"), &RigidDynamicBody2D::set_mass); ClassDB::bind_method(D_METHOD("get_mass"), &RigidDynamicBody2D::get_mass); @@ -918,11 +961,19 @@ void RigidDynamicBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_can_sleep", "able_to_sleep"), &RigidDynamicBody2D::set_can_sleep); ClassDB::bind_method(D_METHOD("is_able_to_sleep"), &RigidDynamicBody2D::is_able_to_sleep); + ClassDB::bind_method(D_METHOD("set_lock_rotation_enabled", "lock_rotation"), &RigidDynamicBody2D::set_lock_rotation_enabled); + ClassDB::bind_method(D_METHOD("is_lock_rotation_enabled"), &RigidDynamicBody2D::is_lock_rotation_enabled); + + ClassDB::bind_method(D_METHOD("set_freeze_enabled", "freeze_mode"), &RigidDynamicBody2D::set_freeze_enabled); + ClassDB::bind_method(D_METHOD("is_freeze_enabled"), &RigidDynamicBody2D::is_freeze_enabled); + + ClassDB::bind_method(D_METHOD("set_freeze_mode", "freeze_mode"), &RigidDynamicBody2D::set_freeze_mode); + ClassDB::bind_method(D_METHOD("get_freeze_mode"), &RigidDynamicBody2D::get_freeze_mode); + ClassDB::bind_method(D_METHOD("get_colliding_bodies"), &RigidDynamicBody2D::get_colliding_bodies); GDVIRTUAL_BIND(_integrate_forces, "state"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Dynamic,Static,DynamicLocked,Kinematic"), "set_mode", "get_mode"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_RANGE, "0.01,1000,0.01,or_greater,exp"), "set_mass", "get_mass"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inertia", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater,exp"), "set_inertia", "get_inertia"); ADD_PROPERTY(PropertyInfo(Variant::INT, "center_of_mass_mode", PROPERTY_HINT_ENUM, "Auto,Custom", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_center_of_mass_mode", "get_center_of_mass_mode"); @@ -936,6 +987,9 @@ void RigidDynamicBody2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "contact_monitor"), "set_contact_monitor", "is_contact_monitor_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sleeping"), "set_sleeping", "is_sleeping"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "can_sleep"), "set_can_sleep", "is_able_to_sleep"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "lock_rotation"), "set_lock_rotation_enabled", "is_lock_rotation_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "freeze"), "set_freeze_enabled", "is_freeze_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "freeze_mode", PROPERTY_HINT_ENUM, "Static,Kinematic"), "set_freeze_mode", "get_freeze_mode"); ADD_GROUP("Linear", "linear_"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "linear_velocity"), "set_linear_velocity", "get_linear_velocity"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), "set_linear_damp", "get_linear_damp"); @@ -952,10 +1006,8 @@ void RigidDynamicBody2D::_bind_methods() { ADD_SIGNAL(MethodInfo("body_exited", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); ADD_SIGNAL(MethodInfo("sleeping_state_changed")); - BIND_ENUM_CONSTANT(MODE_DYNAMIC); - BIND_ENUM_CONSTANT(MODE_STATIC); - BIND_ENUM_CONSTANT(MODE_DYNAMIC_LOCKED); - BIND_ENUM_CONSTANT(MODE_KINEMATIC); + BIND_ENUM_CONSTANT(FREEZE_MODE_STATIC); + BIND_ENUM_CONSTANT(FREEZE_MODE_KINEMATIC); BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_AUTO); BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_CUSTOM); @@ -1004,6 +1056,8 @@ bool CharacterBody2D::move_and_slide() { double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time(); Vector2 current_platform_velocity = platform_velocity; + Transform2D gt = get_global_transform(); + previous_position = gt.elements[2]; if ((on_floor || on_wall) && platform_rid.is_valid()) { bool excluded = false; @@ -1016,7 +1070,6 @@ bool CharacterBody2D::move_and_slide() { //this approach makes sure there is less delay between the actual body velocity and the one we saved PhysicsDirectBodyState2D *bs = PhysicsServer2D::get_singleton()->body_get_direct_state(platform_rid); if (bs) { - Transform2D gt = get_global_transform(); Vector2 local_position = gt.elements[2] - bs->get_transform().elements[2]; current_platform_velocity = bs->get_velocity_at_local_position(local_position); } @@ -1026,6 +1079,7 @@ bool CharacterBody2D::move_and_slide() { } motion_results.clear(); + last_motion = Vector2(); bool was_on_floor = on_floor; on_floor = false; @@ -1033,10 +1087,14 @@ bool CharacterBody2D::move_and_slide() { on_wall = false; if (!current_platform_velocity.is_equal_approx(Vector2())) { + PhysicsServer2D::MotionParameters parameters(get_global_transform(), current_platform_velocity * delta, margin); + parameters.exclude_bodies.insert(platform_rid); + if (platform_object_id.is_valid()) { + parameters.exclude_objects.insert(platform_object_id); + } + PhysicsServer2D::MotionResult floor_result; - Set<RID> exclude; - exclude.insert(platform_rid); - if (move_and_collide(current_platform_velocity * delta, floor_result, margin, false, false, false, exclude)) { + if (move_and_collide(parameters, floor_result, false, false)) { motion_results.push_back(floor_result); _set_collision_direction(floor_result); } @@ -1048,23 +1106,33 @@ bool CharacterBody2D::move_and_slide() { _move_and_slide_free(delta); } - if (!on_floor && !on_wall) { + // Compute real velocity. + real_velocity = get_position_delta() / delta; + + if (moving_platform_apply_velocity_on_leave != PLATFORM_VEL_ON_LEAVE_NEVER) { // Add last platform velocity when just left a moving platform. - linear_velocity += current_platform_velocity; + if (!on_floor && !on_wall) { + if (moving_platform_apply_velocity_on_leave == PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY && current_platform_velocity.dot(up_direction) < 0) { + current_platform_velocity = current_platform_velocity.slide(up_direction); + } + motion_velocity += current_platform_velocity; + } } return motion_results.size() > 0; } void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_floor, const Vector2 &p_prev_platform_velocity) { - Vector2 motion = linear_velocity * p_delta; + Vector2 motion = motion_velocity * p_delta; Vector2 motion_slide_up = motion.slide(up_direction); Vector2 prev_floor_normal = floor_normal; RID prev_platform_rid = platform_rid; + ObjectID prev_platform_object_id = platform_object_id; int prev_platform_layer = platform_layer; platform_rid = RID(); + platform_object_id = ObjectID(); floor_normal = Vector2(); platform_velocity = Vector2(); @@ -1073,30 +1141,47 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo bool sliding_enabled = !floor_stop_on_slope; // Constant speed can be applied only the first time sliding is enabled. bool can_apply_constant_speed = sliding_enabled; + // If the platform's ceiling push down the body. + bool apply_ceiling_velocity = false; bool first_slide = true; - bool vel_dir_facing_up = linear_velocity.dot(up_direction) > 0; + bool vel_dir_facing_up = motion_velocity.dot(up_direction) > 0; Vector2 last_travel; for (int iteration = 0; iteration < max_slides; ++iteration) { - PhysicsServer2D::MotionResult result; + PhysicsServer2D::MotionParameters parameters(get_global_transform(), motion, margin); + + Vector2 prev_position = parameters.from.elements[2]; - Vector2 prev_position = get_global_transform().elements[2]; + PhysicsServer2D::MotionResult result; + bool collided = move_and_collide(parameters, result, false, !sliding_enabled); - bool collided = move_and_collide(motion, result, margin, false, !sliding_enabled); + last_motion = result.travel; if (collided) { motion_results.push_back(result); _set_collision_direction(result); - if (on_floor && floor_stop_on_slope && (linear_velocity.normalized() + up_direction).length() < 0.01) { + // If we hit a ceiling platform, we set the vertical motion_velocity to at least the platform one. + if (on_ceiling && result.collider_velocity != Vector2() && result.collider_velocity.dot(up_direction) < 0) { + // If ceiling sliding is on, only apply when the ceiling is flat or when the motion is upward. + if (!slide_on_ceiling || motion.dot(up_direction) < 0 || (result.collision_normal + up_direction).length() < 0.01) { + apply_ceiling_velocity = true; + Vector2 ceiling_vertical_velocity = up_direction * up_direction.dot(result.collider_velocity); + Vector2 motion_vertical_velocity = up_direction * up_direction.dot(motion_velocity); + if (motion_vertical_velocity.dot(up_direction) > 0 || ceiling_vertical_velocity.length_squared() > motion_vertical_velocity.length_squared()) { + motion_velocity = ceiling_vertical_velocity + motion_velocity.slide(up_direction); + } + } + } + + if (on_floor && floor_stop_on_slope && (motion_velocity.normalized() + up_direction).length() < 0.01) { Transform2D gt = get_global_transform(); - if (result.travel.length() > margin) { - gt.elements[2] -= result.travel.slide(up_direction); - } else { + if (result.travel.length() <= margin + CMP_EPSILON) { gt.elements[2] -= result.travel; } set_global_transform(gt); - linear_velocity = Vector2(); + motion_velocity = Vector2(); + last_motion = Vector2(); motion = Vector2(); break; } @@ -1111,7 +1196,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo // Avoid to move forward on a wall if floor_block_on_wall is true. if (p_was_on_floor && !on_floor && !vel_dir_facing_up) { // If the movement is large the body can be prevented from reaching the walls. - if (result.travel.length() <= margin) { + if (result.travel.length() <= margin + CMP_EPSILON) { // Cancels the motion. Transform2D gt = get_global_transform(); gt.elements[2] -= result.travel; @@ -1119,10 +1204,12 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo } on_floor = true; platform_rid = prev_platform_rid; + platform_object_id = prev_platform_object_id; platform_layer = prev_platform_layer; platform_velocity = p_prev_platform_velocity; floor_normal = prev_floor_normal; - linear_velocity = Vector2(); + motion_velocity = Vector2(); + last_motion = Vector2(); motion = Vector2(); break; } @@ -1141,9 +1228,9 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo motion = motion_slide_norm * (motion_slide_up.length() - result.travel.slide(up_direction).length() - last_travel.slide(up_direction).length()); } // Regular sliding, the last part of the test handle the case when you don't want to slide on the ceiling. - else if ((sliding_enabled || !on_floor) && (!on_ceiling || slide_on_ceiling || !vel_dir_facing_up)) { + else if ((sliding_enabled || !on_floor) && (!on_ceiling || slide_on_ceiling || !vel_dir_facing_up) && !apply_ceiling_velocity) { Vector2 slide_motion = result.remainder.slide(result.collision_normal); - if (slide_motion.dot(linear_velocity) > 0.0) { + if (slide_motion.dot(motion_velocity) > 0.0) { motion = slide_motion; } else { motion = Vector2(); @@ -1151,10 +1238,10 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo if (slide_on_ceiling && on_ceiling) { // Apply slide only in the direction of the input motion, otherwise just stop to avoid jittering when moving against a wall. if (vel_dir_facing_up) { - linear_velocity = linear_velocity.slide(result.collision_normal); + motion_velocity = motion_velocity.slide(result.collision_normal); } else { // Avoid acceleration in slope when falling. - linear_velocity = up_direction * up_direction.dot(linear_velocity); + motion_velocity = up_direction * up_direction.dot(motion_velocity); } } } @@ -1162,7 +1249,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo else { motion = result.remainder; if (on_ceiling && !slide_on_ceiling && vel_dir_facing_up) { - linear_velocity = linear_velocity.slide(up_direction); + motion_velocity = motion_velocity.slide(up_direction); motion = motion.slide(up_direction); } } @@ -1196,28 +1283,37 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo // Reset the gravity accumulation when touching the ground. if (on_floor && !vel_dir_facing_up) { - linear_velocity = linear_velocity.slide(up_direction); + motion_velocity = motion_velocity.slide(up_direction); } } void CharacterBody2D::_move_and_slide_free(double p_delta) { - Vector2 motion = linear_velocity * p_delta; + Vector2 motion = motion_velocity * p_delta; platform_rid = RID(); + platform_object_id = ObjectID(); floor_normal = Vector2(); platform_velocity = Vector2(); bool first_slide = true; for (int iteration = 0; iteration < max_slides; ++iteration) { + PhysicsServer2D::MotionParameters parameters(get_global_transform(), motion, margin); + PhysicsServer2D::MotionResult result; + bool collided = move_and_collide(parameters, result, false, false); - bool collided = move_and_collide(motion, result, margin, false, false); + last_motion = result.travel; if (collided) { motion_results.push_back(result); _set_collision_direction(result); - if (free_mode_min_slide_angle != 0 && result.get_angle(-linear_velocity.normalized()) < free_mode_min_slide_angle + FLOOR_ANGLE_THRESHOLD) { + if (result.remainder.is_equal_approx(Vector2())) { + motion = Vector2(); + break; + } + + if (free_mode_min_slide_angle != 0 && result.get_angle(-motion_velocity.normalized()) < free_mode_min_slide_angle + FLOOR_ANGLE_THRESHOLD) { motion = Vector2(); } else if (first_slide) { Vector2 motion_slide_norm = result.remainder.slide(result.collision_normal).normalized(); @@ -1226,28 +1322,32 @@ void CharacterBody2D::_move_and_slide_free(double p_delta) { motion = result.remainder.slide(result.collision_normal); } - if (motion.dot(linear_velocity) <= 0.0) { + if (motion.dot(motion_velocity) <= 0.0) { motion = Vector2(); } } - first_slide = false; - if (!collided || motion.is_equal_approx(Vector2())) { break; } + + first_slide = false; } } void CharacterBody2D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up) { - if (Math::is_equal_approx(floor_snap_length, 0) || on_floor || !was_on_floor || vel_dir_facing_up) { + if (on_floor || !was_on_floor || vel_dir_facing_up) { return; } - Transform2D gt = get_global_transform(); + // Snap by at least collision margin to keep floor state consistent. + real_t length = MAX(floor_snap_length, margin); + + PhysicsServer2D::MotionParameters parameters(get_global_transform(), -up_direction * length, margin); + parameters.collide_separation_ray = true; + PhysicsServer2D::MotionResult result; - if (move_and_collide(up_direction * -floor_snap_length, result, margin, true, false, true)) { - bool apply = true; + if (move_and_collide(parameters, result, true, false)) { if (result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { on_floor = true; floor_normal = result.collision_normal; @@ -1262,24 +1362,26 @@ void CharacterBody2D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up) result.travel = Vector2(); } } - } else { - apply = false; - } - if (apply) { - gt.elements[2] += result.travel; - set_global_transform(gt); + parameters.from.elements[2] += result.travel; + set_global_transform(parameters.from); } } } bool CharacterBody2D::_on_floor_if_snapped(bool was_on_floor, bool vel_dir_facing_up) { - if (Math::is_equal_approx(floor_snap_length, 0) || up_direction == Vector2() || on_floor || !was_on_floor || vel_dir_facing_up) { + if (up_direction == Vector2() || on_floor || !was_on_floor || vel_dir_facing_up) { return false; } + // Snap by at least collision margin to keep floor state consistent. + real_t length = MAX(floor_snap_length, margin); + + PhysicsServer2D::MotionParameters parameters(get_global_transform(), -up_direction * length, margin); + parameters.collide_separation_ray = true; + PhysicsServer2D::MotionResult result; - if (move_and_collide(up_direction * -floor_snap_length, result, margin, true, false, true)) { + if (move_and_collide(parameters, result, true, false)) { if (result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { return true; } @@ -1297,6 +1399,7 @@ void CharacterBody2D::_set_collision_direction(const PhysicsServer2D::MotionResu on_ceiling = true; } else { on_wall = true; + wall_normal = p_result.collision_normal; // Don't apply wall velocity when the collider is a CharacterBody2D. if (Object::cast_to<CharacterBody2D>(ObjectDB::get_instance(p_result.collider_id)) == nullptr) { _set_platform_data(p_result); @@ -1306,16 +1409,17 @@ void CharacterBody2D::_set_collision_direction(const PhysicsServer2D::MotionResu void CharacterBody2D::_set_platform_data(const PhysicsServer2D::MotionResult &p_result) { platform_rid = p_result.collider; + platform_object_id = p_result.collider_id; platform_velocity = p_result.collider_velocity; platform_layer = PhysicsServer2D::get_singleton()->body_get_collision_layer(platform_rid); } -const Vector2 &CharacterBody2D::get_linear_velocity() const { - return linear_velocity; +const Vector2 &CharacterBody2D::get_motion_velocity() const { + return motion_velocity; } -void CharacterBody2D::set_linear_velocity(const Vector2 &p_velocity) { - linear_velocity = p_velocity; +void CharacterBody2D::set_motion_velocity(const Vector2 &p_velocity) { + motion_velocity = p_velocity; } bool CharacterBody2D::is_on_floor() const { @@ -1342,16 +1446,32 @@ bool CharacterBody2D::is_on_ceiling_only() const { return on_ceiling && !on_floor && !on_wall; } -Vector2 CharacterBody2D::get_floor_normal() const { +const Vector2 &CharacterBody2D::get_floor_normal() const { return floor_normal; } +const Vector2 &CharacterBody2D::get_wall_normal() const { + return wall_normal; +} + +const Vector2 &CharacterBody2D::get_last_motion() const { + return last_motion; +} + +Vector2 CharacterBody2D::get_position_delta() const { + return get_global_transform().elements[2] - previous_position; +} + +const Vector2 &CharacterBody2D::get_real_velocity() const { + return real_velocity; +} + real_t CharacterBody2D::get_floor_angle(const Vector2 &p_up_direction) const { ERR_FAIL_COND_V(p_up_direction == Vector2(), 0); return Math::acos(floor_normal.dot(p_up_direction)); } -Vector2 CharacterBody2D::get_platform_velocity() const { +const Vector2 &CharacterBody2D::get_platform_velocity() const { return platform_velocity; } @@ -1451,6 +1571,14 @@ CharacterBody2D::MotionMode CharacterBody2D::get_motion_mode() const { return motion_mode; } +void CharacterBody2D::set_moving_platform_apply_velocity_on_leave(MovingPlatformApplyVelocityOnLeave p_on_leave_apply_velocity) { + moving_platform_apply_velocity_on_leave = p_on_leave_apply_velocity; +} + +CharacterBody2D::MovingPlatformApplyVelocityOnLeave CharacterBody2D::get_moving_platform_apply_velocity_on_leave() const { + return moving_platform_apply_velocity_on_leave; +} + int CharacterBody2D::get_max_slides() const { return max_slides; } @@ -1500,6 +1628,7 @@ void CharacterBody2D::_notification(int p_what) { // Reset move_and_slide() data. on_floor = false; platform_rid = RID(); + platform_object_id = ObjectID(); on_ceiling = false; on_wall = false; motion_results.clear(); @@ -1511,8 +1640,8 @@ void CharacterBody2D::_notification(int p_what) { void CharacterBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("move_and_slide"), &CharacterBody2D::move_and_slide); - ClassDB::bind_method(D_METHOD("set_linear_velocity", "linear_velocity"), &CharacterBody2D::set_linear_velocity); - ClassDB::bind_method(D_METHOD("get_linear_velocity"), &CharacterBody2D::get_linear_velocity); + ClassDB::bind_method(D_METHOD("set_motion_velocity", "motion_velocity"), &CharacterBody2D::set_motion_velocity); + ClassDB::bind_method(D_METHOD("get_motion_velocity"), &CharacterBody2D::get_motion_velocity); ClassDB::bind_method(D_METHOD("set_safe_margin", "pixels"), &CharacterBody2D::set_safe_margin); ClassDB::bind_method(D_METHOD("get_safe_margin"), &CharacterBody2D::get_safe_margin); @@ -1542,6 +1671,8 @@ void CharacterBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_up_direction", "up_direction"), &CharacterBody2D::set_up_direction); ClassDB::bind_method(D_METHOD("set_motion_mode", "mode"), &CharacterBody2D::set_motion_mode); ClassDB::bind_method(D_METHOD("get_motion_mode"), &CharacterBody2D::get_motion_mode); + ClassDB::bind_method(D_METHOD("set_moving_platform_apply_velocity_on_leave", "on_leave_apply_velocity"), &CharacterBody2D::set_moving_platform_apply_velocity_on_leave); + ClassDB::bind_method(D_METHOD("get_moving_platform_apply_velocity_on_leave"), &CharacterBody2D::get_moving_platform_apply_velocity_on_leave); ClassDB::bind_method(D_METHOD("is_on_floor"), &CharacterBody2D::is_on_floor); ClassDB::bind_method(D_METHOD("is_on_floor_only"), &CharacterBody2D::is_on_floor_only); @@ -1550,6 +1681,10 @@ void CharacterBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("is_on_wall"), &CharacterBody2D::is_on_wall); ClassDB::bind_method(D_METHOD("is_on_wall_only"), &CharacterBody2D::is_on_wall_only); ClassDB::bind_method(D_METHOD("get_floor_normal"), &CharacterBody2D::get_floor_normal); + ClassDB::bind_method(D_METHOD("get_wall_normal"), &CharacterBody2D::get_wall_normal); + ClassDB::bind_method(D_METHOD("get_last_motion"), &CharacterBody2D::get_last_motion); + ClassDB::bind_method(D_METHOD("get_position_delta"), &CharacterBody2D::get_position_delta); + ClassDB::bind_method(D_METHOD("get_real_velocity"), &CharacterBody2D::get_real_velocity); ClassDB::bind_method(D_METHOD("get_floor_angle", "up_direction"), &CharacterBody2D::get_floor_angle, DEFVAL(Vector2(0.0, -1.0))); ClassDB::bind_method(D_METHOD("get_platform_velocity"), &CharacterBody2D::get_platform_velocity); ClassDB::bind_method(D_METHOD("get_slide_collision_count"), &CharacterBody2D::get_slide_collision_count); @@ -1557,10 +1692,11 @@ void CharacterBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_last_slide_collision"), &CharacterBody2D::_get_last_slide_collision); ADD_PROPERTY(PropertyInfo(Variant::INT, "motion_mode", PROPERTY_HINT_ENUM, "Grounded,Free", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_motion_mode", "get_motion_mode"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "linear_velocity"), "set_linear_velocity", "get_linear_velocity"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "up_direction"), "set_up_direction", "get_up_direction"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "motion_velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_motion_velocity", "get_motion_velocity"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_ceiling"), "set_slide_on_ceiling_enabled", "is_slide_on_ceiling_enabled"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_max_slides", "get_max_slides"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "up_direction"), "set_up_direction", "get_up_direction"); + ADD_GROUP("Free Mode", "free_mode_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "free_mode_min_slide_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians", PROPERTY_USAGE_DEFAULT), "set_free_mode_min_slide_angle", "get_free_mode_min_slide_angle"); ADD_GROUP("Floor", "floor_"); @@ -1568,14 +1704,19 @@ void CharacterBody2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_constant_speed"), "set_floor_constant_speed_enabled", "is_floor_constant_speed_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_block_on_wall"), "set_floor_block_on_wall_enabled", "is_floor_block_on_wall_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians"), "set_floor_max_angle", "get_floor_max_angle"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_snap_length", PROPERTY_HINT_RANGE, "0,1000,0.1"), "set_floor_snap_length", "get_floor_snap_length"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_snap_length", PROPERTY_HINT_RANGE, "0,32,0.1,or_greater"), "set_floor_snap_length", "get_floor_snap_length"); ADD_GROUP("Moving platform", "moving_platform"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_apply_velocity_on_leave", PROPERTY_HINT_ENUM, "Always,Upward Only,Never", PROPERTY_USAGE_DEFAULT), "set_moving_platform_apply_velocity_on_leave", "get_moving_platform_apply_velocity_on_leave"); ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_floor_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_floor_layers", "get_moving_platform_floor_layers"); ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_wall_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_wall_layers", "get_moving_platform_wall_layers"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin"); BIND_ENUM_CONSTANT(MOTION_MODE_GROUNDED); BIND_ENUM_CONSTANT(MOTION_MODE_FREE); + + BIND_ENUM_CONSTANT(PLATFORM_VEL_ON_LEAVE_ALWAYS); + BIND_ENUM_CONSTANT(PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY); + BIND_ENUM_CONSTANT(PLATFORM_VEL_ON_LEAVE_NEVER); } void CharacterBody2D::_validate_property(PropertyInfo &property) const { @@ -1670,10 +1811,6 @@ Vector2 KinematicCollision2D::get_collider_velocity() const { return result.collider_velocity; } -Variant KinematicCollision2D::get_collider_metadata() const { - return Variant(); -} - void KinematicCollision2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_position"), &KinematicCollision2D::get_position); ClassDB::bind_method(D_METHOD("get_normal"), &KinematicCollision2D::get_normal); @@ -1687,18 +1824,4 @@ void KinematicCollision2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_collider_shape"), &KinematicCollision2D::get_collider_shape); ClassDB::bind_method(D_METHOD("get_collider_shape_index"), &KinematicCollision2D::get_collider_shape_index); ClassDB::bind_method(D_METHOD("get_collider_velocity"), &KinematicCollision2D::get_collider_velocity); - ClassDB::bind_method(D_METHOD("get_collider_metadata"), &KinematicCollision2D::get_collider_metadata); - - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position"), "", "get_position"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "normal"), "", "get_normal"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "travel"), "", "get_travel"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "remainder"), "", "get_remainder"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "local_shape"), "", "get_local_shape"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "collider"), "", "get_collider"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "collider_id"), "", "get_collider_id"); - ADD_PROPERTY(PropertyInfo(Variant::RID, "collider_rid"), "", "get_collider_rid"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "collider_shape"), "", "get_collider_shape"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "collider_shape_index"), "", "get_collider_shape_index"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "collider_velocity"), "", "get_collider_velocity"); - ADD_PROPERTY(PropertyInfo(Variant::NIL, "collider_metadata", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT), "", "get_collider_metadata"); } diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h index 5d0d98a2df..d1f52b33f2 100644 --- a/scene/2d/physics_body_2d.h +++ b/scene/2d/physics_body_2d.h @@ -47,11 +47,11 @@ protected: Ref<KinematicCollision2D> motion_cache; - Ref<KinematicCollision2D> _move(const Vector2 &p_motion, bool p_test_only = false, real_t p_margin = 0.08); + Ref<KinematicCollision2D> _move(const Vector2 &p_linear_velocity, bool p_test_only = false, real_t p_margin = 0.08); public: - bool move_and_collide(const Vector2 &p_motion, PhysicsServer2D::MotionResult &r_result, real_t p_margin, bool p_test_only = false, bool p_cancel_sliding = true, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>()); - bool test_move(const Transform2D &p_from, const Vector2 &p_motion, const Ref<KinematicCollision2D> &r_collision = Ref<KinematicCollision2D>(), real_t p_margin = 0.08); + bool move_and_collide(const PhysicsServer2D::MotionParameters &p_parameters, PhysicsServer2D::MotionResult &r_result, bool p_test_only = false, bool p_cancel_sliding = true); + bool test_move(const Transform2D &p_from, const Vector2 &p_linear_velocity, const Ref<KinematicCollision2D> &r_collision = Ref<KinematicCollision2D>(), real_t p_margin = 0.08); TypedArray<PhysicsBody2D> get_collision_exceptions(); void add_collision_exception_with(Node *p_node); //must be physicsbody @@ -92,7 +92,7 @@ class AnimatableBody2D : public StaticBody2D { GDCLASS(AnimatableBody2D, StaticBody2D); private: - bool sync_to_physics = false; + bool sync_to_physics = true; Transform2D last_valid_transform; @@ -117,11 +117,9 @@ class RigidDynamicBody2D : public PhysicsBody2D { GDCLASS(RigidDynamicBody2D, PhysicsBody2D); public: - enum Mode { - MODE_DYNAMIC, - MODE_STATIC, - MODE_DYNAMIC_LOCKED, - MODE_KINEMATIC, + enum FreezeMode { + FREEZE_MODE_STATIC, + FREEZE_MODE_KINEMATIC, }; enum CenterOfMassMode { @@ -137,7 +135,9 @@ public: private: bool can_sleep = true; - Mode mode = MODE_DYNAMIC; + bool lock_rotation = false; + bool freeze = false; + FreezeMode freeze_mode = FREEZE_MODE_STATIC; real_t mass = 1.0; real_t inertia = 0.0; @@ -211,9 +211,17 @@ protected: GDVIRTUAL1(_integrate_forces, PhysicsDirectBodyState2D *) + void _apply_body_mode(); + public: - void set_mode(Mode p_mode); - Mode get_mode() const; + void set_lock_rotation_enabled(bool p_lock_rotation); + bool is_lock_rotation_enabled() const; + + void set_freeze_enabled(bool p_freeze); + bool is_freeze_enabled() const; + + void set_freeze_mode(FreezeMode p_freeze_mode); + FreezeMode get_freeze_mode() const; void set_mass(real_t p_mass); real_t get_mass() const; @@ -290,7 +298,7 @@ private: void _reload_physics_characteristics(); }; -VARIANT_ENUM_CAST(RigidDynamicBody2D::Mode); +VARIANT_ENUM_CAST(RigidDynamicBody2D::FreezeMode); VARIANT_ENUM_CAST(RigidDynamicBody2D::CenterOfMassMode); VARIANT_ENUM_CAST(RigidDynamicBody2D::CCDMode); @@ -302,10 +310,15 @@ public: MOTION_MODE_GROUNDED, MOTION_MODE_FREE, }; + enum MovingPlatformApplyVelocityOnLeave { + PLATFORM_VEL_ON_LEAVE_ALWAYS, + PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY, + PLATFORM_VEL_ON_LEAVE_NEVER, + }; bool move_and_slide(); - const Vector2 &get_linear_velocity() const; - void set_linear_velocity(const Vector2 &p_velocity); + const Vector2 &get_motion_velocity() const; + void set_motion_velocity(const Vector2 &p_velocity); bool is_on_floor() const; bool is_on_floor_only() const; @@ -313,9 +326,14 @@ public: bool is_on_wall_only() const; bool is_on_ceiling() const; bool is_on_ceiling_only() const; - Vector2 get_floor_normal() const; + const Vector2 &get_last_motion() const; + Vector2 get_position_delta() const; + const Vector2 &get_floor_normal() const; + const Vector2 &get_wall_normal() const; + const Vector2 &get_real_velocity() const; + real_t get_floor_angle(const Vector2 &p_up_direction = Vector2(0.0, -1.0)) const; - Vector2 get_platform_velocity() const; + const Vector2 &get_platform_velocity() const; int get_slide_collision_count() const; PhysicsServer2D::MotionResult get_slide_collision(int p_bounce) const; @@ -326,24 +344,31 @@ public: private: real_t margin = 0.08; MotionMode motion_mode = MOTION_MODE_GROUNDED; + MovingPlatformApplyVelocityOnLeave moving_platform_apply_velocity_on_leave = PLATFORM_VEL_ON_LEAVE_ALWAYS; - bool floor_stop_on_slope = false; bool floor_constant_speed = false; + bool floor_stop_on_slope = true; bool floor_block_on_wall = true; bool slide_on_ceiling = true; int max_slides = 4; - int platform_layer; + int platform_layer = 0; real_t floor_max_angle = Math::deg2rad((real_t)45.0); - real_t floor_snap_length = 0; + real_t floor_snap_length = 1; real_t free_mode_min_slide_angle = Math::deg2rad((real_t)15.0); Vector2 up_direction = Vector2(0.0, -1.0); uint32_t moving_platform_floor_layers = UINT32_MAX; uint32_t moving_platform_wall_layers = 0; - Vector2 linear_velocity; + Vector2 motion_velocity; Vector2 floor_normal; Vector2 platform_velocity; + Vector2 wall_normal; + Vector2 last_motion; + Vector2 previous_position; + Vector2 real_velocity; + RID platform_rid; + ObjectID platform_object_id; bool on_floor = false; bool on_ceiling = false; bool on_wall = false; @@ -387,6 +412,9 @@ private: void set_motion_mode(MotionMode p_mode); MotionMode get_motion_mode() const; + void set_moving_platform_apply_velocity_on_leave(MovingPlatformApplyVelocityOnLeave p_on_leave_velocity); + MovingPlatformApplyVelocityOnLeave get_moving_platform_apply_velocity_on_leave() const; + void _move_and_slide_free(double p_delta); void _move_and_slide_grounded(double p_delta, bool p_was_on_floor, const Vector2 &p_prev_platform_velocity); @@ -406,6 +434,7 @@ protected: }; VARIANT_ENUM_CAST(CharacterBody2D::MotionMode); +VARIANT_ENUM_CAST(CharacterBody2D::MovingPlatformApplyVelocityOnLeave); class KinematicCollision2D : public RefCounted { GDCLASS(KinematicCollision2D, RefCounted); @@ -431,7 +460,6 @@ public: Object *get_collider_shape() const; int get_collider_shape_index() const; Vector2 get_collider_velocity() const; - Variant get_collider_metadata() const; }; #endif // PHYSICS_BODY_2D_H diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 03db9c0d32..c2f150ce00 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -51,8 +51,8 @@ void TileMapPattern::remove_cell(const Vector2i &p_coords, bool p_update_size) { pattern.erase(p_coords); if (p_update_size) { size = Vector2i(); - for (Map<Vector2i, TileMapCell>::Element *E = pattern.front(); E; E = E->next()) { - size = size.max(E->key() + Vector2i(1, 1)); + for (const KeyValue<Vector2i, TileMapCell> &E : pattern) { + size = size.max(E.key + Vector2i(1, 1)); } } } @@ -80,8 +80,8 @@ TypedArray<Vector2i> TileMapPattern::get_used_cells() const { TypedArray<Vector2i> a; a.resize(pattern.size()); int i = 0; - for (Map<Vector2i, TileMapCell>::Element *E = pattern.front(); E; E = E->next()) { - Vector2i p(E->key().x, E->key().y); + for (const KeyValue<Vector2i, TileMapCell> &E : pattern) { + Vector2i p(E.key.x, E.key.y); a[i++] = p; } @@ -93,8 +93,8 @@ Vector2i TileMapPattern::get_size() const { } void TileMapPattern::set_size(const Vector2i &p_size) { - for (Map<Vector2i, TileMapCell>::Element *E = pattern.front(); E; E = E->next()) { - Vector2i coords = E->key(); + for (const KeyValue<Vector2i, TileMapCell> &E : pattern) { + Vector2i coords = E.key; if (p_size.x <= coords.x || p_size.y <= coords.y) { ERR_FAIL_MSG(vformat("Cannot set pattern size to %s, it contains a tile at %s. Size can only be increased.", p_size, coords)); }; @@ -409,6 +409,19 @@ bool TileMap::is_layer_enabled(int p_layer) const { return layers[p_layer].enabled; } +void TileMap::set_layer_modulate(int p_layer, Color p_modulate) { + ERR_FAIL_INDEX(p_layer, (int)layers.size()); + layers[p_layer].modulate = p_modulate; + _clear_internals(); + _recreate_internals(); + emit_signal(SNAME("changed")); +} + +Color TileMap::get_layer_modulate(int p_layer) const { + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), Color()); + return layers[p_layer].modulate; +} + void TileMap::set_layer_y_sort_enabled(int p_layer, bool p_y_sort_enabled) { ERR_FAIL_INDEX(p_layer, (int)layers.size()); layers[p_layer].y_sort_enabled = p_y_sort_enabled; @@ -452,6 +465,19 @@ int TileMap::get_layer_z_index(int p_layer) const { return layers[p_layer].z_index; } +void TileMap::set_collision_animatable(bool p_enabled) { + collision_animatable = p_enabled; + _clear_internals(); + set_notify_local_transform(p_enabled); + set_physics_process_internal(p_enabled); + _recreate_internals(); + emit_signal(SNAME("changed")); +} + +bool TileMap::is_collision_animatable() const { + return collision_animatable; +} + void TileMap::set_collision_visibility_mode(TileMap::VisibilityMode p_show_collision) { collision_visibility_mode = p_show_collision; _clear_internals(); @@ -508,7 +534,6 @@ Map<Vector2i, TileMapQuadrant>::Element *TileMap::_create_quadrant(int p_layer, // Call the create_quadrant method on plugins if (tile_set.is_valid()) { _rendering_create_quadrant(&q); - _physics_create_quadrant(&q); } return layers[p_layer].quadrant_map.insert(p_qk, q); @@ -526,9 +551,9 @@ void TileMap::_make_quadrant_dirty(Map<Vector2i, TileMapQuadrant>::Element *Q) { void TileMap::_make_all_quadrants_dirty() { // Make all quandrants dirty, then trigger an update later. for (unsigned int layer = 0; layer < layers.size(); layer++) { - for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) { - if (!E->value().dirty_list_element.in_list()) { - layers[layer].dirty_quadrant_list.add(&E->value().dirty_list_element); + for (KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) { + if (!E.value.dirty_list_element.in_list()) { + layers[layer].dirty_quadrant_list.add(&E.value.dirty_list_element); } } } @@ -610,8 +635,8 @@ void TileMap::_recreate_internals() { // Recreate the quadrants. const Map<Vector2i, TileMapCell> &tile_map = layers[layer].tile_map; - for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) { - Vector2i qk = _coords_to_quadrant_coords(layer, Vector2i(E->key().x, E->key().y)); + for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) { + Vector2i qk = _coords_to_quadrant_coords(layer, Vector2i(E.key.x, E.key.y)); Map<Vector2i, TileMapQuadrant>::Element *Q = layers[layer].quadrant_map.find(qk); if (!Q) { @@ -619,7 +644,7 @@ void TileMap::_recreate_internals() { layers[layer].dirty_quadrant_list.add(&Q->get().dirty_list_element); } - Vector2i pk = E->key(); + Vector2i pk = E.key; Q->get().cells.insert(pk); _make_quadrant_dirty(Q); @@ -688,7 +713,7 @@ void TileMap::_recompute_rect_cache() { Rect2 r_total; for (unsigned int layer = 0; layer < layers.size(); layer++) { - for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) { + for (const Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) { Rect2 r; r.position = map_to_world(E->key() * get_effective_quadrant_size(layer)); r.expand_to(map_to_world((E->key() + Vector2i(1, 0)) * get_effective_quadrant_size(layer))); @@ -717,13 +742,13 @@ void TileMap::_rendering_notification(int p_what) { case CanvasItem::NOTIFICATION_VISIBILITY_CHANGED: { bool visible = is_visible_in_tree(); for (int layer = 0; layer < (int)layers.size(); layer++) { - for (Map<Vector2i, TileMapQuadrant>::Element *E_quadrant = layers[layer].quadrant_map.front(); E_quadrant; E_quadrant = E_quadrant->next()) { - TileMapQuadrant &q = E_quadrant->get(); + for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : layers[layer].quadrant_map) { + TileMapQuadrant &q = E_quadrant.value; // Update occluders transform. - for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E_cell = q.world_to_map.front(); E_cell; E_cell = E_cell->next()) { + for (const KeyValue<Vector2i, Vector2i> &E_cell : q.world_to_map) { Transform2D xform; - xform.set_origin(E_cell->key()); + xform.set_origin(E_cell.key); for (const RID &occluder : q.occluders) { RS::get_singleton()->canvas_light_occluder_set_enabled(occluder, visible); } @@ -736,13 +761,13 @@ void TileMap::_rendering_notification(int p_what) { return; } for (int layer = 0; layer < (int)layers.size(); layer++) { - for (Map<Vector2i, TileMapQuadrant>::Element *E_quadrant = layers[layer].quadrant_map.front(); E_quadrant; E_quadrant = E_quadrant->next()) { - TileMapQuadrant &q = E_quadrant->get(); + for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : layers[layer].quadrant_map) { + TileMapQuadrant &q = E_quadrant.value; // Update occluders transform. - for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E_cell = q.world_to_map.front(); E_cell; E_cell = E_cell->next()) { + for (const KeyValue<Vector2i, Vector2i> &E_cell : q.world_to_map) { Transform2D xform; - xform.set_origin(E_cell->key()); + xform.set_origin(E_cell.key); for (const RID &occluder : q.occluders) { RS::get_singleton()->canvas_light_occluder_set_transform(occluder, get_global_transform() * xform); } @@ -786,8 +811,9 @@ void TileMap::_rendering_cleanup_layer(int p_layer) { ERR_FAIL_INDEX(p_layer, (int)layers.size()); RenderingServer *rs = RenderingServer::get_singleton(); - if (!layers[p_layer].canvas_item.is_valid()) { + if (layers[p_layer].canvas_item.is_valid()) { rs->free(layers[p_layer].canvas_item); + layers[p_layer].canvas_item = RID(); } } @@ -820,9 +846,22 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List int prev_z_index = 0; RID prev_canvas_item; + Color modulate = get_self_modulate(); + modulate *= get_layer_modulate(q.layer); + if (selected_layer >= 0) { + int z1 = get_layer_z_index(q.layer); + int z2 = get_layer_z_index(selected_layer); + if (z1 < z2 || (z1 == z2 && q.layer < selected_layer)) { + modulate = modulate.darkened(0.5); + } else if (z1 > z2 || (z1 == z2 && q.layer > selected_layer)) { + modulate = modulate.darkened(0.5); + modulate.a *= 0.3; + } + } + // Iterate over the cells of the quadrant. - for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E_cell = q.world_to_map.front(); E_cell; E_cell = E_cell->next()) { - TileMapCell c = get_cell(q.layer, E_cell->value(), true); + for (const KeyValue<Vector2i, Vector2i> &E_cell : q.world_to_map) { + TileMapCell c = get_cell(q.layer, E_cell.value, true); TileSetSource *source; if (tile_set->has_source(c.source_id)) { @@ -882,21 +921,12 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List } // Drawing the tile in the canvas item. - Color modulate = get_self_modulate(); - if (selected_layer >= 0) { - if (q.layer < selected_layer) { - modulate = modulate.darkened(0.5); - } else if (q.layer > selected_layer) { - modulate = modulate.darkened(0.5); - modulate.a *= 0.3; - } - } - draw_tile(canvas_item, E_cell->key() - position, tile_set, c.source_id, c.get_atlas_coords(), c.alternative_tile, -1, modulate); + draw_tile(canvas_item, E_cell.key - position, tile_set, c.source_id, c.get_atlas_coords(), c.alternative_tile, -1, modulate); // --- Occluders --- for (int i = 0; i < tile_set->get_occlusion_layers_count(); i++) { Transform2D xform; - xform.set_origin(E_cell->key()); + xform.set_origin(E_cell.key); if (tile_data->get_occluder(i).is_valid()) { RID occluder_id = rs->canvas_light_occluder_create(); rs->canvas_light_occluder_set_enabled(occluder_id, visible); @@ -922,13 +952,13 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List for (int layer = 0; layer < (int)layers.size(); layer++) { // Sort the quadrants coords per world coordinates Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator> world_to_map; - for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) { - world_to_map[map_to_world(E->key())] = E->key(); + for (const KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) { + world_to_map[map_to_world(E.key)] = E.key; } // Sort the quadrants - for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E = world_to_map.front(); E; E = E->next()) { - TileMapQuadrant &q = layers[layer].quadrant_map[E->value()]; + for (const KeyValue<Vector2i, Vector2i> &E : world_to_map) { + TileMapQuadrant &q = layers[layer].quadrant_map[E.value]; for (const RID &ci : q.canvas_items) { RS::get_singleton()->canvas_item_set_draw_index(ci, index++); } @@ -1092,24 +1122,67 @@ void TileMap::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSe void TileMap::_physics_notification(int p_what) { switch (p_what) { + case CanvasItem::NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { + bool in_editor = false; +#ifdef TOOLS_ENABLED + in_editor = Engine::get_singleton()->is_editor_hint(); +#endif + if (is_inside_tree() && collision_animatable && !in_editor) { + // Update tranform on the physics tick when in animatable mode. + last_valid_transform = new_transform; + set_notify_local_transform(false); + set_global_transform(new_transform); + set_notify_local_transform(true); + } + } break; case CanvasItem::NOTIFICATION_TRANSFORM_CHANGED: { - // Update the bodies transforms. - if (is_inside_tree()) { + bool in_editor = false; +#ifdef TOOLS_ENABLED + in_editor = Engine::get_singleton()->is_editor_hint(); +#endif + if (is_inside_tree() && (!collision_animatable || in_editor)) { + // Update the new transform directly if we are not in animatable mode. + Transform2D global_transform = get_global_transform(); for (int layer = 0; layer < (int)layers.size(); layer++) { - Transform2D global_transform = get_global_transform(); + for (KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) { + TileMapQuadrant &q = E.value; - for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) { - TileMapQuadrant &q = E->get(); + for (RID body : q.bodies) { + Transform2D xform; + xform.set_origin(map_to_world(bodies_coords[body])); + xform = global_transform * xform; + PhysicsServer2D::get_singleton()->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform); + } + } + } + } + } break; + case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { + bool in_editor = false; +#ifdef TOOLS_ENABLED + in_editor = Engine::get_singleton()->is_editor_hint(); +#endif + if (is_inside_tree() && !in_editor && collision_animatable) { + // Only active when animatable. Send the new transform to the physics... + new_transform = get_global_transform(); + for (int layer = 0; layer < (int)layers.size(); layer++) { + for (KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) { + TileMapQuadrant &q = E.value; - Transform2D xform; - xform.set_origin(map_to_world(E->key() * get_effective_quadrant_size(layer))); - xform = global_transform * xform; + for (RID body : q.bodies) { + Transform2D xform; + xform.set_origin(map_to_world(bodies_coords[body])); + xform = new_transform * xform; - for (int body_index = 0; body_index < q.bodies.size(); body_index++) { - PhysicsServer2D::get_singleton()->body_set_state(q.bodies[body_index], PhysicsServer2D::BODY_STATE_TRANSFORM, xform); + PhysicsServer2D::get_singleton()->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform); } } } + + // ... but then revert changes. + set_notify_local_transform(false); + set_global_transform(last_valid_transform); + set_notify_local_transform(true); } } break; } @@ -1120,29 +1193,23 @@ void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r ERR_FAIL_COND(!tile_set.is_valid()); Transform2D global_transform = get_global_transform(); + last_valid_transform = global_transform; + new_transform = global_transform; PhysicsServer2D *ps = PhysicsServer2D::get_singleton(); + RID space = get_world_2d()->get_space(); SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first(); while (q_list_element) { TileMapQuadrant &q = *q_list_element->self(); - Vector2 quadrant_pos = map_to_world(q.coords * get_effective_quadrant_size(q.layer)); - - LocalVector<int> body_shape_count; - body_shape_count.resize(q.bodies.size()); - - // Clear shapes. - for (int body_index = 0; body_index < q.bodies.size(); body_index++) { - ps->body_clear_shapes(q.bodies[body_index]); - body_shape_count[body_index] = 0; - - // Position the bodies. - Transform2D xform; - xform.set_origin(quadrant_pos); - xform = global_transform * xform; - ps->body_set_state(q.bodies[body_index], PhysicsServer2D::BODY_STATE_TRANSFORM, xform); + // Clear bodies. + for (RID body : q.bodies) { + bodies_coords.erase(body); + ps->free(body); } + q.bodies.clear(); + // Recreate bodies and shapes. for (Set<Vector2i>::Element *E_cell = q.cells.front(); E_cell; E_cell = E_cell->next()) { TileMapCell c = get_cell(q.layer, E_cell->get(), true); @@ -1158,26 +1225,53 @@ void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r if (atlas_source) { TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); - for (int body_index = 0; body_index < q.bodies.size(); body_index++) { - int &body_shape_index = body_shape_count[body_index]; + for (int tile_set_physics_layer = 0; tile_set_physics_layer < tile_set->get_physics_layers_count(); tile_set_physics_layer++) { + Ref<PhysicsMaterial> physics_material = tile_set->get_physics_layer_physics_material(tile_set_physics_layer); + uint32_t physics_layer = tile_set->get_physics_layer_collision_layer(tile_set_physics_layer); + uint32_t physics_mask = tile_set->get_physics_layer_collision_mask(tile_set_physics_layer); - // Add the shapes again. - for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(body_index); polygon_index++) { - bool one_way_collision = tile_data->is_collision_polygon_one_way(body_index, polygon_index); - float one_way_collision_margin = tile_data->get_collision_polygon_one_way_margin(body_index, polygon_index); + // Create the body. + RID body = ps->body_create(); + bodies_coords[body] = E_cell->get(); + ps->body_set_mode(body, collision_animatable ? PhysicsServer2D::BODY_MODE_KINEMATIC : PhysicsServer2D::BODY_MODE_STATIC); + ps->body_set_space(body, space); - int shapes_count = tile_data->get_collision_polygon_shapes_count(body_index, polygon_index); - for (int shape_index = 0; shape_index < shapes_count; shape_index++) { - Transform2D xform = Transform2D(); - xform.set_origin(map_to_world(E_cell->get()) - quadrant_pos); + Transform2D xform; + xform.set_origin(map_to_world(E_cell->get())); + xform = global_transform * xform; + ps->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform); + + ps->body_attach_object_instance_id(body, get_instance_id()); + ps->body_set_collision_layer(body, physics_layer); + ps->body_set_collision_mask(body, physics_mask); + ps->body_set_pickable(body, false); + ps->body_set_state(body, PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY, tile_data->get_constant_linear_velocity(tile_set_physics_layer)); + ps->body_set_state(body, PhysicsServer2D::BODY_STATE_ANGULAR_VELOCITY, tile_data->get_constant_angular_velocity(tile_set_physics_layer)); + + if (!physics_material.is_valid()) { + ps->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, 0); + ps->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, 1); + } else { + ps->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, physics_material->computed_bounce()); + ps->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, physics_material->computed_friction()); + } + + q.bodies.push_back(body); + // Add the shapes to the body. + int body_shape_index = 0; + for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(tile_set_physics_layer); polygon_index++) { + // Iterate over the polygons. + bool one_way_collision = tile_data->is_collision_polygon_one_way(tile_set_physics_layer, polygon_index); + float one_way_collision_margin = tile_data->get_collision_polygon_one_way_margin(tile_set_physics_layer, polygon_index); + int shapes_count = tile_data->get_collision_polygon_shapes_count(tile_set_physics_layer, polygon_index); + for (int shape_index = 0; shape_index < shapes_count; shape_index++) { // Add decomposed convex shapes. - Ref<ConvexPolygonShape2D> shape = tile_data->get_collision_polygon_shape(body_index, polygon_index, shape_index); - ps->body_add_shape(q.bodies[body_index], shape->get_rid(), xform); - ps->body_set_shape_metadata(q.bodies[body_index], body_shape_index, E_cell->get()); - ps->body_set_shape_as_one_way_collision(q.bodies[body_index], body_shape_index, one_way_collision, one_way_collision_margin); + Ref<ConvexPolygonShape2D> shape = tile_data->get_collision_polygon_shape(tile_set_physics_layer, polygon_index, shape_index); + ps->body_add_shape(body, shape->get_rid()); + ps->body_set_shape_as_one_way_collision(body, body_shape_index, one_way_collision, one_way_collision_margin); - ++body_shape_index; + body_shape_index++; } } } @@ -1189,54 +1283,11 @@ void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r } } -void TileMap::_physics_create_quadrant(TileMapQuadrant *p_quadrant) { - ERR_FAIL_COND(!tile_set.is_valid()); - - //Get the TileMap's gobla transform. - Transform2D global_transform; - if (is_inside_tree()) { - global_transform = get_global_transform(); - } - - // Clear all bodies. - p_quadrant->bodies.clear(); - - // Create the body and set its parameters. - for (int layer = 0; layer < tile_set->get_physics_layers_count(); layer++) { - RID body = PhysicsServer2D::get_singleton()->body_create(); - PhysicsServer2D::get_singleton()->body_set_mode(body, PhysicsServer2D::BODY_MODE_STATIC); - - PhysicsServer2D::get_singleton()->body_attach_object_instance_id(body, get_instance_id()); - PhysicsServer2D::get_singleton()->body_set_collision_layer(body, tile_set->get_physics_layer_collision_layer(layer)); - PhysicsServer2D::get_singleton()->body_set_collision_mask(body, tile_set->get_physics_layer_collision_mask(layer)); - - Ref<PhysicsMaterial> physics_material = tile_set->get_physics_layer_physics_material(layer); - if (!physics_material.is_valid()) { - PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, 0); - PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, 1); - } else { - PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, physics_material->computed_bounce()); - PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, physics_material->computed_friction()); - } - - if (is_inside_tree()) { - RID space = get_world_2d()->get_space(); - PhysicsServer2D::get_singleton()->body_set_space(body, space); - - Transform2D xform; - xform.set_origin(map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer))); - xform = global_transform * xform; - PhysicsServer2D::get_singleton()->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform); - } - - p_quadrant->bodies.push_back(body); - } -} - void TileMap::_physics_cleanup_quadrant(TileMapQuadrant *p_quadrant) { // Remove a quadrant. - for (int body_index = 0; body_index < p_quadrant->bodies.size(); body_index++) { - PhysicsServer2D::get_singleton()->free(p_quadrant->bodies[body_index]); + for (RID body : p_quadrant->bodies) { + bodies_coords.erase(body); + PhysicsServer2D::get_singleton()->free(body); } p_quadrant->bodies.clear(); } @@ -1252,7 +1303,7 @@ void TileMap::_physics_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { bool show_collision = false; switch (collision_visibility_mode) { case TileMap::VISIBILITY_MODE_DEFAULT: - show_collision = !Engine::get_singleton()->is_editor_hint() && (get_tree() && get_tree()->is_debugging_navigation_hint()); + show_collision = !Engine::get_singleton()->is_editor_hint() && (get_tree() && get_tree()->is_debugging_collisions_hint()); break; case TileMap::VISIBILITY_MODE_FORCE_HIDE: show_collision = false; @@ -1266,39 +1317,28 @@ void TileMap::_physics_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { } RenderingServer *rs = RenderingServer::get_singleton(); - - Vector2 quadrant_pos = map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)); + PhysicsServer2D *ps = PhysicsServer2D::get_singleton(); Color debug_collision_color = get_tree()->get_debug_collisions_color(); - for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) { - TileMapCell c = get_cell(p_quadrant->layer, E_cell->get(), true); - - Transform2D xform; - xform.set_origin(map_to_world(E_cell->get()) - quadrant_pos); - rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform); - - if (tile_set->has_source(c.source_id)) { - TileSetSource *source = *tile_set->get_source(c.source_id); - - if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { - continue; - } + Vector<Color> color; + color.push_back(debug_collision_color); - TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); - if (atlas_source) { - TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); + Vector2 quadrant_pos = map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)); + Transform2D qudrant_xform; + qudrant_xform.set_origin(quadrant_pos); + Transform2D global_transform_inv = (get_global_transform() * qudrant_xform).affine_inverse(); - for (int body_index = 0; body_index < p_quadrant->bodies.size(); body_index++) { - for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(body_index); polygon_index++) { - // Draw the debug polygon. - Vector<Vector2> polygon = tile_data->get_collision_polygon_points(body_index, polygon_index); - if (polygon.size() >= 3) { - Vector<Color> color; - color.push_back(debug_collision_color); - rs->canvas_item_add_polygon(p_quadrant->debug_canvas_item, polygon, color); - } - } - } + for (RID body : p_quadrant->bodies) { + Transform2D xform = Transform2D(ps->body_get_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM)) * global_transform_inv; + rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform); + for (int shape_index = 0; shape_index < ps->body_get_shape_count(body); shape_index++) { + const RID &shape = ps->body_get_shape(body, shape_index); + PhysicsServer2D::ShapeType type = ps->shape_get_type(shape); + if (type == PhysicsServer2D::SHAPE_CONVEX_POLYGON) { + Vector<Vector2> polygon = ps->shape_get_data(shape); + rs->canvas_item_add_polygon(p_quadrant->debug_canvas_item, polygon, color); + } else { + WARN_PRINT("Wrong shape type for a tile, should be SHAPE_CONVEX_POLYGON."); } } rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, Transform2D()); @@ -1313,16 +1353,16 @@ void TileMap::_navigation_notification(int p_what) { if (is_inside_tree()) { for (int layer = 0; layer < (int)layers.size(); layer++) { Transform2D tilemap_xform = get_global_transform(); - for (Map<Vector2i, TileMapQuadrant>::Element *E_quadrant = layers[layer].quadrant_map.front(); E_quadrant; E_quadrant = E_quadrant->next()) { - TileMapQuadrant &q = E_quadrant->get(); - for (Map<Vector2i, Vector<RID>>::Element *E_region = q.navigation_regions.front(); E_region; E_region = E_region->next()) { - for (int layer_index = 0; layer_index < E_region->get().size(); layer_index++) { - RID region = E_region->get()[layer_index]; + for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : layers[layer].quadrant_map) { + TileMapQuadrant &q = E_quadrant.value; + for (const KeyValue<Vector2i, Vector<RID>> &E_region : q.navigation_regions) { + for (int layer_index = 0; layer_index < E_region.value.size(); layer_index++) { + RID region = E_region.value[layer_index]; if (!region.is_valid()) { continue; } Transform2D tile_transform; - tile_transform.set_origin(map_to_world(E_region->key())); + tile_transform.set_origin(map_to_world(E_region.key)); NavigationServer2D::get_singleton()->region_set_transform(region, tilemap_xform * tile_transform); } } @@ -1351,9 +1391,9 @@ void TileMap::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List TileMapQuadrant &q = *q_list_element->self(); // Clear navigation shapes in the quadrant. - for (Map<Vector2i, Vector<RID>>::Element *E = q.navigation_regions.front(); E; E = E->next()) { - for (int i = 0; i < E->get().size(); i++) { - RID region = E->get()[i]; + for (const KeyValue<Vector2i, Vector<RID>> &E : q.navigation_regions) { + for (int i = 0; i < E.value.size(); i++) { + RID region = E.value[i]; if (!region.is_valid()) { continue; } @@ -1404,9 +1444,9 @@ void TileMap::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List void TileMap::_navigation_cleanup_quadrant(TileMapQuadrant *p_quadrant) { // Clear navigation shapes in the quadrant. - for (Map<Vector2i, Vector<RID>>::Element *E = p_quadrant->navigation_regions.front(); E; E = E->next()) { - for (int i = 0; i < E->get().size(); i++) { - RID region = E->get()[i]; + for (const KeyValue<Vector2i, Vector<RID>> &E : p_quadrant->navigation_regions) { + for (int i = 0; i < E.value.size(); i++) { + RID region = E.value[i]; if (!region.is_valid()) { continue; } @@ -1507,8 +1547,8 @@ void TileMap::_scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_ TileMapQuadrant &q = *q_list_element->self(); // Clear the scenes. - for (Map<Vector2i, String>::Element *E = q.scenes.front(); E; E = E->next()) { - Node *node = get_node(E->get()); + for (const KeyValue<Vector2i, String> &E : q.scenes) { + Node *node = get_node(E.value); if (node) { node->queue_delete(); } @@ -1555,8 +1595,8 @@ void TileMap::_scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_ void TileMap::_scenes_cleanup_quadrant(TileMapQuadrant *p_quadrant) { // Clear the scenes. - for (Map<Vector2i, String>::Element *E = p_quadrant->scenes.front(); E; E = E->next()) { - Node *node = get_node(E->get()); + for (const KeyValue<Vector2i, String> &E : p_quadrant->scenes) { + Node *node = get_node(E.value); if (node) { node->queue_delete(); } @@ -1858,16 +1898,21 @@ Map<Vector2i, TileMapQuadrant> *TileMap::get_quadrant_map(int p_layer) { return &layers[p_layer].quadrant_map; } +Vector2i TileMap::get_coords_for_body_rid(RID p_physics_body) { + ERR_FAIL_COND_V_MSG(!bodies_coords.has(p_physics_body), Vector2i(), vformat("No tiles for the given body RID %d.", p_physics_body)); + return bodies_coords[p_physics_body]; +} + void TileMap::fix_invalid_tiles() { ERR_FAIL_COND_MSG(tile_set.is_null(), "Cannot fix invalid tiles if Tileset is not open."); for (unsigned int i = 0; i < layers.size(); i++) { const Map<Vector2i, TileMapCell> &tile_map = layers[i].tile_map; Set<Vector2i> coords; - for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) { - TileSetSource *source = *tile_set->get_source(E->get().source_id); - if (!source || !source->has_tile(E->get().get_atlas_coords()) || !source->has_alternative_tile(E->get().get_atlas_coords(), E->get().alternative_tile)) { - coords.insert(E->key()); + for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) { + TileSetSource *source = *tile_set->get_source(E.value.source_id); + if (!source || !source->has_tile(E.value.get_atlas_coords()) || !source->has_alternative_tile(E.value.get_atlas_coords(), E.value.alternative_tile)) { + coords.insert(E.key); } } for (Set<Vector2i>::Element *E = coords.front(); E; E = E->next()) { @@ -1990,14 +2035,14 @@ Vector<int> TileMap::_get_tile_data(int p_layer) const { // Save in highest format int idx = 0; - for (const Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) { + for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) { uint8_t *ptr = (uint8_t *)&w[idx]; - encode_uint16((int16_t)(E->key().x), &ptr[0]); - encode_uint16((int16_t)(E->key().y), &ptr[2]); - encode_uint16(E->get().source_id, &ptr[4]); - encode_uint16(E->get().coord_x, &ptr[6]); - encode_uint16(E->get().coord_y, &ptr[8]); - encode_uint16(E->get().alternative_tile, &ptr[10]); + encode_uint16((int16_t)(E.key.x), &ptr[0]); + encode_uint16((int16_t)(E.key.y), &ptr[2]); + encode_uint16(E.value.source_id, &ptr[4]); + encode_uint16(E.value.coord_x, &ptr[6]); + encode_uint16(E.value.coord_y, &ptr[8]); + encode_uint16(E.value.alternative_tile, &ptr[10]); idx += 3; } @@ -2034,16 +2079,31 @@ bool TileMap::_set(const StringName &p_name, const Variant &p_value) { return false; } else if (components.size() == 2 && components[0].begins_with("layer_") && components[0].trim_prefix("layer_").is_valid_int()) { int index = components[0].trim_prefix("layer_").to_int(); - if (index < 0 || index >= (int)layers.size()) { + if (index < 0) { return false; } + if (index >= (int)layers.size()) { + _clear_internals(); + while (index >= (int)layers.size()) { + layers.push_back(TileMapLayer()); + } + _recreate_internals(); + + notify_property_list_changed(); + emit_signal(SNAME("changed")); + update_configuration_warnings(); + } + if (components[1] == "name") { set_layer_name(index, p_value); return true; } else if (components[1] == "enabled") { set_layer_enabled(index, p_value); return true; + } else if (components[1] == "modulate") { + set_layer_modulate(index, p_value); + return true; } else if (components[1] == "y_sort_enabled") { set_layer_y_sort_enabled(index, p_value); return true; @@ -2080,6 +2140,9 @@ bool TileMap::_get(const StringName &p_name, Variant &r_ret) const { } else if (components[1] == "enabled") { r_ret = is_layer_enabled(index); return true; + } else if (components[1] == "modulate") { + r_ret = get_layer_modulate(index); + return true; } else if (components[1] == "y_sort_enabled") { r_ret = is_layer_y_sort_enabled(index); return true; @@ -2105,6 +2168,7 @@ void TileMap::_get_property_list(List<PropertyInfo> *p_list) const { for (unsigned int i = 0; i < layers.size(); i++) { p_list->push_back(PropertyInfo(Variant::STRING, vformat("layer_%d/name", i), PROPERTY_HINT_NONE)); p_list->push_back(PropertyInfo(Variant::BOOL, vformat("layer_%d/enabled", i), PROPERTY_HINT_NONE)); + p_list->push_back(PropertyInfo(Variant::COLOR, vformat("layer_%d/modulate", i), PROPERTY_HINT_NONE)); p_list->push_back(PropertyInfo(Variant::BOOL, vformat("layer_%d/y_sort_enabled", i), PROPERTY_HINT_NONE)); p_list->push_back(PropertyInfo(Variant::INT, vformat("layer_%d/y_sort_origin", i), PROPERTY_HINT_NONE)); p_list->push_back(PropertyInfo(Variant::INT, vformat("layer_%d/z_index", i), PROPERTY_HINT_NONE)); @@ -2739,8 +2803,8 @@ TypedArray<Vector2i> TileMap::get_used_cells(int p_layer) const { TypedArray<Vector2i> a; a.resize(layers[p_layer].tile_map.size()); int i = 0; - for (Map<Vector2i, TileMapCell>::Element *E = layers[p_layer].tile_map.front(); E; E = E->next()) { - Vector2i p(E->key().x, E->key().y); + for (const KeyValue<Vector2i, TileMapCell> &E : layers[p_layer].tile_map) { + Vector2i p(E.key.x, E.key.y); a[i++] = p; } @@ -2761,8 +2825,8 @@ Rect2 TileMap::get_used_rect() { // Not const because of cache first = false; } - for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) { - used_rect_cache.expand_to(Vector2i(E->key().x, E->key().y)); + for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) { + used_rect_cache.expand_to(Vector2i(E.key.x, E.key.y)); } } } @@ -2782,8 +2846,8 @@ void TileMap::set_light_mask(int p_light_mask) { // Occlusion: set light mask. CanvasItem::set_light_mask(p_light_mask); for (unsigned int layer = 0; layer < layers.size(); layer++) { - for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) { - for (const RID &ci : E->get().canvas_items) { + for (const KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) { + for (const RID &ci : E.value.canvas_items) { RenderingServer::get_singleton()->canvas_item_set_light_mask(ci, get_light_mask()); } } @@ -2797,8 +2861,8 @@ void TileMap::set_material(const Ref<Material> &p_material) { // Update material for the whole tilemap. for (unsigned int layer = 0; layer < layers.size(); layer++) { - for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) { - TileMapQuadrant &q = E->get(); + for (KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) { + TileMapQuadrant &q = E.value; for (const RID &ci : q.canvas_items) { RS::get_singleton()->canvas_item_set_use_parent_material(ci, get_use_parent_material() || get_material().is_valid()); } @@ -2813,8 +2877,8 @@ void TileMap::set_use_parent_material(bool p_use_parent_material) { // Update use_parent_material for the whole tilemap. for (unsigned int layer = 0; layer < layers.size(); layer++) { - for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) { - TileMapQuadrant &q = E->get(); + for (KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) { + TileMapQuadrant &q = E.value; for (const RID &ci : q.canvas_items) { RS::get_singleton()->canvas_item_set_use_parent_material(ci, get_use_parent_material() || get_material().is_valid()); } @@ -2988,6 +3052,8 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("get_layer_name", "layer"), &TileMap::get_layer_name); ClassDB::bind_method(D_METHOD("set_layer_enabled", "layer", "enabled"), &TileMap::set_layer_enabled); ClassDB::bind_method(D_METHOD("is_layer_enabled", "layer"), &TileMap::is_layer_enabled); + ClassDB::bind_method(D_METHOD("set_layer_modulate", "layer", "enabled"), &TileMap::set_layer_modulate); + ClassDB::bind_method(D_METHOD("get_layer_modulate", "layer"), &TileMap::get_layer_modulate); ClassDB::bind_method(D_METHOD("set_layer_y_sort_enabled", "layer", "y_sort_enabled"), &TileMap::set_layer_y_sort_enabled); ClassDB::bind_method(D_METHOD("is_layer_y_sort_enabled", "layer"), &TileMap::is_layer_y_sort_enabled); ClassDB::bind_method(D_METHOD("set_layer_y_sort_origin", "layer", "y_sort_origin"), &TileMap::set_layer_y_sort_origin); @@ -2995,6 +3061,8 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("set_layer_z_index", "layer", "z_index"), &TileMap::set_layer_z_index); ClassDB::bind_method(D_METHOD("get_layer_z_index", "layer"), &TileMap::get_layer_z_index); + ClassDB::bind_method(D_METHOD("set_collision_animatable", "enabled"), &TileMap::set_collision_animatable); + ClassDB::bind_method(D_METHOD("is_collision_animatable"), &TileMap::is_collision_animatable); ClassDB::bind_method(D_METHOD("set_collision_visibility_mode", "collision_visibility_mode"), &TileMap::set_collision_visibility_mode); ClassDB::bind_method(D_METHOD("get_collision_visibility_mode"), &TileMap::get_collision_visibility_mode); @@ -3006,10 +3074,14 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("get_cell_atlas_coords", "layer", "coords", "use_proxies"), &TileMap::get_cell_atlas_coords); ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "layer", "coords", "use_proxies"), &TileMap::get_cell_alternative_tile); + ClassDB::bind_method(D_METHOD("get_coords_for_body_rid", "body"), &TileMap::get_coords_for_body_rid); + ClassDB::bind_method(D_METHOD("fix_invalid_tiles"), &TileMap::fix_invalid_tiles); - ClassDB::bind_method(D_METHOD("get_surrounding_tiles", "coords"), &TileMap::get_surrounding_tiles); + ClassDB::bind_method(D_METHOD("clear_layer", "layer"), &TileMap::clear_layer); ClassDB::bind_method(D_METHOD("clear"), &TileMap::clear); + ClassDB::bind_method(D_METHOD("get_surrounding_tiles", "coords"), &TileMap::get_surrounding_tiles); + ClassDB::bind_method(D_METHOD("get_used_cells", "layer"), &TileMap::get_used_cells); ClassDB::bind_method(D_METHOD("get_used_rect"), &TileMap::get_used_rect); @@ -3023,8 +3095,11 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_tile_data", "layer"), &TileMap::_set_tile_data); ClassDB::bind_method(D_METHOD("_get_tile_data", "layer"), &TileMap::_get_tile_data); + ClassDB::bind_method(D_METHOD("_tile_set_changed_deferred_update"), &TileMap::_tile_set_changed_deferred_update); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tile_set", PROPERTY_HINT_RESOURCE_TYPE, "TileSet"), "set_tileset", "get_tileset"); ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_quadrant_size", PROPERTY_HINT_RANGE, "1,128,1"), "set_quadrant_size", "get_quadrant_size"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collision_animatable"), "set_collision_animatable", "is_collision_animatable"); ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_visibility_mode", PROPERTY_HINT_ENUM, "Default,Force Show,Force Hide"), "set_collision_visibility_mode", "get_collision_visibility_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_visibility_mode", PROPERTY_HINT_ENUM, "Default,Force Show,Force Hide"), "set_navigation_visibility_mode", "get_navigation_visibility_mode"); @@ -3041,8 +3116,16 @@ void TileMap::_bind_methods() { void TileMap::_tile_set_changed() { emit_signal(SNAME("changed")); - _clear_internals(); - _recreate_internals(); + _tile_set_changed_deferred_update_needed = true; + call_deferred("_tile_set_changed_deferred_update"); +} + +void TileMap::_tile_set_changed_deferred_update() { + if (_tile_set_changed_deferred_update_needed) { + _clear_internals(); + _recreate_internals(); + _tile_set_changed_deferred_update_needed = false; + } } TileMap::TileMap() { diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index ca38baa550..e5809deabb 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -202,6 +202,7 @@ private: // Properties. Ref<TileSet> tile_set; int quadrant_size = 16; + bool collision_animatable = false; VisibilityMode collision_visibility_mode = VISIBILITY_MODE_DEFAULT; VisibilityMode navigation_visibility_mode = VISIBILITY_MODE_DEFAULT; @@ -218,6 +219,7 @@ private: struct TileMapLayer { String name; bool enabled = true; + Color modulate = Color(1, 1, 1, 1); bool y_sort_enabled = false; int y_sort_origin = 0; int z_index = 0; @@ -229,6 +231,9 @@ private: LocalVector<TileMapLayer> layers; int selected_layer = -1; + // Mapping for RID to coords. + Map<RID, Vector2i> bodies_coords; + // Quadrants and internals management. Vector2i _coords_to_quadrant_coords(int p_layer, const Vector2i &p_coords) const; @@ -259,9 +264,10 @@ private: void _rendering_cleanup_quadrant(TileMapQuadrant *p_quadrant); void _rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant); + Transform2D last_valid_transform; + Transform2D new_transform; void _physics_notification(int p_what); void _physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list); - void _physics_create_quadrant(TileMapQuadrant *p_quadrant); void _physics_cleanup_quadrant(TileMapQuadrant *p_quadrant); void _physics_draw_quadrant_debug(TileMapQuadrant *p_quadrant); @@ -279,6 +285,8 @@ private: Vector<int> _get_tile_data(int p_layer) const; void _tile_set_changed(); + bool _tile_set_changed_deferred_update_needed = false; + void _tile_set_changed_deferred_update(); protected: bool _set(const StringName &p_name, const Variant &p_value); @@ -316,6 +324,8 @@ public: String get_layer_name(int p_layer) const; void set_layer_enabled(int p_layer, bool p_visible); bool is_layer_enabled(int p_layer) const; + void set_layer_modulate(int p_layer, Color p_modulate); + Color get_layer_modulate(int p_layer) const; void set_layer_y_sort_enabled(int p_layer, bool p_enabled); bool is_layer_y_sort_enabled(int p_layer) const; void set_layer_y_sort_origin(int p_layer, int p_y_sort_origin); @@ -325,6 +335,9 @@ public: void set_selected_layer(int p_layer_id); // For editor use. int get_selected_layer() const; + void set_collision_animatable(bool p_enabled); + bool is_collision_animatable() const; + void set_collision_visibility_mode(VisibilityMode p_show_collision); VisibilityMode get_collision_visibility_mode(); @@ -364,6 +377,9 @@ public: virtual void set_texture_filter(CanvasItem::TextureFilter p_texture_filter) override; virtual void set_texture_repeat(CanvasItem::TextureRepeat p_texture_repeat) override; + // For finding tiles from collision. + Vector2i get_coords_for_body_rid(RID p_physics_body); + // Fixing a nclearing methods. void fix_invalid_tiles(); |