summaryrefslogtreecommitdiff
path: root/scene/2d
diff options
context:
space:
mode:
Diffstat (limited to 'scene/2d')
-rw-r--r--scene/2d/physical_bone_2d.cpp20
-rw-r--r--scene/2d/physics_body_2d.cpp218
-rw-r--r--scene/2d/physics_body_2d.h61
-rw-r--r--scene/2d/tile_map.cpp266
-rw-r--r--scene/2d/tile_map.h13
5 files changed, 365 insertions, 213 deletions
diff --git a/scene/2d/physical_bone_2d.cpp b/scene/2d/physical_bone_2d.cpp
index c4b2608812..48817679bc 100644
--- a/scene/2d/physical_bone_2d.cpp
+++ b/scene/2d/physical_bone_2d.cpp
@@ -150,27 +150,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/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index 3281afd819..277541cf46 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -288,6 +288,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: {
@@ -313,7 +319,6 @@ void AnimatableBody2D::_bind_methods() {
AnimatableBody2D::AnimatableBody2D() :
StaticBody2D(PhysicsServer2D::BODY_MODE_KINEMATIC) {
- _update_kinematic_motion();
}
void RigidDynamicBody2D::_body_enter_tree(ObjectID p_id) {
@@ -436,7 +441,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());
}
@@ -530,29 +535,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) {
@@ -850,17 +886,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);
@@ -924,11 +957,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");
@@ -942,6 +983,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");
@@ -958,10 +1002,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);
@@ -1010,6 +1052,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;
@@ -1022,7 +1066,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);
}
@@ -1032,6 +1075,7 @@ bool CharacterBody2D::move_and_slide() {
}
motion_results.clear();
+ last_motion = Vector2();
bool was_on_floor = on_floor;
on_floor = false;
@@ -1054,16 +1098,24 @@ 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;
@@ -1080,7 +1132,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
// Constant speed can be applied only the first time sliding is enabled.
bool can_apply_constant_speed = sliding_enabled;
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) {
@@ -1089,18 +1141,20 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
Vector2 prev_position = get_global_transform().elements[2];
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 (on_floor && floor_stop_on_slope && (motion_velocity.normalized() + up_direction).length() < 0.01) {
Transform2D gt = get_global_transform();
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;
}
@@ -1126,7 +1180,8 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
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;
}
@@ -1147,7 +1202,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
// 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)) {
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();
@@ -1155,10 +1210,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);
}
}
}
@@ -1166,7 +1221,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);
}
}
@@ -1200,12 +1255,12 @@ 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();
floor_normal = Vector2();
@@ -1216,12 +1271,18 @@ void CharacterBody2D::_move_and_slide_free(double p_delta) {
PhysicsServer2D::MotionResult result;
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();
@@ -1230,16 +1291,16 @@ 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;
}
}
@@ -1307,6 +1368,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);
@@ -1320,12 +1382,12 @@ void CharacterBody2D::_set_platform_data(const PhysicsServer2D::MotionResult &p_
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 {
@@ -1352,16 +1414,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;
}
@@ -1461,6 +1539,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;
}
@@ -1521,8 +1607,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);
@@ -1552,6 +1638,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);
@@ -1560,6 +1648,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);
@@ -1567,10 +1659,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_");
@@ -1580,12 +1673,17 @@ void CharacterBody2D::_bind_methods() {
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,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 {
@@ -1680,10 +1778,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);
@@ -1697,7 +1791,6 @@ 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");
@@ -1710,5 +1803,4 @@ void KinematicCollision2D::_bind_methods() {
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 ac1a1357f1..70cc5cd9bf 100644
--- a/scene/2d/physics_body_2d.h
+++ b/scene/2d/physics_body_2d.h
@@ -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,23 +344,29 @@ 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 = 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;
bool on_floor = false;
bool on_ceiling = false;
@@ -387,6 +411,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 +433,7 @@ protected:
};
VARIANT_ENUM_CAST(CharacterBody2D::MotionMode);
+VARIANT_ENUM_CAST(CharacterBody2D::MovingPlatformApplyVelocityOnLeave);
class KinematicCollision2D : public RefCounted {
GDCLASS(KinematicCollision2D, RefCounted);
@@ -431,7 +459,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 929233e4e0..78d018ac53 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -452,6 +452,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 +521,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);
@@ -1092,24 +1104,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 (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 (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) {
TileMapQuadrant &q = E->get();
- 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 +1175,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 +1207,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 +1265,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 +1285,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 +1299,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);
+ Vector<Color> color;
+ color.push_back(debug_collision_color);
- 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;
- }
-
- 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());
@@ -1858,6 +1880,11 @@ 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.");
@@ -3007,6 +3034,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);
@@ -3018,10 +3047,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);
@@ -3037,6 +3070,7 @@ void TileMap::_bind_methods() {
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");
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index ca38baa550..2faede2445 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;
@@ -229,6 +230,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 +263,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);
@@ -325,6 +330,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 +372,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();