summaryrefslogtreecommitdiff
path: root/scene/2d
diff options
context:
space:
mode:
Diffstat (limited to 'scene/2d')
-rw-r--r--scene/2d/collision_object_2d.cpp4
-rw-r--r--scene/2d/collision_object_2d.h3
-rw-r--r--scene/2d/collision_polygon_2d.cpp2
-rw-r--r--scene/2d/collision_shape_2d.cpp2
-rw-r--r--scene/2d/node_2d.cpp15
-rw-r--r--scene/2d/node_2d.h4
-rw-r--r--scene/2d/physical_bone_2d.cpp303
-rw-r--r--scene/2d/physical_bone_2d.h (renamed from scene/2d/y_sort.h)63
-rw-r--r--scene/2d/physics_body_2d.cpp594
-rw-r--r--scene/2d/physics_body_2d.h121
-rw-r--r--scene/2d/position_2d.cpp39
-rw-r--r--scene/2d/skeleton_2d.cpp531
-rw-r--r--scene/2d/skeleton_2d.h53
-rw-r--r--scene/2d/tile_map.cpp6
-rw-r--r--scene/2d/tile_map.h1
-rw-r--r--scene/2d/visibility_notifier_2d.cpp2
-rw-r--r--scene/2d/y_sort.cpp52
17 files changed, 1420 insertions, 375 deletions
diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp
index de648d404c..a633923be7 100644
--- a/scene/2d/collision_object_2d.cpp
+++ b/scene/2d/collision_object_2d.cpp
@@ -408,6 +408,10 @@ void CollisionObject2D::set_only_update_transform_changes(bool p_enable) {
only_update_transform_changes = p_enable;
}
+bool CollisionObject2D::is_only_update_transform_changes_enabled() const {
+ return only_update_transform_changes;
+}
+
void CollisionObject2D::_update_pickable() {
if (!is_inside_tree()) {
return;
diff --git a/scene/2d/collision_object_2d.h b/scene/2d/collision_object_2d.h
index bb1a9dfcf5..e10f3097d9 100644
--- a/scene/2d/collision_object_2d.h
+++ b/scene/2d/collision_object_2d.h
@@ -62,7 +62,7 @@ class CollisionObject2D : public Node2D {
int total_subshapes = 0;
Map<uint32_t, ShapeData> shapes;
- bool only_update_transform_changes = false; //this is used for sync physics in KinematicBody
+ bool only_update_transform_changes = false; //this is used for sync physics in CharacterBody2D
protected:
CollisionObject2D(RID p_rid, bool p_area);
@@ -77,6 +77,7 @@ protected:
void _mouse_exit();
void set_only_update_transform_changes(bool p_enable);
+ bool is_only_update_transform_changes_enabled() const;
public:
void set_collision_layer(uint32_t p_layer);
diff --git a/scene/2d/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp
index a69ef73a54..2a2fde80e2 100644
--- a/scene/2d/collision_polygon_2d.cpp
+++ b/scene/2d/collision_polygon_2d.cpp
@@ -244,7 +244,7 @@ TypedArray<String> CollisionPolygon2D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!Object::cast_to<CollisionObject2D>(get_parent())) {
- warnings.push_back(TTR("CollisionPolygon2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape."));
+ warnings.push_back(TTR("CollisionPolygon2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, CharacterBody2D, etc. to give them a shape."));
}
int polygon_count = polygon.size();
diff --git a/scene/2d/collision_shape_2d.cpp b/scene/2d/collision_shape_2d.cpp
index d9009ef85c..60780f1cc3 100644
--- a/scene/2d/collision_shape_2d.cpp
+++ b/scene/2d/collision_shape_2d.cpp
@@ -181,7 +181,7 @@ TypedArray<String> CollisionShape2D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!Object::cast_to<CollisionObject2D>(get_parent())) {
- warnings.push_back(TTR("CollisionShape2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape."));
+ warnings.push_back(TTR("CollisionShape2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, CharacterBody2D, etc. to give them a shape."));
}
if (!shape.is_valid()) {
warnings.push_back(TTR("A shape must be provided for CollisionShape2D to function. Please create a shape resource for it!"));
diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp
index 8afc43ddc9..049d121213 100644
--- a/scene/2d/node_2d.cpp
+++ b/scene/2d/node_2d.cpp
@@ -391,6 +391,15 @@ Point2 Node2D::to_global(Point2 p_local) const {
return get_global_transform().xform(p_local);
}
+void Node2D::set_y_sort_enabled(bool p_enabled) {
+ y_sort_enabled = p_enabled;
+ RS::get_singleton()->canvas_item_set_sort_children_by_y(get_canvas_item(), y_sort_enabled);
+}
+
+bool Node2D::is_y_sort_enabled() const {
+ return y_sort_enabled;
+}
+
void Node2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_position", "position"), &Node2D::set_position);
ClassDB::bind_method(D_METHOD("set_rotation", "radians"), &Node2D::set_rotation);
@@ -437,6 +446,9 @@ void Node2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_z_as_relative", "enable"), &Node2D::set_z_as_relative);
ClassDB::bind_method(D_METHOD("is_z_relative"), &Node2D::is_z_relative);
+ ClassDB::bind_method(D_METHOD("set_y_sort_enabled", "enabled"), &Node2D::set_y_sort_enabled);
+ ClassDB::bind_method(D_METHOD("is_y_sort_enabled"), &Node2D::is_y_sort_enabled);
+
ClassDB::bind_method(D_METHOD("get_relative_transform_to_parent", "parent"), &Node2D::get_relative_transform_to_parent);
ADD_GROUP("Transform", "");
@@ -454,7 +466,8 @@ void Node2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "global_scale", PROPERTY_HINT_NONE, "", 0), "set_global_scale", "get_global_scale");
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "global_transform", PROPERTY_HINT_NONE, "", 0), "set_global_transform", "get_global_transform");
- ADD_GROUP("Z Index", "");
+ ADD_GROUP("Ordering", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "z_index", PROPERTY_HINT_RANGE, itos(RS::CANVAS_ITEM_Z_MIN) + "," + itos(RS::CANVAS_ITEM_Z_MAX) + ",1"), "set_z_index", "get_z_index");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "z_as_relative"), "set_z_as_relative", "is_z_relative");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "y_sort_enabled"), "set_y_sort_enabled", "is_y_sort_enabled");
}
diff --git a/scene/2d/node_2d.h b/scene/2d/node_2d.h
index 358b7e6520..339efd9179 100644
--- a/scene/2d/node_2d.h
+++ b/scene/2d/node_2d.h
@@ -42,6 +42,7 @@ class Node2D : public CanvasItem {
real_t skew = 0.0;
int z_index = 0;
bool z_relative = true;
+ bool y_sort_enabled = false;
Transform2D _mat;
@@ -117,6 +118,9 @@ public:
void set_z_as_relative(bool p_enabled);
bool is_z_relative() const;
+ virtual void set_y_sort_enabled(bool p_enabled);
+ virtual bool is_y_sort_enabled() const;
+
Transform2D get_relative_transform_to_parent(const Node *p_parent) const;
Transform2D get_transform() const override;
diff --git a/scene/2d/physical_bone_2d.cpp b/scene/2d/physical_bone_2d.cpp
new file mode 100644
index 0000000000..0c1be16174
--- /dev/null
+++ b/scene/2d/physical_bone_2d.cpp
@@ -0,0 +1,303 @@
+/*************************************************************************/
+/* physical_bone_2d.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "physical_bone_2d.h"
+
+void PhysicalBone2D::_notification(int p_what) {
+ if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
+ // Position the RigidBody in the correct position
+ if (follow_bone_when_simulating) {
+ _position_at_bone2d();
+ }
+
+ // Keep the child joint in the correct position.
+ if (child_joint && auto_configure_joint) {
+ child_joint->set_global_position(get_global_position());
+ }
+ } else if (p_what == NOTIFICATION_READY) {
+ _find_skeleton_parent();
+ _find_joint_child();
+
+ // Configure joint
+ if (child_joint && auto_configure_joint) {
+ _auto_configure_joint();
+ }
+
+ // Simulate physics if set
+ if (simulate_physics) {
+ _start_physics_simulation();
+ } else {
+ _stop_physics_simulation();
+ }
+
+ set_physics_process_internal(true);
+ }
+}
+
+void PhysicalBone2D::_position_at_bone2d() {
+ // Reset to Bone2D position
+ if (parent_skeleton) {
+ Bone2D *bone_to_use = parent_skeleton->get_bone(bone2d_index);
+ ERR_FAIL_COND_MSG(bone_to_use == nullptr, "It's not possible to position the bone with ID: " + itos(bone2d_index));
+ set_global_transform(bone_to_use->get_global_transform());
+ }
+}
+
+void PhysicalBone2D::_find_skeleton_parent() {
+ Node *current_parent = get_parent();
+
+ while (current_parent != nullptr) {
+ Skeleton2D *potential_skeleton = Object::cast_to<Skeleton2D>(current_parent);
+ if (potential_skeleton) {
+ parent_skeleton = potential_skeleton;
+ break;
+ } else {
+ PhysicalBone2D *potential_parent_bone = Object::cast_to<PhysicalBone2D>(current_parent);
+ if (potential_parent_bone) {
+ current_parent = potential_parent_bone->get_parent();
+ } else {
+ current_parent = nullptr;
+ }
+ }
+ }
+}
+
+void PhysicalBone2D::_find_joint_child() {
+ for (int i = 0; i < get_child_count(); i++) {
+ Node *child_node = get_child(i);
+ Joint2D *potential_joint = Object::cast_to<Joint2D>(child_node);
+ if (potential_joint) {
+ child_joint = potential_joint;
+ break;
+ }
+ }
+}
+
+TypedArray<String> PhysicalBone2D::get_configuration_warnings() const {
+ TypedArray<String> warnings = Node::get_configuration_warnings();
+
+ if (!parent_skeleton) {
+ warnings.push_back(TTR("A PhysicalBone2D only works with a Skeleton2D or another PhysicalBone2D as a parent node!"));
+ }
+ if (parent_skeleton && bone2d_index <= -1) {
+ warnings.push_back(TTR("A PhysicalBone2D needs to be assigned to a Bone2D node in order to function! Please set a Bone2D node in the inspector."));
+ }
+ if (!child_joint) {
+ PhysicalBone2D *parent_bone = Object::cast_to<PhysicalBone2D>(get_parent());
+ if (parent_bone) {
+ warnings.push_back(TTR("A PhysicalBone2D node should have a Joint2D-based child node to keep bones connected! Please add a Joint2D-based node as a child to this node!"));
+ }
+ }
+
+ return warnings;
+}
+
+void PhysicalBone2D::_auto_configure_joint() {
+ if (!auto_configure_joint) {
+ return;
+ }
+
+ if (child_joint) {
+ // Node A = parent | Node B = this node
+ Node *parent_node = get_parent();
+ PhysicalBone2D *potential_parent_bone = Object::cast_to<PhysicalBone2D>(parent_node);
+
+ if (potential_parent_bone) {
+ child_joint->set_node_a(child_joint->get_path_to(potential_parent_bone));
+ child_joint->set_node_b(child_joint->get_path_to(this));
+ } else {
+ WARN_PRINT("Cannot setup joint without a parent PhysicalBone2D node.");
+ }
+
+ // Place the child joint at this node's position.
+ child_joint->set_global_position(get_global_position());
+ }
+}
+
+void PhysicalBone2D::_start_physics_simulation() {
+ if (_internal_simulate_physics) {
+ return;
+ }
+
+ // Reset to Bone2D position
+ _position_at_bone2d();
+
+ // 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
+ RigidBody2D::Mode rigid_mode = get_mode();
+ if (rigid_mode == RigidBody2D::MODE_STATIC) {
+ PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BodyMode::BODY_MODE_STATIC);
+ } else if (rigid_mode == RigidBody2D::MODE_DYNAMIC) {
+ PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BodyMode::BODY_MODE_DYNAMIC);
+ } else if (rigid_mode == RigidBody2D::MODE_KINEMATIC) {
+ PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BodyMode::BODY_MODE_KINEMATIC);
+ } else if (rigid_mode == RigidBody2D::MODE_DYNAMIC_LOCKED) {
+ PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BodyMode::BODY_MODE_DYNAMIC_LOCKED);
+ } else {
+ // Default to Rigid
+ PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BodyMode::BODY_MODE_DYNAMIC);
+ }
+
+ _internal_simulate_physics = true;
+ set_physics_process_internal(true);
+}
+
+void PhysicalBone2D::_stop_physics_simulation() {
+ if (_internal_simulate_physics) {
+ _internal_simulate_physics = false;
+
+ // Reset to Bone2D position
+ _position_at_bone2d();
+
+ set_physics_process_internal(false);
+ PhysicsServer2D::get_singleton()->body_set_collision_layer(get_rid(), 0);
+ PhysicsServer2D::get_singleton()->body_set_collision_mask(get_rid(), 0);
+ PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BodyMode::BODY_MODE_STATIC);
+ }
+}
+
+Joint2D *PhysicalBone2D::get_joint() const {
+ return child_joint;
+}
+
+bool PhysicalBone2D::get_auto_configure_joint() const {
+ return auto_configure_joint;
+}
+
+void PhysicalBone2D::set_auto_configure_joint(bool p_auto_configure) {
+ auto_configure_joint = p_auto_configure;
+ _auto_configure_joint();
+}
+
+void PhysicalBone2D::set_simulate_physics(bool p_simulate) {
+ if (p_simulate == simulate_physics) {
+ return;
+ }
+ simulate_physics = p_simulate;
+
+ if (simulate_physics) {
+ _start_physics_simulation();
+ } else {
+ _stop_physics_simulation();
+ }
+}
+
+bool PhysicalBone2D::get_simulate_physics() const {
+ return simulate_physics;
+}
+
+bool PhysicalBone2D::is_simulating_physics() const {
+ return _internal_simulate_physics;
+}
+
+void PhysicalBone2D::set_bone2d_nodepath(const NodePath &p_nodepath) {
+ bone2d_nodepath = p_nodepath;
+ notify_property_list_changed();
+}
+
+NodePath PhysicalBone2D::get_bone2d_nodepath() const {
+ return bone2d_nodepath;
+}
+
+void PhysicalBone2D::set_bone2d_index(int p_bone_idx) {
+ ERR_FAIL_COND_MSG(p_bone_idx < 0, "Bone index is out of range: The index is too low!");
+
+ if (!is_inside_tree()) {
+ bone2d_index = p_bone_idx;
+ return;
+ }
+
+ if (parent_skeleton) {
+ ERR_FAIL_INDEX_MSG(p_bone_idx, parent_skeleton->get_bone_count(), "Passed-in Bone index is out of range!");
+ bone2d_index = p_bone_idx;
+
+ bone2d_nodepath = get_path_to(parent_skeleton->get_bone(bone2d_index));
+ } else {
+ WARN_PRINT("Cannot verify bone index...");
+ bone2d_index = p_bone_idx;
+ }
+
+ notify_property_list_changed();
+}
+
+int PhysicalBone2D::get_bone2d_index() const {
+ return bone2d_index;
+}
+
+void PhysicalBone2D::set_follow_bone_when_simulating(bool p_follow_bone) {
+ follow_bone_when_simulating = p_follow_bone;
+
+ if (_internal_simulate_physics) {
+ _stop_physics_simulation();
+ _start_physics_simulation();
+ }
+}
+
+bool PhysicalBone2D::get_follow_bone_when_simulating() const {
+ return follow_bone_when_simulating;
+}
+
+void PhysicalBone2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_joint"), &PhysicalBone2D::get_joint);
+ ClassDB::bind_method(D_METHOD("get_auto_configure_joint"), &PhysicalBone2D::get_auto_configure_joint);
+ ClassDB::bind_method(D_METHOD("set_auto_configure_joint", "auto_configure_joint"), &PhysicalBone2D::set_auto_configure_joint);
+
+ ClassDB::bind_method(D_METHOD("set_simulate_physics", "simulate_physics"), &PhysicalBone2D::set_simulate_physics);
+ ClassDB::bind_method(D_METHOD("get_simulate_physics"), &PhysicalBone2D::get_simulate_physics);
+ ClassDB::bind_method(D_METHOD("is_simulating_physics"), &PhysicalBone2D::is_simulating_physics);
+
+ ClassDB::bind_method(D_METHOD("set_bone2d_nodepath", "nodepath"), &PhysicalBone2D::set_bone2d_nodepath);
+ ClassDB::bind_method(D_METHOD("get_bone2d_nodepath"), &PhysicalBone2D::get_bone2d_nodepath);
+ ClassDB::bind_method(D_METHOD("set_bone2d_index", "bone_index"), &PhysicalBone2D::set_bone2d_index);
+ ClassDB::bind_method(D_METHOD("get_bone2d_index"), &PhysicalBone2D::get_bone2d_index);
+ ClassDB::bind_method(D_METHOD("set_follow_bone_when_simulating", "follow_bone"), &PhysicalBone2D::set_follow_bone_when_simulating);
+ ClassDB::bind_method(D_METHOD("get_follow_bone_when_simulating"), &PhysicalBone2D::get_follow_bone_when_simulating);
+
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "bone2d_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Bone2D"), "set_bone2d_nodepath", "get_bone2d_nodepath");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "bone2d_index", PROPERTY_HINT_RANGE, "-1, 1000, 1"), "set_bone2d_index", "get_bone2d_index");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_configure_joint"), "set_auto_configure_joint", "get_auto_configure_joint");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "simulate_physics"), "set_simulate_physics", "get_simulate_physics");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "follow_bone_when_simulating"), "set_follow_bone_when_simulating", "get_follow_bone_when_simulating");
+}
+
+PhysicalBone2D::PhysicalBone2D() {
+ // Stop the RigidBody from executing its force integration.
+ PhysicsServer2D::get_singleton()->body_set_collision_layer(get_rid(), 0);
+ PhysicsServer2D::get_singleton()->body_set_collision_mask(get_rid(), 0);
+ PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BodyMode::BODY_MODE_STATIC);
+
+ child_joint = nullptr;
+}
+
+PhysicalBone2D::~PhysicalBone2D() {
+}
diff --git a/scene/2d/y_sort.h b/scene/2d/physical_bone_2d.h
index 7d36ee3391..d650a0426f 100644
--- a/scene/2d/y_sort.h
+++ b/scene/2d/physical_bone_2d.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* y_sort.h */
+/* physical_bone_2d.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,20 +28,61 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef Y_SORT_H
-#define Y_SORT_H
+#ifndef PHYSICAL_BONE_2D_H
+#define PHYSICAL_BONE_2D_H
-#include "scene/2d/node_2d.h"
+#include "scene/2d/joints_2d.h"
+#include "scene/2d/physics_body_2d.h"
-class YSort : public Node2D {
- GDCLASS(YSort, Node2D);
- bool sort_enabled = true;
+#include "scene/2d/skeleton_2d.h"
+
+class PhysicalBone2D : public RigidBody2D {
+ GDCLASS(PhysicalBone2D, RigidBody2D);
+
+protected:
+ void _notification(int p_what);
static void _bind_methods();
+private:
+ Skeleton2D *parent_skeleton;
+ int bone2d_index = -1;
+ NodePath bone2d_nodepath;
+ bool follow_bone_when_simulating = false;
+
+ Joint2D *child_joint;
+ bool auto_configure_joint = true;
+
+ bool simulate_physics = false;
+ bool _internal_simulate_physics = false;
+
+ void _find_skeleton_parent();
+ void _find_joint_child();
+ void _auto_configure_joint();
+
+ void _start_physics_simulation();
+ void _stop_physics_simulation();
+ void _position_at_bone2d();
+
public:
- void set_sort_enabled(bool p_enabled);
- bool is_sort_enabled() const;
- YSort();
+ Joint2D *get_joint() const;
+ bool get_auto_configure_joint() const;
+ void set_auto_configure_joint(bool p_auto_configure);
+
+ void set_simulate_physics(bool p_simulate);
+ bool get_simulate_physics() const;
+ bool is_simulating_physics() const;
+
+ void set_bone2d_nodepath(const NodePath &p_nodepath);
+ NodePath get_bone2d_nodepath() const;
+ void set_bone2d_index(int p_bone_idx);
+ int get_bone2d_index() const;
+ void set_follow_bone_when_simulating(bool p_follow);
+ bool get_follow_bone_when_simulating() const;
+
+ TypedArray<String> get_configuration_warnings() const override;
+
+ PhysicalBone2D();
+ ~PhysicalBone2D();
};
-#endif // Y_SORT_H
+#endif // PHYSICAL_BONE_2D_H
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index 4f52f62e99..8f6e1c4695 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -38,10 +38,10 @@
#include "core/templates/rid.h"
#include "scene/scene_string_names.h"
-void PhysicsBody2D::_notification(int p_what) {
-}
-
void PhysicsBody2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "infinite_inertia", "exclude_raycast_shapes", "test_only", "safe_margin"), &PhysicsBody2D::_move, DEFVAL(true), DEFVAL(true), DEFVAL(false), DEFVAL(0.08));
+ ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec", "infinite_inertia", "exclude_raycast_shapes", "collision", "safe_margin"), &PhysicsBody2D::test_move, DEFVAL(true), DEFVAL(true), 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);
ClassDB::bind_method(D_METHOD("remove_collision_exception_with", "body"), &PhysicsBody2D::remove_collision_exception_with);
@@ -53,6 +53,56 @@ PhysicsBody2D::PhysicsBody2D(PhysicsServer2D::BodyMode p_mode) :
set_pickable(false);
}
+PhysicsBody2D::~PhysicsBody2D() {
+ if (motion_cache.is_valid()) {
+ motion_cache->owner = nullptr;
+ }
+}
+
+Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_motion, bool p_infinite_inertia, bool p_exclude_raycast_shapes, bool p_test_only, real_t p_margin) {
+ PhysicsServer2D::MotionResult result;
+
+ if (move_and_collide(p_motion, p_infinite_inertia, result, p_margin, p_exclude_raycast_shapes, p_test_only)) {
+ if (motion_cache.is_null()) {
+ motion_cache.instance();
+ motion_cache->owner = this;
+ }
+
+ motion_cache->result = result;
+
+ return motion_cache;
+ }
+
+ return Ref<KinematicCollision2D>();
+}
+
+bool PhysicsBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, PhysicsServer2D::MotionResult &r_result, real_t p_margin, bool p_exclude_raycast_shapes, bool p_test_only) {
+ 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_infinite_inertia, p_margin, &r_result, p_exclude_raycast_shapes);
+
+ if (!p_test_only) {
+ gt.elements[2] += r_result.motion;
+ set_global_transform(gt);
+ }
+
+ return colliding;
+}
+
+bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia, bool p_exclude_raycast_shapes, const Ref<KinematicCollision2D> &r_collision, real_t p_margin) {
+ ERR_FAIL_COND_V(!is_inside_tree(), false);
+
+ PhysicsServer2D::MotionResult *r = nullptr;
+ if (r_collision.is_valid()) {
+ // Needs const_cast because method bindings don't support non-const Ref.
+ r = const_cast<PhysicsServer2D::MotionResult *>(&r_collision->result);
+ }
+
+ return PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), p_from, p_motion, p_infinite_inertia, p_margin, r, p_exclude_raycast_shapes);
+}
+
TypedArray<PhysicsBody2D> PhysicsBody2D::get_collision_exceptions() {
List<RID> exceptions;
PhysicsServer2D::get_singleton()->body_get_collision_exceptions(get_rid(), &exceptions);
@@ -83,12 +133,22 @@ void PhysicsBody2D::remove_collision_exception_with(Node *p_node) {
void StaticBody2D::set_constant_linear_velocity(const Vector2 &p_vel) {
constant_linear_velocity = p_vel;
- PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY, constant_linear_velocity);
+
+ if (kinematic_motion) {
+ _update_kinematic_motion();
+ } else {
+ PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY, constant_linear_velocity);
+ }
}
void StaticBody2D::set_constant_angular_velocity(real_t p_vel) {
constant_angular_velocity = p_vel;
- PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_ANGULAR_VELOCITY, constant_angular_velocity);
+
+ if (kinematic_motion) {
+ _update_kinematic_motion();
+ } else {
+ PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_ANGULAR_VELOCITY, constant_angular_velocity);
+ }
}
Vector2 StaticBody2D::get_constant_linear_velocity() const {
@@ -118,27 +178,74 @@ Ref<PhysicsMaterial> StaticBody2D::get_physics_material_override() const {
return physics_material_override;
}
+void StaticBody2D::set_kinematic_motion_enabled(bool p_enabled) {
+ if (p_enabled == kinematic_motion) {
+ return;
+ }
+
+ kinematic_motion = p_enabled;
+
+ if (kinematic_motion) {
+ PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BODY_MODE_KINEMATIC);
+ } else {
+ PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BODY_MODE_STATIC);
+ }
+
+ _update_kinematic_motion();
+}
+
+bool StaticBody2D::is_kinematic_motion_enabled() const {
+ return kinematic_motion;
+}
+
+void StaticBody2D::_notification(int p_what) {
+ if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ return;
+ }
+#endif
+
+ ERR_FAIL_COND(!kinematic_motion);
+
+ real_t delta_time = get_physics_process_delta_time();
+
+ Transform2D new_transform = get_global_transform();
+
+ new_transform.translate(constant_linear_velocity * delta_time);
+ new_transform.set_rotation(new_transform.get_rotation() + constant_angular_velocity * delta_time);
+
+ PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_TRANSFORM, new_transform);
+
+ // Propagate transform change to node.
+ set_block_transform_notify(true);
+ set_global_transform(new_transform);
+ set_block_transform_notify(false);
+ }
+}
+
void StaticBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_constant_linear_velocity", "vel"), &StaticBody2D::set_constant_linear_velocity);
ClassDB::bind_method(D_METHOD("set_constant_angular_velocity", "vel"), &StaticBody2D::set_constant_angular_velocity);
ClassDB::bind_method(D_METHOD("get_constant_linear_velocity"), &StaticBody2D::get_constant_linear_velocity);
ClassDB::bind_method(D_METHOD("get_constant_angular_velocity"), &StaticBody2D::get_constant_angular_velocity);
+ ClassDB::bind_method(D_METHOD("set_kinematic_motion_enabled", "enabled"), &StaticBody2D::set_kinematic_motion_enabled);
+ ClassDB::bind_method(D_METHOD("is_kinematic_motion_enabled"), &StaticBody2D::is_kinematic_motion_enabled);
+
ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &StaticBody2D::set_physics_material_override);
ClassDB::bind_method(D_METHOD("get_physics_material_override"), &StaticBody2D::get_physics_material_override);
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "constant_linear_velocity"), "set_constant_linear_velocity", "get_constant_linear_velocity");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "constant_angular_velocity"), "set_constant_angular_velocity", "get_constant_angular_velocity");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "kinematic_motion"), "set_kinematic_motion_enabled", "is_kinematic_motion_enabled");
}
StaticBody2D::StaticBody2D() :
PhysicsBody2D(PhysicsServer2D::BODY_MODE_STATIC) {
}
-StaticBody2D::~StaticBody2D() {
-}
-
void StaticBody2D::_reload_physics_characteristics() {
if (physics_material_override.is_null()) {
PhysicsServer2D::get_singleton()->body_set_param(get_rid(), PhysicsServer2D::BODY_PARAM_BOUNCE, 0);
@@ -149,6 +256,23 @@ void StaticBody2D::_reload_physics_characteristics() {
}
}
+void StaticBody2D::_update_kinematic_motion() {
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ return;
+ }
+#endif
+
+ if (kinematic_motion) {
+ if (!Math::is_zero_approx(constant_angular_velocity) || !constant_linear_velocity.is_equal_approx(Vector2())) {
+ set_physics_process_internal(true);
+ return;
+ }
+ }
+
+ set_physics_process_internal(false);
+}
+
void RigidBody2D::_body_enter_tree(ObjectID p_id) {
Object *obj = ObjectDB::get_instance(p_id);
Node *node = Object::cast_to<Node>(obj);
@@ -262,14 +386,6 @@ struct _RigidBody2DInOut {
int local_shape = 0;
};
-bool RigidBody2D::_test_motion(const Vector2 &p_motion, bool p_infinite_inertia, real_t p_margin, const Ref<PhysicsTestMotionResult2D> &p_result) {
- PhysicsServer2D::MotionResult *r = nullptr;
- if (p_result.is_valid()) {
- r = p_result->get_result_ptr();
- }
- return PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), get_global_transform(), p_motion, p_infinite_inertia, p_margin, r);
-}
-
void RigidBody2D::_direct_state_changed(Object *p_state) {
#ifdef DEBUG_ENABLED
state = Object::cast_to<PhysicsDirectBodyState2D>(p_state);
@@ -378,8 +494,8 @@ void RigidBody2D::_direct_state_changed(Object *p_state) {
void RigidBody2D::set_mode(Mode p_mode) {
mode = p_mode;
switch (p_mode) {
- case MODE_RIGID: {
- PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BODY_MODE_RIGID);
+ case MODE_DYNAMIC: {
+ PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BODY_MODE_DYNAMIC);
} break;
case MODE_STATIC: {
PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BODY_MODE_STATIC);
@@ -389,8 +505,8 @@ void RigidBody2D::set_mode(Mode p_mode) {
PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BODY_MODE_KINEMATIC);
} break;
- case MODE_CHARACTER: {
- PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BODY_MODE_CHARACTER);
+ case MODE_DYNAMIC_LOCKED: {
+ PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BODY_MODE_DYNAMIC_LOCKED);
} break;
}
@@ -666,8 +782,8 @@ TypedArray<String> RigidBody2D::get_configuration_warnings() const {
TypedArray<String> warnings = CollisionObject2D::get_configuration_warnings();
- if ((get_mode() == MODE_RIGID || get_mode() == MODE_CHARACTER) && (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 RigidBody2D (in character or rigid modes) will be overridden by the physics engine when running.\nChange the size in children collision shapes instead."));
+ 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 RigidBody2D (in dynamic modes) will be overridden by the physics engine when running.\nChange the size in children collision shapes instead."));
}
return warnings;
@@ -734,13 +850,11 @@ void RigidBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_can_sleep", "able_to_sleep"), &RigidBody2D::set_can_sleep);
ClassDB::bind_method(D_METHOD("is_able_to_sleep"), &RigidBody2D::is_able_to_sleep);
- ClassDB::bind_method(D_METHOD("test_motion", "motion", "infinite_inertia", "margin", "result"), &RigidBody2D::_test_motion, DEFVAL(true), DEFVAL(0.08), DEFVAL(Variant()));
-
ClassDB::bind_method(D_METHOD("get_colliding_bodies"), &RigidBody2D::get_colliding_bodies);
BIND_VMETHOD(MethodInfo("_integrate_forces", PropertyInfo(Variant::OBJECT, "state", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsDirectBodyState2D")));
- ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Rigid,Static,Character,Kinematic"), "set_mode", "get_mode");
+ 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_EXP_RANGE, "0.01,65535,0.01"), "set_mass", "get_mass");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inertia", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01", 0), "set_inertia", "get_inertia");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override");
@@ -767,9 +881,9 @@ void RigidBody2D::_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_RIGID);
+ BIND_ENUM_CONSTANT(MODE_DYNAMIC);
BIND_ENUM_CONSTANT(MODE_STATIC);
- BIND_ENUM_CONSTANT(MODE_CHARACTER);
+ BIND_ENUM_CONSTANT(MODE_DYNAMIC_LOCKED);
BIND_ENUM_CONSTANT(MODE_KINEMATIC);
BIND_ENUM_CONSTANT(CCD_MODE_DISABLED);
@@ -778,7 +892,7 @@ void RigidBody2D::_bind_methods() {
}
RigidBody2D::RigidBody2D() :
- PhysicsBody2D(PhysicsServer2D::BODY_MODE_RIGID) {
+ PhysicsBody2D(PhysicsServer2D::BODY_MODE_DYNAMIC) {
PhysicsServer2D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &RigidBody2D::_direct_state_changed));
}
@@ -800,95 +914,13 @@ void RigidBody2D::_reload_physics_characteristics() {
//////////////////////////
-Ref<KinematicCollision2D> KinematicBody2D::_move(const Vector2 &p_motion, bool p_infinite_inertia, bool p_exclude_raycast_shapes, bool p_test_only) {
- Collision col;
-
- if (move_and_collide(p_motion, p_infinite_inertia, col, p_exclude_raycast_shapes, p_test_only)) {
- if (motion_cache.is_null()) {
- motion_cache.instance();
- motion_cache->owner = this;
- }
-
- motion_cache->collision = col;
-
- return motion_cache;
- }
-
- return Ref<KinematicCollision2D>();
-}
-
-bool KinematicBody2D::separate_raycast_shapes(bool p_infinite_inertia, Collision &r_collision) {
- PhysicsServer2D::SeparationResult sep_res[8]; //max 8 rays
-
- Transform2D gt = get_global_transform();
-
- Vector2 recover;
- int hits = PhysicsServer2D::get_singleton()->body_test_ray_separation(get_rid(), gt, p_infinite_inertia, recover, sep_res, 8, margin);
- int deepest = -1;
- real_t deepest_depth;
- for (int i = 0; i < hits; i++) {
- if (deepest == -1 || sep_res[i].collision_depth > deepest_depth) {
- deepest = i;
- deepest_depth = sep_res[i].collision_depth;
- }
- }
-
- gt.elements[2] += recover;
- set_global_transform(gt);
-
- if (deepest != -1) {
- r_collision.collider = sep_res[deepest].collider_id;
- r_collision.collider_metadata = sep_res[deepest].collider_metadata;
- r_collision.collider_shape = sep_res[deepest].collider_shape;
- r_collision.collider_vel = sep_res[deepest].collider_velocity;
- r_collision.collision = sep_res[deepest].collision_point;
- r_collision.normal = sep_res[deepest].collision_normal;
- r_collision.local_shape = sep_res[deepest].collision_local_shape;
- r_collision.travel = recover;
- r_collision.remainder = Vector2();
-
- return true;
- } else {
- return false;
- }
-}
-
-bool KinematicBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, Collision &r_collision, bool p_exclude_raycast_shapes, bool p_test_only) {
- if (sync_to_physics) {
- ERR_PRINT("Functions move_and_slide and move_and_collide do not work together with 'sync to physics' option. Please read the documentation.");
- }
- Transform2D gt = get_global_transform();
- PhysicsServer2D::MotionResult result;
- bool colliding = PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_infinite_inertia, margin, &result, p_exclude_raycast_shapes);
-
- if (colliding) {
- r_collision.collider_metadata = result.collider_metadata;
- r_collision.collider_shape = result.collider_shape;
- r_collision.collider_vel = result.collider_velocity;
- r_collision.collision = result.collision_point;
- r_collision.normal = result.collision_normal;
- r_collision.collider = result.collider_id;
- r_collision.collider_rid = result.collider;
- r_collision.travel = result.motion;
- r_collision.remainder = result.remainder;
- r_collision.local_shape = result.collision_local_shape;
- }
-
- if (!p_test_only) {
- gt.elements[2] += result.motion;
- set_global_transform(gt);
- }
-
- return colliding;
-}
-
//so, if you pass 45 as limit, avoid numerical precision errors when angle is 45.
#define FLOOR_ANGLE_THRESHOLD 0.01
-Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const Vector2 &p_up_direction, bool p_stop_on_slope, int p_max_slides, real_t p_floor_max_angle, bool p_infinite_inertia) {
- Vector2 body_velocity = p_linear_velocity;
- Vector2 body_velocity_normal = body_velocity.normalized();
- Vector2 up_direction = p_up_direction.normalized();
+void CharacterBody2D::move_and_slide() {
+ Vector2 body_velocity_normal = linear_velocity.normalized();
+
+ bool was_on_floor = on_floor;
Vector2 current_floor_velocity = floor_velocity;
if (on_floor && on_floor_body.is_valid()) {
@@ -900,69 +932,71 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const
}
// Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky
- Vector2 motion = (current_floor_velocity + body_velocity) * (Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time());
+ Vector2 motion = (current_floor_velocity + linear_velocity) * (Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time());
on_floor = false;
on_floor_body = RID();
on_ceiling = false;
on_wall = false;
- colliders.clear();
+ motion_results.clear();
floor_normal = Vector2();
floor_velocity = Vector2();
- while (p_max_slides) {
- Collision collision;
+ int slide_count = max_slides;
+ while (slide_count) {
+ PhysicsServer2D::MotionResult result;
bool found_collision = false;
for (int i = 0; i < 2; ++i) {
bool collided;
if (i == 0) { //collide
- collided = move_and_collide(motion, p_infinite_inertia, collision);
+ collided = move_and_collide(motion, infinite_inertia, result, margin);
if (!collided) {
motion = Vector2(); //clear because no collision happened and motion completed
}
} else { //separate raycasts (if any)
- collided = separate_raycast_shapes(p_infinite_inertia, collision);
+ collided = separate_raycast_shapes(result);
if (collided) {
- collision.remainder = motion; //keep
- collision.travel = Vector2();
+ result.remainder = motion; //keep
+ result.motion = Vector2();
}
}
if (collided) {
found_collision = true;
- colliders.push_back(collision);
- motion = collision.remainder;
+ motion_results.push_back(result);
+ motion = result.remainder;
if (up_direction == Vector2()) {
//all is a wall
on_wall = true;
} else {
- if (Math::acos(collision.normal.dot(up_direction)) <= p_floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //floor
+ if (Math::acos(result.collision_normal.dot(up_direction)) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //floor
on_floor = true;
- floor_normal = collision.normal;
- on_floor_body = collision.collider_rid;
- floor_velocity = collision.collider_vel;
+ floor_normal = result.collision_normal;
+ on_floor_body = result.collider;
+ floor_velocity = result.collider_velocity;
- if (p_stop_on_slope) {
- if ((body_velocity_normal + up_direction).length() < 0.01 && collision.travel.length() < 1) {
+ if (stop_on_slope) {
+ if ((body_velocity_normal + up_direction).length() < 0.01 && result.motion.length() < 1) {
Transform2D gt = get_global_transform();
- gt.elements[2] -= collision.travel.slide(up_direction);
+ gt.elements[2] -= result.motion.slide(up_direction);
set_global_transform(gt);
- return Vector2();
+ linear_velocity = Vector2();
+ return;
}
}
- } else if (Math::acos(collision.normal.dot(-up_direction)) <= p_floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //ceiling
+ } else if (Math::acos(result.collision_normal.dot(-up_direction)) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //ceiling
on_ceiling = true;
} else {
on_wall = true;
}
}
- motion = motion.slide(collision.normal);
- body_velocity = body_velocity.slide(collision.normal);
+ motion = motion.slide(result.collision_normal);
+ linear_velocity = linear_velocity.slide(result.collision_normal);
}
}
@@ -970,36 +1004,28 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const
break;
}
- --p_max_slides;
+ --slide_count;
}
- return body_velocity;
-}
-
-Vector2 KinematicBody2D::move_and_slide_with_snap(const Vector2 &p_linear_velocity, const Vector2 &p_snap, const Vector2 &p_up_direction, bool p_stop_on_slope, int p_max_slides, real_t p_floor_max_angle, bool p_infinite_inertia) {
- Vector2 up_direction = p_up_direction.normalized();
- bool was_on_floor = on_floor;
-
- Vector2 ret = move_and_slide(p_linear_velocity, up_direction, p_stop_on_slope, p_max_slides, p_floor_max_angle, p_infinite_inertia);
- if (!was_on_floor || p_snap == Vector2()) {
- return ret;
+ if (!was_on_floor || snap == Vector2()) {
+ return;
}
- Collision col;
+ // Apply snap.
Transform2D gt = get_global_transform();
-
- if (move_and_collide(p_snap, p_infinite_inertia, col, false, true)) {
+ PhysicsServer2D::MotionResult result;
+ if (move_and_collide(snap, infinite_inertia, result, margin, false, true)) {
bool apply = true;
if (up_direction != Vector2()) {
- if (Math::acos(col.normal.dot(up_direction)) <= p_floor_max_angle + FLOOR_ANGLE_THRESHOLD) {
+ if (Math::acos(result.collision_normal.dot(up_direction)) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) {
on_floor = true;
- floor_normal = col.normal;
- on_floor_body = col.collider_rid;
- floor_velocity = col.collider_vel;
- if (p_stop_on_slope) {
+ floor_normal = result.collision_normal;
+ on_floor_body = result.collider;
+ floor_velocity = result.collider_velocity;
+ if (stop_on_slope) {
// move and collide may stray the object a bit because of pre un-stucking,
// so only ensure that motion happens on floor direction in this case.
- col.travel = up_direction * up_direction.dot(col.travel);
+ result.motion = up_direction * up_direction.dot(result.motion);
}
} else {
@@ -1008,59 +1034,87 @@ Vector2 KinematicBody2D::move_and_slide_with_snap(const Vector2 &p_linear_veloci
}
if (apply) {
- gt.elements[2] += col.travel;
+ gt.elements[2] += result.motion;
set_global_transform(gt);
}
}
-
- return ret;
}
-bool KinematicBody2D::is_on_floor() const {
- return on_floor;
-}
+bool CharacterBody2D::separate_raycast_shapes(PhysicsServer2D::MotionResult &r_result) {
+ PhysicsServer2D::SeparationResult sep_res[8]; //max 8 rays
-bool KinematicBody2D::is_on_wall() const {
- return on_wall;
+ Transform2D gt = get_global_transform();
+
+ Vector2 recover;
+ int hits = PhysicsServer2D::get_singleton()->body_test_ray_separation(get_rid(), gt, infinite_inertia, recover, sep_res, 8, margin);
+ int deepest = -1;
+ real_t deepest_depth;
+ for (int i = 0; i < hits; i++) {
+ if (deepest == -1 || sep_res[i].collision_depth > deepest_depth) {
+ deepest = i;
+ deepest_depth = sep_res[i].collision_depth;
+ }
+ }
+
+ gt.elements[2] += recover;
+ set_global_transform(gt);
+
+ if (deepest != -1) {
+ r_result.collider_id = sep_res[deepest].collider_id;
+ r_result.collider_metadata = sep_res[deepest].collider_metadata;
+ r_result.collider_shape = sep_res[deepest].collider_shape;
+ r_result.collider_velocity = sep_res[deepest].collider_velocity;
+ r_result.collision_point = sep_res[deepest].collision_point;
+ r_result.collision_normal = sep_res[deepest].collision_normal;
+ r_result.collision_local_shape = sep_res[deepest].collision_local_shape;
+ r_result.motion = recover;
+ r_result.remainder = Vector2();
+
+ return true;
+ } else {
+ return false;
+ }
}
-bool KinematicBody2D::is_on_ceiling() const {
- return on_ceiling;
+const Vector2 &CharacterBody2D::get_linear_velocity() const {
+ return linear_velocity;
}
-Vector2 KinematicBody2D::get_floor_normal() const {
- return floor_normal;
+void CharacterBody2D::set_linear_velocity(const Vector2 &p_velocity) {
+ linear_velocity = p_velocity;
}
-Vector2 KinematicBody2D::get_floor_velocity() const {
- return floor_velocity;
+bool CharacterBody2D::is_on_floor() const {
+ return on_floor;
}
-bool KinematicBody2D::test_move(const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia) {
- ERR_FAIL_COND_V(!is_inside_tree(), false);
+bool CharacterBody2D::is_on_wall() const {
+ return on_wall;
+}
- return PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), p_from, p_motion, p_infinite_inertia, margin);
+bool CharacterBody2D::is_on_ceiling() const {
+ return on_ceiling;
}
-void KinematicBody2D::set_safe_margin(real_t p_margin) {
- margin = p_margin;
+Vector2 CharacterBody2D::get_floor_normal() const {
+ return floor_normal;
}
-real_t KinematicBody2D::get_safe_margin() const {
- return margin;
+Vector2 CharacterBody2D::get_floor_velocity() const {
+ return floor_velocity;
}
-int KinematicBody2D::get_slide_count() const {
- return colliders.size();
+int CharacterBody2D::get_slide_count() const {
+ return motion_results.size();
}
-KinematicBody2D::Collision KinematicBody2D::get_slide_collision(int p_bounce) const {
- ERR_FAIL_INDEX_V(p_bounce, colliders.size(), Collision());
- return colliders[p_bounce];
+PhysicsServer2D::MotionResult CharacterBody2D::get_slide_collision(int p_bounce) const {
+ ERR_FAIL_INDEX_V(p_bounce, motion_results.size(), PhysicsServer2D::MotionResult());
+ return motion_results[p_bounce];
}
-Ref<KinematicCollision2D> KinematicBody2D::_get_slide_collision(int p_bounce) {
- ERR_FAIL_INDEX_V(p_bounce, colliders.size(), Ref<KinematicCollision2D>());
+Ref<KinematicCollision2D> CharacterBody2D::_get_slide_collision(int p_bounce) {
+ ERR_FAIL_INDEX_V(p_bounce, motion_results.size(), Ref<KinematicCollision2D>());
if (p_bounce >= slide_colliders.size()) {
slide_colliders.resize(p_bounce + 1);
}
@@ -1070,11 +1124,11 @@ Ref<KinematicCollision2D> KinematicBody2D::_get_slide_collision(int p_bounce) {
slide_colliders.write[p_bounce]->owner = this;
}
- slide_colliders.write[p_bounce]->collision = colliders[p_bounce];
+ slide_colliders.write[p_bounce]->result = motion_results[p_bounce];
return slide_colliders[p_bounce];
}
-void KinematicBody2D::set_sync_to_physics(bool p_enable) {
+void CharacterBody2D::set_sync_to_physics(bool p_enable) {
if (sync_to_physics == p_enable) {
return;
}
@@ -1085,7 +1139,7 @@ void KinematicBody2D::set_sync_to_physics(bool p_enable) {
}
if (p_enable) {
- PhysicsServer2D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &KinematicBody2D::_direct_state_changed));
+ PhysicsServer2D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &CharacterBody2D::_direct_state_changed));
set_only_update_transform_changes(true);
set_notify_local_transform(true);
} else {
@@ -1095,11 +1149,11 @@ void KinematicBody2D::set_sync_to_physics(bool p_enable) {
}
}
-bool KinematicBody2D::is_sync_to_physics_enabled() const {
+bool CharacterBody2D::is_sync_to_physics_enabled() const {
return sync_to_physics;
}
-void KinematicBody2D::_direct_state_changed(Object *p_state) {
+void CharacterBody2D::_direct_state_changed(Object *p_state) {
if (!sync_to_physics) {
return;
}
@@ -1113,7 +1167,63 @@ void KinematicBody2D::_direct_state_changed(Object *p_state) {
set_notify_local_transform(true);
}
-void KinematicBody2D::_notification(int p_what) {
+void CharacterBody2D::set_safe_margin(real_t p_margin) {
+ margin = p_margin;
+}
+
+real_t CharacterBody2D::get_safe_margin() const {
+ return margin;
+}
+
+bool CharacterBody2D::is_stop_on_slope_enabled() const {
+ return stop_on_slope;
+}
+
+void CharacterBody2D::set_stop_on_slope_enabled(bool p_enabled) {
+ stop_on_slope = p_enabled;
+}
+
+bool CharacterBody2D::is_infinite_inertia_enabled() const {
+ return infinite_inertia;
+}
+void CharacterBody2D::set_infinite_inertia_enabled(bool p_enabled) {
+ infinite_inertia = p_enabled;
+}
+
+int CharacterBody2D::get_max_slides() const {
+ return max_slides;
+}
+
+void CharacterBody2D::set_max_slides(int p_max_slides) {
+ ERR_FAIL_COND(p_max_slides > 0);
+ max_slides = p_max_slides;
+}
+
+real_t CharacterBody2D::get_floor_max_angle() const {
+ return floor_max_angle;
+}
+
+void CharacterBody2D::set_floor_max_angle(real_t p_floor_max_angle) {
+ floor_max_angle = p_floor_max_angle;
+}
+
+const Vector2 &CharacterBody2D::get_snap() const {
+ return snap;
+}
+
+void CharacterBody2D::set_snap(const Vector2 &p_snap) {
+ snap = p_snap;
+}
+
+const Vector2 &CharacterBody2D::get_up_direction() const {
+ return up_direction;
+}
+
+void CharacterBody2D::set_up_direction(const Vector2 &p_up_direction) {
+ up_direction = p_up_direction.normalized();
+}
+
+void CharacterBody2D::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
last_valid_transform = get_global_transform();
@@ -1122,7 +1232,7 @@ void KinematicBody2D::_notification(int p_what) {
on_floor_body = RID();
on_ceiling = false;
on_wall = false;
- colliders.clear();
+ motion_results.clear();
floor_velocity = Vector2();
}
@@ -1137,47 +1247,55 @@ void KinematicBody2D::_notification(int p_what) {
}
}
-void KinematicBody2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "infinite_inertia", "exclude_raycast_shapes", "test_only"), &KinematicBody2D::_move, DEFVAL(true), DEFVAL(true), DEFVAL(false));
- ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "up_direction", "stop_on_slope", "max_slides", "floor_max_angle", "infinite_inertia"), &KinematicBody2D::move_and_slide, DEFVAL(Vector2(0, 0)), DEFVAL(false), DEFVAL(4), DEFVAL(Math::deg2rad((real_t)45.0)), DEFVAL(true));
- ClassDB::bind_method(D_METHOD("move_and_slide_with_snap", "linear_velocity", "snap", "up_direction", "stop_on_slope", "max_slides", "floor_max_angle", "infinite_inertia"), &KinematicBody2D::move_and_slide_with_snap, DEFVAL(Vector2(0, 0)), DEFVAL(false), DEFVAL(4), DEFVAL(Math::deg2rad((real_t)45.0)), DEFVAL(true));
-
- ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec", "infinite_inertia"), &KinematicBody2D::test_move, DEFVAL(true));
-
- ClassDB::bind_method(D_METHOD("is_on_floor"), &KinematicBody2D::is_on_floor);
- ClassDB::bind_method(D_METHOD("is_on_ceiling"), &KinematicBody2D::is_on_ceiling);
- ClassDB::bind_method(D_METHOD("is_on_wall"), &KinematicBody2D::is_on_wall);
- ClassDB::bind_method(D_METHOD("get_floor_normal"), &KinematicBody2D::get_floor_normal);
- ClassDB::bind_method(D_METHOD("get_floor_velocity"), &KinematicBody2D::get_floor_velocity);
-
- ClassDB::bind_method(D_METHOD("set_safe_margin", "pixels"), &KinematicBody2D::set_safe_margin);
- ClassDB::bind_method(D_METHOD("get_safe_margin"), &KinematicBody2D::get_safe_margin);
+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_safe_margin", "pixels"), &CharacterBody2D::set_safe_margin);
+ ClassDB::bind_method(D_METHOD("get_safe_margin"), &CharacterBody2D::get_safe_margin);
+ ClassDB::bind_method(D_METHOD("is_stop_on_slope_enabled"), &CharacterBody2D::is_stop_on_slope_enabled);
+ ClassDB::bind_method(D_METHOD("set_stop_on_slope_enabled", "enabled"), &CharacterBody2D::set_stop_on_slope_enabled);
+ ClassDB::bind_method(D_METHOD("is_infinite_inertia_enabled"), &CharacterBody2D::is_infinite_inertia_enabled);
+ ClassDB::bind_method(D_METHOD("set_infinite_inertia_enabled", "enabled"), &CharacterBody2D::set_infinite_inertia_enabled);
+ ClassDB::bind_method(D_METHOD("get_max_slides"), &CharacterBody2D::get_max_slides);
+ ClassDB::bind_method(D_METHOD("set_max_slides", "max_slides"), &CharacterBody2D::set_max_slides);
+ ClassDB::bind_method(D_METHOD("get_floor_max_angle"), &CharacterBody2D::get_floor_max_angle);
+ ClassDB::bind_method(D_METHOD("set_floor_max_angle", "floor_max_angle"), &CharacterBody2D::set_floor_max_angle);
+ ClassDB::bind_method(D_METHOD("get_snap"), &CharacterBody2D::get_snap);
+ ClassDB::bind_method(D_METHOD("set_snap", "snap"), &CharacterBody2D::set_snap);
+ ClassDB::bind_method(D_METHOD("get_up_direction"), &CharacterBody2D::get_up_direction);
+ ClassDB::bind_method(D_METHOD("set_up_direction", "up_direction"), &CharacterBody2D::set_up_direction);
+
+ ClassDB::bind_method(D_METHOD("is_on_floor"), &CharacterBody2D::is_on_floor);
+ ClassDB::bind_method(D_METHOD("is_on_ceiling"), &CharacterBody2D::is_on_ceiling);
+ ClassDB::bind_method(D_METHOD("is_on_wall"), &CharacterBody2D::is_on_wall);
+ ClassDB::bind_method(D_METHOD("get_floor_normal"), &CharacterBody2D::get_floor_normal);
+ ClassDB::bind_method(D_METHOD("get_floor_velocity"), &CharacterBody2D::get_floor_velocity);
+ ClassDB::bind_method(D_METHOD("get_slide_count"), &CharacterBody2D::get_slide_count);
+ ClassDB::bind_method(D_METHOD("get_slide_collision", "slide_idx"), &CharacterBody2D::_get_slide_collision);
+
+ ClassDB::bind_method(D_METHOD("set_sync_to_physics", "enable"), &CharacterBody2D::set_sync_to_physics);
+ ClassDB::bind_method(D_METHOD("is_sync_to_physics_enabled"), &CharacterBody2D::is_sync_to_physics_enabled);
- ClassDB::bind_method(D_METHOD("get_slide_count"), &KinematicBody2D::get_slide_count);
- ClassDB::bind_method(D_METHOD("get_slide_collision", "slide_idx"), &KinematicBody2D::_get_slide_collision);
-
- ClassDB::bind_method(D_METHOD("set_sync_to_physics", "enable"), &KinematicBody2D::set_sync_to_physics);
- ClassDB::bind_method(D_METHOD("is_sync_to_physics_enabled"), &KinematicBody2D::is_sync_to_physics_enabled);
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "linear_velocity"), "set_linear_velocity", "get_linear_velocity");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stop_on_slope"), "set_stop_on_slope_enabled", "is_stop_on_slope_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "infinite_inertia"), "set_infinite_inertia_enabled", "is_infinite_inertia_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides"), "set_max_slides", "get_max_slides");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle"), "set_floor_max_angle", "get_floor_max_angle");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "snap"), "set_snap", "get_snap");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "up_direction"), "set_up_direction", "get_up_direction");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "motion/sync_to_physics"), "set_sync_to_physics", "is_sync_to_physics_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin");
}
-KinematicBody2D::KinematicBody2D() :
+CharacterBody2D::CharacterBody2D() :
PhysicsBody2D(PhysicsServer2D::BODY_MODE_KINEMATIC) {
- margin = 0.08;
-
- on_floor = false;
- on_ceiling = false;
- on_wall = false;
- sync_to_physics = false;
}
-KinematicBody2D::~KinematicBody2D() {
- if (motion_cache.is_valid()) {
- motion_cache->owner = nullptr;
- }
-
+CharacterBody2D::~CharacterBody2D() {
for (int i = 0; i < slide_colliders.size(); i++) {
if (slide_colliders[i].is_valid()) {
slide_colliders.write[i]->owner = nullptr;
@@ -1188,39 +1306,39 @@ KinematicBody2D::~KinematicBody2D() {
////////////////////////
Vector2 KinematicCollision2D::get_position() const {
- return collision.collision;
+ return result.collision_point;
}
Vector2 KinematicCollision2D::get_normal() const {
- return collision.normal;
+ return result.collision_normal;
}
Vector2 KinematicCollision2D::get_travel() const {
- return collision.travel;
+ return result.motion;
}
Vector2 KinematicCollision2D::get_remainder() const {
- return collision.remainder;
+ return result.remainder;
}
Object *KinematicCollision2D::get_local_shape() const {
if (!owner) {
return nullptr;
}
- uint32_t ownerid = owner->shape_find_owner(collision.local_shape);
+ uint32_t ownerid = owner->shape_find_owner(result.collision_local_shape);
return owner->shape_owner_get_owner(ownerid);
}
Object *KinematicCollision2D::get_collider() const {
- if (collision.collider.is_valid()) {
- return ObjectDB::get_instance(collision.collider);
+ if (result.collider_id.is_valid()) {
+ return ObjectDB::get_instance(result.collider_id);
}
return nullptr;
}
ObjectID KinematicCollision2D::get_collider_id() const {
- return collision.collider;
+ return result.collider_id;
}
Object *KinematicCollision2D::get_collider_shape() const {
@@ -1228,7 +1346,7 @@ Object *KinematicCollision2D::get_collider_shape() const {
if (collider) {
CollisionObject2D *obj2d = Object::cast_to<CollisionObject2D>(collider);
if (obj2d) {
- uint32_t ownerid = obj2d->shape_find_owner(collision.collider_shape);
+ uint32_t ownerid = obj2d->shape_find_owner(result.collider_shape);
return obj2d->shape_owner_get_owner(ownerid);
}
}
@@ -1237,11 +1355,11 @@ Object *KinematicCollision2D::get_collider_shape() const {
}
int KinematicCollision2D::get_collider_shape_index() const {
- return collision.collider_shape;
+ return result.collider_shape;
}
Vector2 KinematicCollision2D::get_collider_velocity() const {
- return collision.collider_vel;
+ return result.collider_velocity;
}
Variant KinematicCollision2D::get_collider_metadata() const {
@@ -1273,9 +1391,3 @@ void KinematicCollision2D::_bind_methods() {
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");
}
-
-KinematicCollision2D::KinematicCollision2D() {
- collision.collider_shape = 0;
- collision.local_shape = 0;
- owner = nullptr;
-}
diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h
index 47d55d11fa..5a44d31cc2 100644
--- a/scene/2d/physics_body_2d.h
+++ b/scene/2d/physics_body_2d.h
@@ -42,17 +42,22 @@ class PhysicsBody2D : public CollisionObject2D {
GDCLASS(PhysicsBody2D, CollisionObject2D);
protected:
- void _notification(int p_what);
+ static void _bind_methods();
PhysicsBody2D(PhysicsServer2D::BodyMode p_mode);
- static void _bind_methods();
+ Ref<KinematicCollision2D> motion_cache;
+
+ Ref<KinematicCollision2D> _move(const Vector2 &p_motion, bool p_infinite_inertia = true, bool p_exclude_raycast_shapes = true, bool p_test_only = false, real_t p_margin = 0.08);
public:
+ bool move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, PhysicsServer2D::MotionResult &r_result, real_t p_margin, bool p_exclude_raycast_shapes = true, bool p_test_only = false);
+ bool test_move(const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia = true, bool p_exclude_raycast_shapes = true, 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
void remove_collision_exception_with(Node *p_node);
- PhysicsBody2D();
+ virtual ~PhysicsBody2D();
};
class StaticBody2D : public PhysicsBody2D {
@@ -63,7 +68,10 @@ class StaticBody2D : public PhysicsBody2D {
Ref<PhysicsMaterial> physics_material_override;
+ bool kinematic_motion = false;
+
protected:
+ void _notification(int p_what);
static void _bind_methods();
public:
@@ -77,10 +85,14 @@ public:
real_t get_constant_angular_velocity() const;
StaticBody2D();
- ~StaticBody2D();
private:
void _reload_physics_characteristics();
+
+ void _update_kinematic_motion();
+
+ void set_kinematic_motion_enabled(bool p_enabled);
+ bool is_kinematic_motion_enabled() const;
};
class RigidBody2D : public PhysicsBody2D {
@@ -88,9 +100,9 @@ class RigidBody2D : public PhysicsBody2D {
public:
enum Mode {
- MODE_RIGID,
+ MODE_DYNAMIC,
MODE_STATIC,
- MODE_CHARACTER,
+ MODE_DYNAMIC_LOCKED,
MODE_KINEMATIC,
};
@@ -103,7 +115,7 @@ public:
private:
bool can_sleep = true;
PhysicsDirectBodyState2D *state = nullptr;
- Mode mode = MODE_RIGID;
+ Mode mode = MODE_DYNAMIC;
real_t mass = 1.0;
Ref<PhysicsMaterial> physics_material_override;
@@ -163,8 +175,6 @@ private:
void _body_inout(int p_status, const RID &p_body, ObjectID p_instance, int p_body_shape, int p_local_shape);
void _direct_state_changed(Object *p_state);
- bool _test_motion(const Vector2 &p_motion, bool p_infinite_inertia = true, real_t p_margin = 0.08, const Ref<PhysicsTestMotionResult2D> &p_result = Ref<PhysicsTestMotionResult2D>());
-
protected:
void _notification(int p_what);
static void _bind_methods();
@@ -245,62 +255,70 @@ private:
VARIANT_ENUM_CAST(RigidBody2D::Mode);
VARIANT_ENUM_CAST(RigidBody2D::CCDMode);
-class KinematicBody2D : public PhysicsBody2D {
- GDCLASS(KinematicBody2D, PhysicsBody2D);
-
-public:
- struct Collision {
- Vector2 collision;
- Vector2 normal;
- Vector2 collider_vel;
- ObjectID collider;
- RID collider_rid;
- int collider_shape = 0;
- Variant collider_metadata;
- Vector2 remainder;
- Vector2 travel;
- int local_shape = 0;
- };
+class CharacterBody2D : public PhysicsBody2D {
+ GDCLASS(CharacterBody2D, PhysicsBody2D);
private:
- real_t margin;
+ real_t margin = 0.08;
+
+ bool stop_on_slope = false;
+ bool infinite_inertia = true;
+ int max_slides = 4;
+ real_t floor_max_angle = Math::deg2rad((real_t)45.0);
+ Vector2 snap;
+ Vector2 up_direction = Vector2(0.0, -1.0);
+
+ Vector2 linear_velocity;
Vector2 floor_normal;
Vector2 floor_velocity;
RID on_floor_body;
- bool on_floor;
- bool on_ceiling;
- bool on_wall;
- bool sync_to_physics;
+ bool on_floor = false;
+ bool on_ceiling = false;
+ bool on_wall = false;
+ bool sync_to_physics = false;
- Vector<Collision> colliders;
+ Vector<PhysicsServer2D::MotionResult> motion_results;
Vector<Ref<KinematicCollision2D>> slide_colliders;
- Ref<KinematicCollision2D> motion_cache;
-
- _FORCE_INLINE_ bool _ignores_mode(PhysicsServer2D::BodyMode) const;
- Ref<KinematicCollision2D> _move(const Vector2 &p_motion, bool p_infinite_inertia = true, bool p_exclude_raycast_shapes = true, bool p_test_only = false);
Ref<KinematicCollision2D> _get_slide_collision(int p_bounce);
+ bool separate_raycast_shapes(PhysicsServer2D::MotionResult &r_result);
+
Transform2D last_valid_transform;
void _direct_state_changed(Object *p_state);
+ void set_safe_margin(real_t p_margin);
+ real_t get_safe_margin() const;
+
+ bool is_stop_on_slope_enabled() const;
+ void set_stop_on_slope_enabled(bool p_enabled);
+
+ bool is_infinite_inertia_enabled() const;
+ void set_infinite_inertia_enabled(bool p_enabled);
+
+ int get_max_slides() const;
+ void set_max_slides(int p_max_slides);
+
+ real_t get_floor_max_angle() const;
+ void set_floor_max_angle(real_t p_floor_max_angle);
+
+ const Vector2 &get_snap() const;
+ void set_snap(const Vector2 &p_snap);
+
+ const Vector2 &get_up_direction() const;
+ void set_up_direction(const Vector2 &p_up_direction);
+
protected:
void _notification(int p_what);
static void _bind_methods();
public:
- bool move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, Collision &r_collision, bool p_exclude_raycast_shapes = true, bool p_test_only = false);
-
- bool test_move(const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia = true);
-
- bool separate_raycast_shapes(bool p_infinite_inertia, Collision &r_collision);
+ void move_and_slide();
- void set_safe_margin(real_t p_margin);
- real_t get_safe_margin() const;
+ const Vector2 &get_linear_velocity() const;
+ void set_linear_velocity(const Vector2 &p_velocity);
- Vector2 move_and_slide(const Vector2 &p_linear_velocity, const Vector2 &p_up_direction = Vector2(0, 0), bool p_stop_on_slope = false, int p_max_slides = 4, real_t p_floor_max_angle = Math::deg2rad((real_t)45.0), bool p_infinite_inertia = true);
- Vector2 move_and_slide_with_snap(const Vector2 &p_linear_velocity, const Vector2 &p_snap, const Vector2 &p_up_direction = Vector2(0, 0), bool p_stop_on_slope = false, int p_max_slides = 4, real_t p_floor_max_angle = Math::deg2rad((real_t)45.0), bool p_infinite_inertia = true);
bool is_on_floor() const;
bool is_on_wall() const;
bool is_on_ceiling() const;
@@ -308,21 +326,22 @@ public:
Vector2 get_floor_velocity() const;
int get_slide_count() const;
- Collision get_slide_collision(int p_bounce) const;
+ PhysicsServer2D::MotionResult get_slide_collision(int p_bounce) const;
void set_sync_to_physics(bool p_enable);
bool is_sync_to_physics_enabled() const;
- KinematicBody2D();
- ~KinematicBody2D();
+ CharacterBody2D();
+ ~CharacterBody2D();
};
class KinematicCollision2D : public Reference {
GDCLASS(KinematicCollision2D, Reference);
- KinematicBody2D *owner;
- friend class KinematicBody2D;
- KinematicBody2D::Collision collision;
+ PhysicsBody2D *owner = nullptr;
+ friend class PhysicsBody2D;
+ friend class CharacterBody2D;
+ PhysicsServer2D::MotionResult result;
protected:
static void _bind_methods();
@@ -339,8 +358,6 @@ public:
int get_collider_shape_index() const;
Vector2 get_collider_velocity() const;
Variant get_collider_metadata() const;
-
- KinematicCollision2D();
};
#endif // PHYSICS_BODY_2D_H
diff --git a/scene/2d/position_2d.cpp b/scene/2d/position_2d.cpp
index 5c7d65e3e0..1019f85c8a 100644
--- a/scene/2d/position_2d.cpp
+++ b/scene/2d/position_2d.cpp
@@ -36,10 +36,41 @@
const real_t DEFAULT_GIZMO_EXTENTS = 10.0;
void Position2D::_draw_cross() {
- real_t extents = get_gizmo_extents();
- // Colors taken from `axis_x_color` and `axis_y_color` (defined in `editor/editor_themes.cpp`)
- draw_line(Point2(-extents, 0), Point2(+extents, 0), Color(0.96, 0.20, 0.32));
- draw_line(Point2(0, -extents), Point2(0, +extents), Color(0.53, 0.84, 0.01));
+ const real_t extents = get_gizmo_extents();
+
+ // Add more points to create a "hard stop" in the color gradient.
+ PackedVector2Array points_x;
+ points_x.push_back(Point2(+extents, 0));
+ points_x.push_back(Point2());
+ points_x.push_back(Point2());
+ points_x.push_back(Point2(-extents, 0));
+
+ PackedVector2Array points_y;
+ points_y.push_back(Point2(0, +extents));
+ points_y.push_back(Point2());
+ points_y.push_back(Point2());
+ points_y.push_back(Point2(0, -extents));
+
+ // Use the axis color which is brighter for the positive axis.
+ // Use a darkened axis color for the negative axis.
+ // This makes it possible to see in which direction the Position3D node is rotated
+ // (which can be important depending on how it's used).
+ // Axis colors are taken from `axis_x_color` and `axis_y_color` (defined in `editor/editor_themes.cpp`).
+ PackedColorArray colors_x;
+ const Color color_x = Color(0.96, 0.20, 0.32);
+ colors_x.push_back(color_x);
+ colors_x.push_back(color_x);
+ colors_x.push_back(color_x.lerp(Color(0, 0, 0), 0.5));
+ colors_x.push_back(color_x.lerp(Color(0, 0, 0), 0.5));
+ draw_multiline_colors(points_x, colors_x);
+
+ PackedColorArray colors_y;
+ const Color color_y = Color(0.53, 0.84, 0.01);
+ colors_y.push_back(color_y);
+ colors_y.push_back(color_y);
+ colors_y.push_back(color_y.lerp(Color(0, 0, 0), 0.5));
+ colors_y.push_back(color_y.lerp(Color(0, 0, 0), 0.5));
+ draw_multiline_colors(points_y, colors_y);
}
#ifdef TOOLS_ENABLED
diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp
index 22180797f0..c5ad3dde39 100644
--- a/scene/2d/skeleton_2d.cpp
+++ b/scene/2d/skeleton_2d.cpp
@@ -30,6 +30,69 @@
#include "skeleton_2d.h"
+#include "scene/resources/skeleton_modification_2d.h"
+
+#ifdef TOOLS_ENABLED
+#include "editor/editor_settings.h"
+#include "editor/plugins/canvas_item_editor_plugin.h"
+#endif //TOOLS_ENABLED
+
+bool Bone2D::_set(const StringName &p_path, const Variant &p_value) {
+ String path = p_path;
+
+ if (path.begins_with("auto_calculate_length_and_angle")) {
+ set_autocalculate_length_and_angle(p_value);
+ } else if (path.begins_with("length")) {
+ set_length(p_value);
+ } else if (path.begins_with("bone_angle")) {
+ set_bone_angle(Math::deg2rad(float(p_value)));
+ } else if (path.begins_with("default_length")) {
+ set_length(p_value);
+ }
+
+#ifdef TOOLS_ENABLED
+ if (path.begins_with("editor_settings/show_bone_gizmo")) {
+ _editor_set_show_bone_gizmo(p_value);
+ }
+#endif // TOOLS_ENABLED
+
+ return true;
+}
+
+bool Bone2D::_get(const StringName &p_path, Variant &r_ret) const {
+ String path = p_path;
+
+ if (path.begins_with("auto_calculate_length_and_angle")) {
+ r_ret = get_autocalculate_length_and_angle();
+ } else if (path.begins_with("length")) {
+ r_ret = get_length();
+ } else if (path.begins_with("bone_angle")) {
+ r_ret = Math::rad2deg(get_bone_angle());
+ } else if (path.begins_with("default_length")) {
+ r_ret = get_length();
+ }
+
+#ifdef TOOLS_ENABLED
+ if (path.begins_with("editor_settings/show_bone_gizmo")) {
+ r_ret = _editor_get_show_bone_gizmo();
+ }
+#endif // TOOLS_ENABLED
+
+ return true;
+}
+
+void Bone2D::_get_property_list(List<PropertyInfo> *p_list) const {
+ p_list->push_back(PropertyInfo(Variant::BOOL, "auto_calculate_length_and_angle", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+ if (!autocalculate_length_and_angle) {
+ p_list->push_back(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_RANGE, "1, 1024, 1", PROPERTY_USAGE_DEFAULT));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, "bone_angle", PROPERTY_HINT_RANGE, "-360, 360, 0.01", PROPERTY_USAGE_DEFAULT));
+ }
+
+#ifdef TOOLS_ENABLED
+ p_list->push_back(PropertyInfo(Variant::BOOL, "editor_settings/show_bone_gizmo", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
+#endif // TOOLS_ENABLED
+}
+
void Bone2D::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
Node *parent = get_parent();
@@ -53,19 +116,54 @@ void Bone2D::_notification(int p_what) {
skeleton->bones.push_back(bone);
skeleton->_make_bone_setup_dirty();
}
+
+ cache_transform = get_transform();
+ copy_transform_to_cache = true;
+
+#ifdef TOOLS_ENABLED
+ // Only draw the gizmo in the editor!
+ if (Engine::get_singleton()->is_editor_hint() == false) {
+ return;
+ }
+
+ update();
+#endif // TOOLS_ENABLED
}
- if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) {
+
+ else if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) {
if (skeleton) {
skeleton->_make_transform_dirty();
}
+ if (copy_transform_to_cache) {
+ cache_transform = get_transform();
+ }
+#ifdef TOOLS_ENABLED
+ // Only draw the gizmo in the editor!
+ if (Engine::get_singleton()->is_editor_hint() == false) {
+ return;
+ }
+
+ update();
+
+ if (get_parent()) {
+ Bone2D *parent_bone = Object::cast_to<Bone2D>(get_parent());
+ if (parent_bone) {
+ parent_bone->update();
+ }
+ }
+#endif // TOOLS_ENABLED
}
- if (p_what == NOTIFICATION_MOVED_IN_PARENT) {
+
+ else if (p_what == NOTIFICATION_MOVED_IN_PARENT) {
if (skeleton) {
skeleton->_make_bone_setup_dirty();
}
+ if (copy_transform_to_cache) {
+ cache_transform = get_transform();
+ }
}
- if (p_what == NOTIFICATION_EXIT_TREE) {
+ else if (p_what == NOTIFICATION_EXIT_TREE) {
if (skeleton) {
for (int i = 0; i < skeleton->bones.size(); i++) {
if (skeleton->bones[i].bone == this) {
@@ -77,9 +175,200 @@ void Bone2D::_notification(int p_what) {
skeleton = nullptr;
}
parent_bone = nullptr;
+ set_transform(cache_transform);
}
+
+ else if (p_what == NOTIFICATION_READY) {
+ if (autocalculate_length_and_angle) {
+ calculate_length_and_rotation();
+ }
+ }
+#ifdef TOOLS_ENABLED
+ else if (p_what == NOTIFICATION_EDITOR_PRE_SAVE || p_what == NOTIFICATION_EDITOR_POST_SAVE) {
+ Transform2D tmp_trans = get_transform();
+ set_transform(cache_transform);
+ cache_transform = tmp_trans;
+ }
+ // Bone2D Editor gizmo drawing:
+#ifndef _MSC_VER
+#warning TODO Bone2D gizmo drawing needs to be moved to an editor plugin
+#endif
+ else if (p_what == NOTIFICATION_DRAW) {
+ // Only draw the gizmo in the editor!
+ if (Engine::get_singleton()->is_editor_hint() == false) {
+ return;
+ }
+
+ if (editor_gizmo_rid.is_null()) {
+ editor_gizmo_rid = RenderingServer::get_singleton()->canvas_item_create();
+ RenderingServer::get_singleton()->canvas_item_set_parent(editor_gizmo_rid, get_canvas_item());
+ RenderingServer::get_singleton()->canvas_item_set_z_as_relative_to_parent(editor_gizmo_rid, true);
+ RenderingServer::get_singleton()->canvas_item_set_z_index(editor_gizmo_rid, 10);
+ }
+ RenderingServer::get_singleton()->canvas_item_clear(editor_gizmo_rid);
+
+ if (!_editor_show_bone_gizmo) {
+ return;
+ }
+
+ // Undo scaling
+ Transform2D editor_gizmo_trans = Transform2D();
+ editor_gizmo_trans.set_scale(Vector2(1, 1) / get_global_scale());
+ RenderingServer::get_singleton()->canvas_item_set_transform(editor_gizmo_rid, editor_gizmo_trans);
+
+ Color bone_color1 = EditorSettings::get_singleton()->get("editors/2d/bone_color1");
+ Color bone_color2 = EditorSettings::get_singleton()->get("editors/2d/bone_color2");
+ Color bone_ik_color = EditorSettings::get_singleton()->get("editors/2d/bone_ik_color");
+ Color bone_outline_color = EditorSettings::get_singleton()->get("editors/2d/bone_outline_color");
+ Color bone_selected_color = EditorSettings::get_singleton()->get("editors/2d/bone_selected_color");
+
+ bool Bone2D_found = false;
+ for (int i = 0; i < get_child_count(); i++) {
+ Bone2D *child_node = nullptr;
+ child_node = Object::cast_to<Bone2D>(get_child(i));
+ if (!child_node) {
+ continue;
+ }
+ Bone2D_found = true;
+
+ Vector<Vector2> bone_shape;
+ Vector<Vector2> bone_shape_outline;
+
+ _editor_get_bone_shape(&bone_shape, &bone_shape_outline, child_node);
+
+ Vector<Color> colors;
+ if (has_meta("_local_pose_override_enabled_")) {
+ colors.push_back(bone_ik_color);
+ colors.push_back(bone_ik_color);
+ colors.push_back(bone_ik_color);
+ colors.push_back(bone_ik_color);
+ } else {
+ colors.push_back(bone_color1);
+ colors.push_back(bone_color2);
+ colors.push_back(bone_color1);
+ colors.push_back(bone_color2);
+ }
+
+ Vector<Color> outline_colors;
+ if (CanvasItemEditor::get_singleton()->editor_selection->is_selected(this)) {
+ outline_colors.push_back(bone_selected_color);
+ outline_colors.push_back(bone_selected_color);
+ outline_colors.push_back(bone_selected_color);
+ outline_colors.push_back(bone_selected_color);
+ outline_colors.push_back(bone_selected_color);
+ outline_colors.push_back(bone_selected_color);
+ } else {
+ outline_colors.push_back(bone_outline_color);
+ outline_colors.push_back(bone_outline_color);
+ outline_colors.push_back(bone_outline_color);
+ outline_colors.push_back(bone_outline_color);
+ outline_colors.push_back(bone_outline_color);
+ outline_colors.push_back(bone_outline_color);
+ }
+
+ RenderingServer::get_singleton()->canvas_item_add_polygon(editor_gizmo_rid, bone_shape_outline, outline_colors);
+ RenderingServer::get_singleton()->canvas_item_add_polygon(editor_gizmo_rid, bone_shape, colors);
+ }
+
+ if (!Bone2D_found) {
+ Vector<Vector2> bone_shape;
+ Vector<Vector2> bone_shape_outline;
+
+ _editor_get_bone_shape(&bone_shape, &bone_shape_outline, nullptr);
+
+ Vector<Color> colors;
+ if (has_meta("_local_pose_override_enabled_")) {
+ colors.push_back(bone_ik_color);
+ colors.push_back(bone_ik_color);
+ colors.push_back(bone_ik_color);
+ colors.push_back(bone_ik_color);
+ } else {
+ colors.push_back(bone_color1);
+ colors.push_back(bone_color2);
+ colors.push_back(bone_color1);
+ colors.push_back(bone_color2);
+ }
+
+ Vector<Color> outline_colors;
+ if (CanvasItemEditor::get_singleton()->editor_selection->is_selected(this)) {
+ outline_colors.push_back(bone_selected_color);
+ outline_colors.push_back(bone_selected_color);
+ outline_colors.push_back(bone_selected_color);
+ outline_colors.push_back(bone_selected_color);
+ outline_colors.push_back(bone_selected_color);
+ outline_colors.push_back(bone_selected_color);
+ } else {
+ outline_colors.push_back(bone_outline_color);
+ outline_colors.push_back(bone_outline_color);
+ outline_colors.push_back(bone_outline_color);
+ outline_colors.push_back(bone_outline_color);
+ outline_colors.push_back(bone_outline_color);
+ outline_colors.push_back(bone_outline_color);
+ }
+
+ RenderingServer::get_singleton()->canvas_item_add_polygon(editor_gizmo_rid, bone_shape_outline, outline_colors);
+ RenderingServer::get_singleton()->canvas_item_add_polygon(editor_gizmo_rid, bone_shape, colors);
+ }
+ }
+#endif // TOOLS_ENALBED
}
+#ifdef TOOLS_ENABLED
+bool Bone2D::_editor_get_bone_shape(Vector<Vector2> *p_shape, Vector<Vector2> *p_outline_shape, Bone2D *p_other_bone) {
+ int bone_width = EditorSettings::get_singleton()->get("editors/2d/bone_width");
+ int bone_outline_width = EditorSettings::get_singleton()->get("editors/2d/bone_outline_size");
+
+ if (!is_inside_tree()) {
+ return false; //may have been removed
+ }
+ if (!p_other_bone && length <= 0) {
+ return false;
+ }
+
+ Vector2 rel;
+ if (p_other_bone) {
+ rel = (p_other_bone->get_global_transform().get_origin() - get_global_transform().get_origin());
+ rel = rel.rotated(-get_global_rotation()); // Undo Bone2D node's rotation so its drawn correctly regardless of the node's rotation
+ } else {
+ float angle_to_use = get_rotation() + bone_angle;
+ rel = Vector2(cos(angle_to_use), sin(angle_to_use)) * (length * MIN(get_global_scale().x, get_global_scale().y));
+ rel = rel.rotated(-get_rotation()); // Undo Bone2D node's rotation so its drawn correctly regardless of the node's rotation
+ }
+
+ Vector2 relt = rel.rotated(Math_PI * 0.5).normalized() * bone_width;
+ Vector2 reln = rel.normalized();
+ Vector2 reltn = relt.normalized();
+
+ if (p_shape) {
+ p_shape->clear();
+ p_shape->push_back(Vector2(0, 0));
+ p_shape->push_back(rel * 0.2 + relt);
+ p_shape->push_back(rel);
+ p_shape->push_back(rel * 0.2 - relt);
+ }
+
+ if (p_outline_shape) {
+ p_outline_shape->clear();
+ p_outline_shape->push_back((-reln - reltn) * bone_outline_width);
+ p_outline_shape->push_back((-reln + reltn) * bone_outline_width);
+ p_outline_shape->push_back(rel * 0.2 + relt + reltn * bone_outline_width);
+ p_outline_shape->push_back(rel + (reln + reltn) * bone_outline_width);
+ p_outline_shape->push_back(rel + (reln - reltn) * bone_outline_width);
+ p_outline_shape->push_back(rel * 0.2 - relt - reltn * bone_outline_width);
+ }
+ return true;
+}
+
+void Bone2D::_editor_set_show_bone_gizmo(bool p_show_gizmo) {
+ _editor_show_bone_gizmo = p_show_gizmo;
+ update();
+}
+
+bool Bone2D::_editor_get_show_bone_gizmo() const {
+ return _editor_show_bone_gizmo;
+}
+#endif // TOOLS_ENABLED
+
void Bone2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_rest", "rest"), &Bone2D::set_rest);
ClassDB::bind_method(D_METHOD("get_rest"), &Bone2D::get_rest);
@@ -90,8 +379,14 @@ void Bone2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_default_length", "default_length"), &Bone2D::set_default_length);
ClassDB::bind_method(D_METHOD("get_default_length"), &Bone2D::get_default_length);
+ ClassDB::bind_method(D_METHOD("set_autocalculate_length_and_angle", "auto_calculate"), &Bone2D::set_autocalculate_length_and_angle);
+ ClassDB::bind_method(D_METHOD("get_autocalculate_length_and_angle"), &Bone2D::get_autocalculate_length_and_angle);
+ ClassDB::bind_method(D_METHOD("set_length", "length"), &Bone2D::set_length);
+ ClassDB::bind_method(D_METHOD("get_length"), &Bone2D::get_length);
+ ClassDB::bind_method(D_METHOD("set_bone_angle", "angle"), &Bone2D::set_bone_angle);
+ ClassDB::bind_method(D_METHOD("get_bone_angle"), &Bone2D::get_bone_angle);
+
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "rest"), "set_rest", "get_rest");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "default_length", PROPERTY_HINT_RANGE, "1,1024,1"), "set_default_length", "get_default_length");
}
void Bone2D::set_rest(const Transform2D &p_rest) {
@@ -119,12 +414,14 @@ void Bone2D::apply_rest() {
set_transform(rest);
}
-void Bone2D::set_default_length(real_t p_length) {
- default_length = p_length;
+void Bone2D::set_default_length(float p_length) {
+ WARN_DEPRECATED_MSG("set_default_length is deprecated. Please use set_length instead!");
+ set_length(p_length);
}
-real_t Bone2D::get_default_length() const {
- return default_length;
+float Bone2D::get_default_length() const {
+ WARN_DEPRECATED_MSG("get_default_length is deprecated. Please use get_length instead!");
+ return get_length();
}
int Bone2D::get_index_in_skeleton() const {
@@ -150,16 +447,121 @@ TypedArray<String> Bone2D::get_configuration_warnings() const {
return warnings;
}
+void Bone2D::calculate_length_and_rotation() {
+ // if there is at least a single child Bone2D node, we can calculate
+ // the length and direction. We will always just use the first Bone2D for this.
+ bool calculated = false;
+ int child_count = get_child_count();
+ if (child_count > 0) {
+ for (int i = 0; i < child_count; i++) {
+ Bone2D *child = Object::cast_to<Bone2D>(get_child(i));
+ if (child) {
+ Vector2 child_local_pos = to_local(child->get_global_transform().get_origin());
+ length = child_local_pos.length();
+ bone_angle = Math::atan2(child_local_pos.normalized().y, child_local_pos.normalized().x);
+ calculated = true;
+ break;
+ }
+ }
+ }
+ if (calculated) {
+ return; // Finished!
+ } else {
+ WARN_PRINT("No Bone2D children of node " + get_name() + ". Cannot calculate bone length or angle reliably.\nUsing transform rotation for bone angle");
+ bone_angle = get_transform().get_rotation();
+ return;
+ }
+}
+
+void Bone2D::set_autocalculate_length_and_angle(bool p_autocalculate) {
+ autocalculate_length_and_angle = p_autocalculate;
+ if (autocalculate_length_and_angle) {
+ calculate_length_and_rotation();
+ }
+ notify_property_list_changed();
+}
+
+bool Bone2D::get_autocalculate_length_and_angle() const {
+ return autocalculate_length_and_angle;
+}
+
+void Bone2D::set_length(float p_length) {
+ length = p_length;
+
+#ifdef TOOLS_ENABLED
+ update();
+#endif // TOOLS_ENABLED
+}
+
+float Bone2D::get_length() const {
+ return length;
+}
+
+void Bone2D::set_bone_angle(float p_angle) {
+ bone_angle = p_angle;
+
+#ifdef TOOLS_ENABLED
+ update();
+#endif // TOOLS_ENABLED
+}
+
+float Bone2D::get_bone_angle() const {
+ return bone_angle;
+}
+
Bone2D::Bone2D() {
+ skeleton = nullptr;
+ parent_bone = nullptr;
+ skeleton_index = -1;
+ length = 16;
+ bone_angle = 0;
+ autocalculate_length_and_angle = true;
set_notify_local_transform(true);
//this is a clever hack so the bone knows no rest has been set yet, allowing to show an error.
for (int i = 0; i < 3; i++) {
rest[i] = Vector2(0, 0);
}
+ copy_transform_to_cache = true;
+}
+
+Bone2D::~Bone2D() {
+#ifdef TOOLS_ENABLED
+ if (!editor_gizmo_rid.is_null()) {
+ RenderingServer::get_singleton()->free(editor_gizmo_rid);
+ }
+#endif // TOOLS_ENABLED
}
//////////////////////////////////////
+bool Skeleton2D::_set(const StringName &p_path, const Variant &p_value) {
+ String path = p_path;
+
+ if (path.begins_with("modification_stack")) {
+ set_modification_stack(p_value);
+ return true;
+ }
+ return true;
+}
+
+bool Skeleton2D::_get(const StringName &p_path, Variant &r_ret) const {
+ String path = p_path;
+
+ if (path.begins_with("modification_stack")) {
+ r_ret = get_modification_stack();
+ return true;
+ }
+ return true;
+}
+
+void Skeleton2D::_get_property_list(List<PropertyInfo> *p_list) const {
+ p_list->push_back(
+ PropertyInfo(Variant::OBJECT, "modification_stack",
+ PROPERTY_HINT_RESOURCE_TYPE,
+ "SkeletonModificationStack2D",
+ PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE));
+}
+
void Skeleton2D::_make_bone_setup_dirty() {
if (bone_setup_dirty) {
return;
@@ -189,6 +591,8 @@ void Skeleton2D::_update_bone_setup() {
} else {
bones.write[i].parent_index = -1;
}
+
+ bones.write[i].local_pose_override = bones[i].bone->get_skeleton_rest();
}
transform_dirty = true;
@@ -257,19 +661,121 @@ void Skeleton2D::_notification(int p_what) {
if (transform_dirty) {
_update_transform();
}
-
request_ready();
}
if (p_what == NOTIFICATION_TRANSFORM_CHANGED) {
RS::get_singleton()->skeleton_set_base_transform_2d(skeleton, get_global_transform());
+ } else if (p_what == NOTIFICATION_INTERNAL_PROCESS) {
+ if (modification_stack.is_valid()) {
+ execute_modifications(get_process_delta_time(), SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_process);
+ }
+ } else if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
+ if (modification_stack.is_valid()) {
+ execute_modifications(get_physics_process_delta_time(), SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_physics_process);
+ }
}
+#ifdef TOOLS_ENABLED
+ else if (p_what == NOTIFICATION_DRAW) {
+ if (Engine::get_singleton()->is_editor_hint()) {
+ if (modification_stack.is_valid()) {
+ modification_stack->draw_editor_gizmos();
+ }
+ }
+ }
+#endif // TOOLS_ENABLED
}
RID Skeleton2D::get_skeleton() const {
return skeleton;
}
+void Skeleton2D::set_bone_local_pose_override(int p_bone_idx, Transform2D p_override, float p_amount, bool p_persistent) {
+ ERR_FAIL_INDEX_MSG(p_bone_idx, bones.size(), "Bone index is out of range!");
+ bones.write[p_bone_idx].local_pose_override = p_override;
+ bones.write[p_bone_idx].local_pose_override_amount = p_amount;
+ bones.write[p_bone_idx].local_pose_override_persistent = p_persistent;
+}
+
+Transform2D Skeleton2D::get_bone_local_pose_override(int p_bone_idx) {
+ ERR_FAIL_INDEX_V_MSG(p_bone_idx, bones.size(), Transform2D(), "Bone index is out of range!");
+ return bones[p_bone_idx].local_pose_override;
+}
+
+void Skeleton2D::set_modification_stack(Ref<SkeletonModificationStack2D> p_stack) {
+ if (modification_stack.is_valid()) {
+ modification_stack->is_setup = false;
+ modification_stack->set_skeleton(nullptr);
+
+ set_process_internal(false);
+ set_physics_process_internal(false);
+ }
+ modification_stack = p_stack;
+ if (modification_stack.is_valid()) {
+ modification_stack->set_skeleton(this);
+ modification_stack->setup();
+
+ set_process_internal(true);
+ set_physics_process_internal(true);
+
+#ifdef TOOLS_ENABLED
+ modification_stack->set_editor_gizmos_dirty(true);
+#endif // TOOLS_ENABLED
+ }
+}
+
+Ref<SkeletonModificationStack2D> Skeleton2D::get_modification_stack() const {
+ return modification_stack;
+}
+
+void Skeleton2D::execute_modifications(float p_delta, int p_execution_mode) {
+ if (!modification_stack.is_valid()) {
+ return;
+ }
+
+ // Do not cache the transform changes caused by the modifications!
+ for (int i = 0; i < bones.size(); i++) {
+ bones[i].bone->copy_transform_to_cache = false;
+ }
+
+ if (modification_stack->skeleton != this) {
+ modification_stack->set_skeleton(this);
+ }
+
+ modification_stack->execute(p_delta, p_execution_mode);
+
+ // Only apply the local pose override on _process. Otherwise, just calculate the local_pose_override and reset the transform.
+ if (p_execution_mode == SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_process) {
+ for (int i = 0; i < bones.size(); i++) {
+ if (bones[i].local_pose_override_amount > 0) {
+ bones[i].bone->set_meta("_local_pose_override_enabled_", true);
+
+ Transform2D final_trans = bones[i].bone->cache_transform;
+ final_trans = final_trans.interpolate_with(bones[i].local_pose_override, bones[i].local_pose_override_amount);
+ bones[i].bone->set_transform(final_trans);
+ bones[i].bone->propagate_call("force_update_transform");
+
+ if (bones[i].local_pose_override_persistent) {
+ bones.write[i].local_pose_override_amount = 0.0;
+ }
+ } else {
+ // TODO: see if there is a way to undo the override without having to resort to setting every bone's transform.
+ bones[i].bone->remove_meta("_local_pose_override_enabled_");
+ bones[i].bone->set_transform(bones[i].bone->cache_transform);
+ }
+ }
+ }
+
+ // Cache any future transform changes
+ for (int i = 0; i < bones.size(); i++) {
+ bones[i].bone->copy_transform_to_cache = true;
+ }
+
+#ifdef TOOLS_ENABLED
+ modification_stack->set_editor_gizmos_dirty(true);
+#endif // TOOLS_ENABLED
+}
+
void Skeleton2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("_update_bone_setup"), &Skeleton2D::_update_bone_setup);
ClassDB::bind_method(D_METHOD("_update_transform"), &Skeleton2D::_update_transform);
@@ -279,6 +785,13 @@ void Skeleton2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_skeleton"), &Skeleton2D::get_skeleton);
+ ClassDB::bind_method(D_METHOD("set_modification_stack", "modification_stack"), &Skeleton2D::set_modification_stack);
+ ClassDB::bind_method(D_METHOD("get_modification_stack"), &Skeleton2D::get_modification_stack);
+ ClassDB::bind_method(D_METHOD("execute_modifications", "execution_mode", "execution_mode"), &Skeleton2D::execute_modifications);
+
+ ClassDB::bind_method(D_METHOD("set_bone_local_pose_override", "bone_idx", "override_pose", "strength", "persistent"), &Skeleton2D::set_bone_local_pose_override);
+ ClassDB::bind_method(D_METHOD("get_bone_local_pose_override", "bone_idx"), &Skeleton2D::get_bone_local_pose_override);
+
ADD_SIGNAL(MethodInfo("bone_setup_changed"));
}
diff --git a/scene/2d/skeleton_2d.h b/scene/2d/skeleton_2d.h
index fd62b87bde..59bd711960 100644
--- a/scene/2d/skeleton_2d.h
+++ b/scene/2d/skeleton_2d.h
@@ -32,6 +32,7 @@
#define SKELETON_2D_H
#include "scene/2d/node_2d.h"
+#include "scene/resources/skeleton_modification_2d.h"
class Skeleton2D;
@@ -46,15 +47,32 @@ class Bone2D : public Node2D {
Bone2D *parent_bone = nullptr;
Skeleton2D *skeleton = nullptr;
Transform2D rest;
- real_t default_length = 16.0;
+
+ bool autocalculate_length_and_angle = true;
+ float length = 16;
+ float bone_angle = 0;
int skeleton_index = -1;
+ void calculate_length_and_rotation();
+
+#ifdef TOOLS_ENABLED
+ RID editor_gizmo_rid;
+ bool _editor_get_bone_shape(Vector<Vector2> *p_shape, Vector<Vector2> *p_outline_shape, Bone2D *p_other_bone);
+ bool _editor_show_bone_gizmo = true;
+#endif // TOOLS ENABLED
+
protected:
void _notification(int p_what);
static void _bind_methods();
+ bool _set(const StringName &p_path, const Variant &p_value);
+ bool _get(const StringName &p_path, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
public:
+ Transform2D cache_transform;
+ bool copy_transform_to_cache = true;
+
void set_rest(const Transform2D &p_rest);
Transform2D get_rest() const;
void apply_rest();
@@ -65,11 +83,26 @@ public:
void set_default_length(real_t p_length);
real_t get_default_length() const;
+ void set_autocalculate_length_and_angle(bool p_autocalculate);
+ bool get_autocalculate_length_and_angle() const;
+ void set_length(float p_length);
+ float get_length() const;
+ void set_bone_angle(float p_angle);
+ float get_bone_angle() const;
+
int get_index_in_skeleton() const;
+#ifdef TOOLS_ENABLED
+ void _editor_set_show_bone_gizmo(bool p_show_gizmo);
+ bool _editor_get_show_bone_gizmo() const;
+#endif // TOOLS_ENABLED
+
Bone2D();
+ ~Bone2D();
};
+class SkeletonModificationStack2D;
+
class Skeleton2D : public Node2D {
GDCLASS(Skeleton2D, Node2D);
@@ -86,6 +119,11 @@ class Skeleton2D : public Node2D {
int parent_index = 0;
Transform2D accum_transform;
Transform2D rest_inverse;
+
+ //Transform2D local_pose_cache;
+ Transform2D local_pose_override;
+ float local_pose_override_amount = 0;
+ bool local_pose_override_persistent = false;
};
Vector<Bone> bones;
@@ -100,15 +138,28 @@ class Skeleton2D : public Node2D {
RID skeleton;
+ Ref<SkeletonModificationStack2D> modification_stack;
+
protected:
void _notification(int p_what);
static void _bind_methods();
+ bool _set(const StringName &p_path, const Variant &p_value);
+ bool _get(const StringName &p_path, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
public:
int get_bone_count() const;
Bone2D *get_bone(int p_idx);
RID get_skeleton() const;
+
+ void set_bone_local_pose_override(int p_bone_idx, Transform2D p_override, float p_amount, bool p_persistent = true);
+ Transform2D get_bone_local_pose_override(int p_bone_idx);
+
+ Ref<SkeletonModificationStack2D> get_modification_stack() const;
+ void set_modification_stack(Ref<SkeletonModificationStack2D> p_stack);
+ void execute_modifications(float p_delta, int p_execution_mode);
+
Skeleton2D();
~Skeleton2D();
};
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index 24b907fe6c..e79dfb019c 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -334,6 +334,12 @@ TileMap::VisibilityMode TileMap::get_navigation_visibility_mode() {
return show_navigation;
}
+void TileMap::set_y_sort_enabled(bool p_enable) {
+ Node2D::set_y_sort_enabled(p_enable);
+ _recreate_quadrants();
+ emit_signal("changed");
+}
+
void TileMap::update_dirty_quadrants() {
if (!pending_update) {
return;
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index f02455a84b..3001e6b471 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -279,6 +279,7 @@ public:
int get_effective_quadrant_size() const;
void update_dirty_quadrants();
+ virtual void set_y_sort_enabled(bool p_enable) override;
Vector2 map_to_world(const Vector2i &p_pos) const;
Vector2i world_to_map(const Vector2 &p_pos) const;
diff --git a/scene/2d/visibility_notifier_2d.cpp b/scene/2d/visibility_notifier_2d.cpp
index 8feb47f1cc..c85b2c85a4 100644
--- a/scene/2d/visibility_notifier_2d.cpp
+++ b/scene/2d/visibility_notifier_2d.cpp
@@ -176,7 +176,7 @@ void VisibilityEnabler2D::_find_nodes(Node *p_node) {
{
RigidBody2D *rb2d = Object::cast_to<RigidBody2D>(p_node);
- if (rb2d && ((rb2d->get_mode() == RigidBody2D::MODE_CHARACTER || rb2d->get_mode() == RigidBody2D::MODE_RIGID))) {
+ if (rb2d && ((rb2d->get_mode() == RigidBody2D::MODE_DYNAMIC || rb2d->get_mode() == RigidBody2D::MODE_DYNAMIC_LOCKED))) {
add = true;
meta = rb2d->get_mode();
}
diff --git a/scene/2d/y_sort.cpp b/scene/2d/y_sort.cpp
deleted file mode 100644
index 7e7bc27cc2..0000000000
--- a/scene/2d/y_sort.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-/*************************************************************************/
-/* y_sort.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#include "y_sort.h"
-
-void YSort::set_sort_enabled(bool p_enabled) {
- sort_enabled = p_enabled;
- RS::get_singleton()->canvas_item_set_sort_children_by_y(get_canvas_item(), sort_enabled);
-}
-
-bool YSort::is_sort_enabled() const {
- return sort_enabled;
-}
-
-void YSort::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_sort_enabled", "enabled"), &YSort::set_sort_enabled);
- ClassDB::bind_method(D_METHOD("is_sort_enabled"), &YSort::is_sort_enabled);
-
- ADD_GROUP("Sort", "sort_");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sort_enabled"), "set_sort_enabled", "is_sort_enabled");
-}
-
-YSort::YSort() {
- RS::get_singleton()->canvas_item_set_sort_children_by_y(get_canvas_item(), true);
-}