- Hurtboxes now create their own collision shape nodes automatically.

- Character3D prevent crashes on _ready by checking for a valid skeleton.
- Fixed deadzone reading as cleared for all axes when only one has done so.
- Singletons once again deregistered, since it seems to now cause less crashes.
- PlayerSpawn now uses the new error-printing macro.
This commit is contained in:
2026-09-13 00:02:40 -04:00
parent ef761713b7
commit 36c7f87dc0
5 changed files with 74 additions and 26 deletions
@@ -20,15 +20,24 @@ using namespace godot;
void Character3D::_ready() void Character3D::_ready()
{ {
TypedArray<Node> child_mesh_candidates = this->skeleton->get_children(); if (this->skeleton)
const uint32_t num_children = child_mesh_candidates.size();
for (uint32_t i = 0; i < num_children; i++)
{ {
if (MeshInstance3D *mesh = cast_to<MeshInstance3D>(child_mesh_candidates[i])) TypedArray<Node> child_mesh_candidates = this->skeleton->get_children();
const uint32_t num_children = child_mesh_candidates.size();
for (uint32_t i = 0; i < num_children; i++)
{ {
this->meshes.emplace_back(mesh); if (MeshInstance3D *mesh = cast_to<MeshInstance3D>(child_mesh_candidates[i]))
{
this->meshes.emplace_back(mesh);
}
} }
} }
#ifdef DEBUG_ENABLED
else
{
PRINT_ERROR(CHARACTER3D_TAG, "No skeleton has been set, and there is currently no way to seek one out in the hierarchy. This will be a problem.");
}
#endif // DEBUG_ENABLED
} }
@@ -55,16 +64,19 @@ void Character3D::take_damage(const Dictionary &hit_data)
void Character3D::_activate_ragdoll() void Character3D::_activate_ragdoll()
{ {
const TypedArray<Node> &physical_bones = this->physical_bone_simulator->get_children(); if (this->physical_bone_simulator)
const uint32_t num_physical_bones = physical_bones.size();
for (uint32_t i = 0; i < num_physical_bones; i++)
{ {
if (PhysicalBone3D *bone = cast_to<PhysicalBone3D>(physical_bones[i])) const TypedArray<Node> &physical_bones = this->physical_bone_simulator->get_children();
const uint32_t num_physical_bones = physical_bones.size();
for (uint32_t i = 0; i < num_physical_bones; i++)
{ {
bone->set_collision_layer(0); if (PhysicalBone3D *bone = cast_to<PhysicalBone3D>(physical_bones[i]))
{
bone->set_collision_layer(0);
}
} }
this->physical_bone_simulator->physical_bones_start_simulation();
} }
this->physical_bone_simulator->physical_bones_start_simulation();
} }
+32 -6
View File
@@ -10,6 +10,7 @@
#include "character_3d.h" #include "character_3d.h"
#include <godot_cpp/classes/engine.hpp>
#include <godot_cpp/classes/physical_bone3d.hpp> #include <godot_cpp/classes/physical_bone3d.hpp>
#include <godot_cpp/classes/scene_tree.hpp> #include <godot_cpp/classes/scene_tree.hpp>
#include <godot_cpp/classes/scene_tree_timer.hpp> #include <godot_cpp/classes/scene_tree_timer.hpp>
@@ -17,8 +18,35 @@
using namespace godot; using namespace godot;
void HurtboxBase::_enter_tree()
{
if (!this->collision_shape)
{
this->collision_shape = memnew(CollisionShape3D);
this->collision_shape->set_shape(this->shape);
this->add_child(this->collision_shape);
}
Area3D::_enter_tree();
}
void HurtboxBase::_exit_tree()
{
if (this->collision_shape)
{
this->collision_shape->set_shape(this->shape);
this->remove_child(this->collision_shape);
this->collision_shape->queue_free();
}
Area3D::_exit_tree();
}
void HurtboxBase::_ready() void HurtboxBase::_ready()
{ {
#ifdef DEBUG_ENABLED
if(Engine::get_singleton()->is_editor_hint()) { this->set_physics_process(false); return; }
#endif // DEBUG_ENABLED
this->set_process(false); this->set_process(false);
this->remaining_life = this->lifetime; this->remaining_life = this->lifetime;
@@ -27,11 +55,6 @@ void HurtboxBase::_ready()
void HurtboxBase::_physics_process(double delta) void HurtboxBase::_physics_process(double delta)
{ {
if (this->remaining_life <= 0.0f)
{
this->collision_shape->set_disabled(true);
}
TypedArray<Character3D> characters_hit_this_frame; TypedArray<Character3D> characters_hit_this_frame;
const TypedArray<Node3D> &bodies = this->get_overlapping_bodies(); const TypedArray<Node3D> &bodies = this->get_overlapping_bodies();
@@ -73,7 +96,10 @@ void HurtboxBase::_physics_process(double delta)
this->hit_character_bodies.append_array(characters_hit_this_frame); this->hit_character_bodies.append_array(characters_hit_this_frame);
this->remaining_life -= delta; if (this->remaining_life <= 0.0f)
this->collision_shape->set_disabled(true);
else
this->remaining_life -= delta;
} }
void HurtboxBase::_send_next_in_queue() void HurtboxBase::_send_next_in_queue()
+7 -3
View File
@@ -12,6 +12,7 @@
#include "character_3d.h" #include "character_3d.h"
#include <godot_cpp/classes/collision_shape3d.hpp> #include <godot_cpp/classes/collision_shape3d.hpp>
#include <godot_cpp/classes/shape3d.hpp>
#include <godot_cpp/classes/area3d.hpp> #include <godot_cpp/classes/area3d.hpp>
using namespace godot; using namespace godot;
@@ -23,11 +24,13 @@ class HurtboxBase : public Area3D
GDCLASS(HurtboxBase, Area3D); GDCLASS(HurtboxBase, Area3D);
public: public:
virtual void _enter_tree() override;
virtual void _exit_tree() override;
virtual void _ready() override; virtual void _ready() override;
virtual void _physics_process(double delta) override; virtual void _physics_process(double delta) override;
void set_collision_shape(CollisionShape3D *cs) { this->collision_shape = cs; } void set_shape(Ref<Shape3D> s) { this->shape = s; }
CollisionShape3D *get_collision_shape() const { return this->collision_shape; } Ref<Shape3D> get_shape() const { return this->shape; }
void set_lifetime(const float lt) { this->lifetime = lt; } void set_lifetime(const float lt) { this->lifetime = lt; }
float get_lifetime() const { return this->lifetime; } float get_lifetime() const { return this->lifetime; }
@@ -42,6 +45,7 @@ private:
Dictionary _create_hit_data_struct() const; Dictionary _create_hit_data_struct() const;
void _send_next_in_queue(); void _send_next_in_queue();
Ref<Shape3D> shape = nullptr;
CollisionShape3D *collision_shape = nullptr; CollisionShape3D *collision_shape = nullptr;
float lifetime = 0.05f; float lifetime = 0.05f;
@@ -55,7 +59,7 @@ private:
protected: protected:
static void _bind_methods() static void _bind_methods()
{ {
ADD_GETTER_SETTER_HINTED(HurtboxBase, collision_shape, Variant::OBJECT, PROPERTY_HINT_NODE_TYPE, "CollisionShape3D"); ADD_GETTER_SETTER_HINTED(HurtboxBase, shape, Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "Shape3D");
ADD_GETTER_SETTER(HurtboxBase, lifetime, Variant::FLOAT); ADD_GETTER_SETTER(HurtboxBase, lifetime, Variant::FLOAT);
ADD_GETTER_SETTER(HurtboxBase, hit_points, Variant::INT); ADD_GETTER_SETTER(HurtboxBase, hit_points, Variant::INT);
+3 -3
View File
@@ -88,9 +88,9 @@ void uninitialize_orng_module(ModuleInitializationLevel p_level) {
if (p_level == ModuleInitializationLevel::MODULE_INITIALIZATION_LEVEL_SCENE) if (p_level == ModuleInitializationLevel::MODULE_INITIALIZATION_LEVEL_SCENE)
{ {
// GDSINGLETON_UNREGISTER_CLASS(_vendor_service_singleton); // GDSINGLETON_UNREGISTER_CLASS(_vendor_service_singleton);
// GDSINGLETON_UNREGISTER_CLASS(_scene_loader_singleton); GDSINGLETON_UNREGISTER_CLASS(_scene_loader_singleton);
// GDSINGLETON_UNREGISTER_CLASS(_save_manager_singleton); GDSINGLETON_UNREGISTER_CLASS(_save_manager_singleton);
// GDSINGLETON_UNREGISTER_CLASS(_input_handler_singleton); GDSINGLETON_UNREGISTER_CLASS(_input_handler_singleton);
} }
} }
+9 -3
View File
@@ -55,7 +55,8 @@ void InputResource::update_axes(const float delta)
const StringName &signal_1d = prefix + INPUTAXIS_ONE_DIMENSIONAL_SUFFIX + INPUTHANDLER_SUFFIX_SIGNAL; const StringName &signal_1d = prefix + INPUTAXIS_ONE_DIMENSIONAL_SUFFIX + INPUTHANDLER_SUFFIX_SIGNAL;
if (this->has_signal(signal_1d)) if (this->has_signal(signal_1d))
{ {
const float x_axis = axis->get_x_axis(); float x_axis = axis->get_x_axis();
if (UtilityFunctions::absf(x_axis) < axis_deadzone) x_axis = 0.0f;
if (current_input.x != x_axis || current_input.length() > SMALL_NUMBER) if (current_input.x != x_axis || current_input.length() > SMALL_NUMBER)
this->emit_signal(signal_1d, delta, x_axis); this->emit_signal(signal_1d, delta, x_axis);
current_input.x = x_axis; current_input.x = x_axis;
@@ -64,7 +65,9 @@ void InputResource::update_axes(const float delta)
const StringName &signal_2d = prefix + INPUTAXIS_TWO_DIMENSIONAL_SUFFIX + INPUTHANDLER_SUFFIX_SIGNAL; const StringName &signal_2d = prefix + INPUTAXIS_TWO_DIMENSIONAL_SUFFIX + INPUTHANDLER_SUFFIX_SIGNAL;
if (this->has_signal(signal_2d)) if (this->has_signal(signal_2d))
{ {
const Vector2 &vector = axis->get_lateral_vector(); Vector2 &vector = axis->get_lateral_vector();
if (UtilityFunctions::absf(vector.x) < axis_deadzone) vector.x = 0.0f;
if (UtilityFunctions::absf(vector.y) < axis_deadzone) vector.y = 0.0f;
if ((current_input.x != vector.x || current_input.z != vector.y) || current_input.length() > SMALL_NUMBER) if ((current_input.x != vector.x || current_input.z != vector.y) || current_input.length() > SMALL_NUMBER)
this->emit_signal(signal_2d, delta, vector); this->emit_signal(signal_2d, delta, vector);
current_input.x = vector.x; current_input.z = vector.y; current_input.x = vector.x; current_input.z = vector.y;
@@ -73,7 +76,10 @@ void InputResource::update_axes(const float delta)
const StringName &signal_3d = prefix + INPUTAXIS_THREE_DIMENSIONAL_SUFFIX + INPUTHANDLER_SUFFIX_SIGNAL; const StringName &signal_3d = prefix + INPUTAXIS_THREE_DIMENSIONAL_SUFFIX + INPUTHANDLER_SUFFIX_SIGNAL;
if (this->has_signal(signal_3d)) if (this->has_signal(signal_3d))
{ {
const Vector3 &vector = axis->get_spherical_vector(); Vector3 &vector = axis->get_spherical_vector();
if (UtilityFunctions::absf(vector.x) < axis_deadzone) vector.x = 0.0f;
if (UtilityFunctions::absf(vector.y) < axis_deadzone) vector.y = 0.0f;
if (UtilityFunctions::absf(vector.z) < axis_deadzone) vector.z = 0.0f;
if (current_input != vector || current_input.length() > SMALL_NUMBER) if (current_input != vector || current_input.length() > SMALL_NUMBER)
this->emit_signal(signal_3d, delta, vector); this->emit_signal(signal_3d, delta, vector);
current_input = vector; current_input = vector;