/**************************************************************************/ /* physical_bone_2d.cpp */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /**************************************************************************/ /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ /* */ /* 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" #include "scene/2d/joint_2d.h" void PhysicalBone2D::_notification(int p_what) { switch (p_what) { case 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()); } } break; case 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); } break; } } void PhysicalBone2D::_position_at_bone2d() { // Reset to Bone2D position if (parent_skeleton) { Bone2D *bone_to_use = parent_skeleton->get_bone(bone2d_index); ERR_FAIL_NULL_MSG(bone_to_use, "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(current_parent); if (potential_skeleton) { parent_skeleton = potential_skeleton; break; } else { PhysicalBone2D *potential_parent_bone = Object::cast_to(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(child_node); if (potential_joint) { child_joint = potential_joint; break; } } } PackedStringArray PhysicalBone2D::get_configuration_warnings() const { PackedStringArray warnings = Node::get_configuration_warnings(); if (!parent_skeleton) { warnings.push_back(RTR("A PhysicalBone2D only works with a Skeleton2D or another PhysicalBone2D as a parent node!")); } if (parent_skeleton && bone2d_index <= -1) { warnings.push_back(RTR("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(get_parent()); if (parent_bone) { warnings.push_back(RTR("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(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()); PhysicsServer2D::get_singleton()->body_set_collision_priority(get_rid(), get_collision_priority()); // Apply the correct mode. _apply_body_mode(); _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_collision_priority(get_rid(), 1.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() { }