Skip to content

Commit

Permalink
Merge pull request #49834 from nekomatata/physics-disable-modes
Browse files Browse the repository at this point in the history
Add support for controlling physics nodes' behavior when disabled
  • Loading branch information
akien-mga authored Jun 30, 2021
2 parents 270f9d4 + 5cbdc7a commit 915344f
Show file tree
Hide file tree
Showing 16 changed files with 639 additions and 199 deletions.
14 changes: 14 additions & 0 deletions doc/classes/CollisionObject2D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,9 @@
The physics layers this CollisionObject2D scans. Collision objects can scan one or more of 32 different layers. See also [member collision_layer].
[b]Note:[/b] A contact is detected if object A is in any of the layers that object B scans, or object B is in any layers that object A scans. See [url=https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information.
</member>
<member name="disable_mode" type="int" setter="set_disable_mode" getter="get_disable_mode" enum="CollisionObject2D.DisableMode" default="0">
Defines the behavior in physics when [member Node.process_mode] is set to [constant Node.PROCESS_MODE_DISABLED]. See [enum DisableMode] for more details about the different modes.
</member>
<member name="input_pickable" type="bool" setter="set_pickable" getter="is_pickable" default="true">
If [code]true[/code], this object is pickable. A pickable object can detect the mouse pointer entering/leaving, and if the mouse is inside it, report input events. Requires at least one [code]collision_layer[/code] bit to be set.
</member>
Expand Down Expand Up @@ -294,5 +297,16 @@
</signal>
</signals>
<constants>
<constant name="DISABLE_MODE_REMOVE" value="0" enum="DisableMode">
When [member Node.process_mode] is set to [constant Node.PROCESS_MODE_DISABLED], remove from the physics simulation to stop all physics interactions with this [CollisionObject2D].
Automatically re-added to the physics simulation when the [Node] is processed again.
</constant>
<constant name="DISABLE_MODE_MAKE_STATIC" value="1" enum="DisableMode">
When [member Node.process_mode] is set to [constant Node.PROCESS_MODE_DISABLED], make the body static. Doesn't affect [Area2D]. [PhysicsBody2D] can't be affected by forces or other bodies while static.
Automatically set [PhysicsBody2D] back to its original mode when the [Node] is processed again.
</constant>
<constant name="DISABLE_MODE_KEEP_ACTIVE" value="2" enum="DisableMode">
When [member Node.process_mode] is set to [constant Node.PROCESS_MODE_DISABLED], do not affect the physics simulation.
</constant>
</constants>
</class>
14 changes: 14 additions & 0 deletions doc/classes/CollisionObject3D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,9 @@
The physics layers this CollisionObject3D scans. Collision objects can scan one or more of 32 different layers. See also [member collision_layer].
[b]Note:[/b] A contact is detected if object A is in any of the layers that object B scans, or object B is in any layers that object A scans. See [url=https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information.
</member>
<member name="disable_mode" type="int" setter="set_disable_mode" getter="get_disable_mode" enum="CollisionObject3D.DisableMode" default="0">
Defines the behavior in physics when [member Node.process_mode] is set to [constant Node.PROCESS_MODE_DISABLED]. See [enum DisableMode] for more details about the different modes.
</member>
<member name="input_capture_on_drag" type="bool" setter="set_capture_input_on_drag" getter="get_capture_input_on_drag" default="false">
If [code]true[/code], the [CollisionObject3D] will continue to receive input events as the mouse is dragged across its shapes.
</member>
Expand Down Expand Up @@ -265,5 +268,16 @@
</signal>
</signals>
<constants>
<constant name="DISABLE_MODE_REMOVE" value="0" enum="DisableMode">
When [member Node.process_mode] is set to [constant Node.PROCESS_MODE_DISABLED], remove from the physics simulation to stop all physics interactions with this [CollisionObject3D].
Automatically re-added to the physics simulation when the [Node] is processed again.
</constant>
<constant name="DISABLE_MODE_MAKE_STATIC" value="1" enum="DisableMode">
When [member Node.process_mode] is set to [constant Node.PROCESS_MODE_DISABLED], make the body static. Doesn't affect [Area2D]. [PhysicsBody3D] can't be affected by forces or other bodies while static.
Automatically set [PhysicsBody3D] back to its original mode when the [Node] is processed again.
</constant>
<constant name="DISABLE_MODE_KEEP_ACTIVE" value="2" enum="DisableMode">
When [member Node.process_mode] is set to [constant Node.PROCESS_MODE_DISABLED], do not affect the physics simulation.
</constant>
</constants>
</class>
6 changes: 6 additions & 0 deletions doc/classes/Node.xml
Original file line number Diff line number Diff line change
Expand Up @@ -904,6 +904,12 @@
<constant name="NOTIFICATION_POST_ENTER_TREE" value="27">
Notification received when the node is ready, just before [constant NOTIFICATION_READY] is received. Unlike the latter, it's sent every time the node enters tree, instead of only once.
</constant>
<constant name="NOTIFICATION_DISABLED" value="28">
Notification received when the node is disabled. See [constant PROCESS_MODE_DISABLED].
</constant>
<constant name="NOTIFICATION_ENABLED" value="29">
Notification received when the node is enabled again after being disabled. See [constant PROCESS_MODE_DISABLED].
</constant>
<constant name="NOTIFICATION_EDITOR_PRE_SAVE" value="9001">
Notification received right before the scene with the node is saved in the editor. This notification is only sent in the Godot editor and will not occur in exported projects.
</constant>
Expand Down
10 changes: 10 additions & 0 deletions doc/classes/SoftBody3D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@
</member>
<member name="damping_coefficient" type="float" setter="set_damping_coefficient" getter="get_damping_coefficient" default="0.01">
</member>
<member name="disable_mode" type="int" setter="set_disable_mode" getter="get_disable_mode" enum="SoftBody3D.DisableMode" default="0">
Defines the behavior in physics when [member Node.process_mode] is set to [constant Node.PROCESS_MODE_DISABLED]. See [enum DisableMode] for more details about the different modes.
</member>
<member name="drag_coefficient" type="float" setter="set_drag_coefficient" getter="get_drag_coefficient" default="0.0">
</member>
<member name="linear_stiffness" type="float" setter="set_linear_stiffness" getter="get_linear_stiffness" default="0.5">
Expand All @@ -113,5 +116,12 @@
</member>
</members>
<constants>
<constant name="DISABLE_MODE_REMOVE" value="0" enum="DisableMode">
When [member Node.process_mode] is set to [constant Node.PROCESS_MODE_DISABLED], remove from the physics simulation to stop all physics interactions with this [SoftBody3D].
Automatically re-added to the physics simulation when the [Node] is processed again.
</constant>
<constant name="DISABLE_MODE_KEEP_ACTIVE" value="1" enum="DisableMode">
When [member Node.process_mode] is set to [constant Node.PROCESS_MODE_DISABLED], do not affect the physics simulation.
</constant>
</constants>
</class>
145 changes: 132 additions & 13 deletions scene/2d/collision_object_2d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
#include "collision_object_2d.h"

#include "scene/scene_string_names.h"
#include "servers/physics_server_2d.h"

void CollisionObject2D::_notification(int p_what) {
switch (p_what) {
Expand All @@ -44,16 +43,22 @@ void CollisionObject2D::_notification(int p_what) {
PhysicsServer2D::get_singleton()->body_set_state(rid, PhysicsServer2D::BODY_STATE_TRANSFORM, global_transform);
}

RID space = get_world_2d()->get_space();
if (area) {
PhysicsServer2D::get_singleton()->area_set_space(rid, space);
} else {
PhysicsServer2D::get_singleton()->body_set_space(rid, space);
bool disabled = !is_enabled();

if (disabled && (disable_mode != DISABLE_MODE_REMOVE)) {
_apply_disabled();
}

_update_pickable();
if (!disabled || (disable_mode != DISABLE_MODE_REMOVE)) {
RID space = get_world_2d()->get_space();
if (area) {
PhysicsServer2D::get_singleton()->area_set_space(rid, space);
} else {
PhysicsServer2D::get_singleton()->body_set_space(rid, space);
}
}

//get space
_update_pickable();
} break;

case NOTIFICATION_ENTER_CANVAS: {
Expand All @@ -67,6 +72,7 @@ void CollisionObject2D::_notification(int p_what) {
case NOTIFICATION_VISIBILITY_CHANGED: {
_update_pickable();
} break;

case NOTIFICATION_TRANSFORM_CHANGED: {
if (only_update_transform_changes) {
return;
Expand All @@ -79,15 +85,22 @@ void CollisionObject2D::_notification(int p_what) {
} else {
PhysicsServer2D::get_singleton()->body_set_state(rid, PhysicsServer2D::BODY_STATE_TRANSFORM, global_transform);
}

} break;

case NOTIFICATION_EXIT_TREE: {
if (area) {
PhysicsServer2D::get_singleton()->area_set_space(rid, RID());
} else {
PhysicsServer2D::get_singleton()->body_set_space(rid, RID());
bool disabled = !is_enabled();

if (!disabled || (disable_mode != DISABLE_MODE_REMOVE)) {
if (area) {
PhysicsServer2D::get_singleton()->area_set_space(rid, RID());
} else {
PhysicsServer2D::get_singleton()->body_set_space(rid, RID());
}
}

if (disabled && (disable_mode != DISABLE_MODE_REMOVE)) {
_apply_enabled();
}
} break;

case NOTIFICATION_EXIT_CANVAS: {
Expand All @@ -97,6 +110,14 @@ void CollisionObject2D::_notification(int p_what) {
PhysicsServer2D::get_singleton()->body_attach_canvas_instance_id(rid, ObjectID());
}
} break;

case NOTIFICATION_DISABLED: {
_apply_disabled();
} break;

case NOTIFICATION_ENABLED: {
_apply_enabled();
} break;
}
}

Expand Down Expand Up @@ -158,6 +179,79 @@ bool CollisionObject2D::get_collision_mask_bit(int p_bit) const {
return get_collision_mask() & (1 << p_bit);
}

void CollisionObject2D::set_disable_mode(DisableMode p_mode) {
if (disable_mode == p_mode) {
return;
}

bool disabled = is_inside_tree() && !is_enabled();

if (disabled) {
// Cancel previous disable mode.
_apply_enabled();
}

disable_mode = p_mode;

if (disabled) {
// Apply new disable mode.
_apply_disabled();
}
}

CollisionObject2D::DisableMode CollisionObject2D::get_disable_mode() const {
return disable_mode;
}

void CollisionObject2D::_apply_disabled() {
switch (disable_mode) {
case DISABLE_MODE_REMOVE: {
if (is_inside_tree()) {
if (area) {
PhysicsServer2D::get_singleton()->area_set_space(rid, RID());
} else {
PhysicsServer2D::get_singleton()->body_set_space(rid, RID());
}
}
} break;

case DISABLE_MODE_MAKE_STATIC: {
if (!area && (body_mode != PhysicsServer2D::BODY_MODE_STATIC)) {
PhysicsServer2D::get_singleton()->body_set_mode(rid, PhysicsServer2D::BODY_MODE_STATIC);
}
} break;

case DISABLE_MODE_KEEP_ACTIVE: {
// Nothing to do.
} break;
}
}

void CollisionObject2D::_apply_enabled() {
switch (disable_mode) {
case DISABLE_MODE_REMOVE: {
if (is_inside_tree()) {
RID space = get_world_2d()->get_space();
if (area) {
PhysicsServer2D::get_singleton()->area_set_space(rid, space);
} else {
PhysicsServer2D::get_singleton()->body_set_space(rid, space);
}
}
} break;

case DISABLE_MODE_MAKE_STATIC: {
if (!area && (body_mode != PhysicsServer2D::BODY_MODE_STATIC)) {
PhysicsServer2D::get_singleton()->body_set_mode(rid, body_mode);
}
} break;

case DISABLE_MODE_KEEP_ACTIVE: {
// Nothing to do.
} break;
}
}

uint32_t CollisionObject2D::create_shape_owner(Object *p_owner) {
ShapeData sd;
uint32_t id;
Expand Down Expand Up @@ -412,6 +506,22 @@ bool CollisionObject2D::is_only_update_transform_changes_enabled() const {
return only_update_transform_changes;
}

void CollisionObject2D::set_body_mode(PhysicsServer2D::BodyMode p_mode) {
ERR_FAIL_COND(area);

if (body_mode == p_mode) {
return;
}

body_mode = p_mode;

if (is_inside_tree() && !is_enabled() && (disable_mode == DISABLE_MODE_MAKE_STATIC)) {
return;
}

PhysicsServer2D::get_singleton()->body_set_mode(rid, p_mode);
}

void CollisionObject2D::_update_pickable() {
if (!is_inside_tree()) {
return;
Expand Down Expand Up @@ -445,6 +555,8 @@ void CollisionObject2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &CollisionObject2D::get_collision_layer_bit);
ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &CollisionObject2D::set_collision_mask_bit);
ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &CollisionObject2D::get_collision_mask_bit);
ClassDB::bind_method(D_METHOD("set_disable_mode", "mode"), &CollisionObject2D::set_disable_mode);
ClassDB::bind_method(D_METHOD("get_disable_mode"), &CollisionObject2D::get_disable_mode);
ClassDB::bind_method(D_METHOD("set_pickable", "enabled"), &CollisionObject2D::set_pickable);
ClassDB::bind_method(D_METHOD("is_pickable"), &CollisionObject2D::is_pickable);
ClassDB::bind_method(D_METHOD("create_shape_owner", "owner"), &CollisionObject2D::create_shape_owner);
Expand Down Expand Up @@ -473,12 +585,18 @@ void CollisionObject2D::_bind_methods() {
ADD_SIGNAL(MethodInfo("mouse_entered"));
ADD_SIGNAL(MethodInfo("mouse_exited"));

ADD_PROPERTY(PropertyInfo(Variant::INT, "disable_mode", PROPERTY_HINT_ENUM, "Remove,MakeStatic,KeepActive"), "set_disable_mode", "get_disable_mode");

ADD_GROUP("Collision", "collision_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_layer", "get_collision_layer");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_mask", "get_collision_mask");

ADD_GROUP("Input", "input_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "input_pickable"), "set_pickable", "is_pickable");

BIND_ENUM_CONSTANT(DISABLE_MODE_REMOVE);
BIND_ENUM_CONSTANT(DISABLE_MODE_MAKE_STATIC);
BIND_ENUM_CONSTANT(DISABLE_MODE_KEEP_ACTIVE);
}

CollisionObject2D::CollisionObject2D(RID p_rid, bool p_area) {
Expand All @@ -493,6 +611,7 @@ CollisionObject2D::CollisionObject2D(RID p_rid, bool p_area) {
PhysicsServer2D::get_singleton()->area_attach_object_instance_id(rid, get_instance_id());
} else {
PhysicsServer2D::get_singleton()->body_attach_object_instance_id(rid, get_instance_id());
PhysicsServer2D::get_singleton()->body_set_mode(rid, body_mode);
}
}

Expand Down
23 changes: 23 additions & 0 deletions scene/2d/collision_object_2d.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,30 @@

#include "scene/2d/node_2d.h"
#include "scene/resources/shape_2d.h"
#include "servers/physics_server_2d.h"

class CollisionObject2D : public Node2D {
GDCLASS(CollisionObject2D, Node2D);

public:
enum DisableMode {
DISABLE_MODE_REMOVE,
DISABLE_MODE_MAKE_STATIC,
DISABLE_MODE_KEEP_ACTIVE,
};

private:
uint32_t collision_layer = 1;
uint32_t collision_mask = 1;

bool area = false;
RID rid;
bool pickable = false;

DisableMode disable_mode = DISABLE_MODE_REMOVE;

PhysicsServer2D::BodyMode body_mode = PhysicsServer2D::BODY_MODE_STATIC;

struct ShapeData {
Object *owner = nullptr;
Transform2D xform;
Expand All @@ -64,6 +77,9 @@ class CollisionObject2D : public Node2D {
Map<uint32_t, ShapeData> shapes;
bool only_update_transform_changes = false; //this is used for sync physics in CharacterBody2D

void _apply_disabled();
void _apply_enabled();

protected:
CollisionObject2D(RID p_rid, bool p_area);

Expand All @@ -79,6 +95,8 @@ class CollisionObject2D : public Node2D {
void set_only_update_transform_changes(bool p_enable);
bool is_only_update_transform_changes_enabled() const;

void set_body_mode(PhysicsServer2D::BodyMode p_mode);

public:
void set_collision_layer(uint32_t p_layer);
uint32_t get_collision_layer() const;
Expand All @@ -92,6 +110,9 @@ class CollisionObject2D : public Node2D {
void set_collision_mask_bit(int p_bit, bool p_value);
bool get_collision_mask_bit(int p_bit) const;

void set_disable_mode(DisableMode p_mode);
DisableMode get_disable_mode() const;

uint32_t create_shape_owner(Object *p_owner);
void remove_shape_owner(uint32_t owner);
void get_shape_owners(List<uint32_t> *r_owners);
Expand Down Expand Up @@ -131,4 +152,6 @@ class CollisionObject2D : public Node2D {
~CollisionObject2D();
};

VARIANT_ENUM_CAST(CollisionObject2D::DisableMode);

#endif // COLLISION_OBJECT_2D_H
Loading

0 comments on commit 915344f

Please sign in to comment.