From 7cfa9ae539fda47c6e90fcc8dfc722e3818edd4b Mon Sep 17 00:00:00 2001 From: FireForge <67974470+fire-forge@users.noreply.github.com> Date: Sun, 31 Jul 2022 15:08:16 -0500 Subject: Improve ShapeCast2D editor and debug drawing - Rename RayCast2DEditorPlugin to Cast2DEditorPlugin and make it also support editing ShapeCast2D. - Apply RayCast2D debug drawing improvements from #46675 to ShapeCast2D. --- editor/editor_node.cpp | 4 +- editor/plugins/cast_2d_editor_plugin.cpp | 155 +++++++++++++++++++++++++++ editor/plugins/cast_2d_editor_plugin.h | 79 ++++++++++++++ editor/plugins/ray_cast_2d_editor_plugin.cpp | 151 -------------------------- editor/plugins/ray_cast_2d_editor_plugin.h | 79 -------------- scene/2d/shape_cast_2d.cpp | 31 ++++-- 6 files changed, 258 insertions(+), 241 deletions(-) create mode 100644 editor/plugins/cast_2d_editor_plugin.cpp create mode 100644 editor/plugins/cast_2d_editor_plugin.h delete mode 100644 editor/plugins/ray_cast_2d_editor_plugin.cpp delete mode 100644 editor/plugins/ray_cast_2d_editor_plugin.h diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 8b540f9787..2282274b65 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -140,6 +140,7 @@ #include "editor/plugins/bone_map_editor_plugin.h" #include "editor/plugins/camera_3d_editor_plugin.h" #include "editor/plugins/canvas_item_editor_plugin.h" +#include "editor/plugins/cast_2d_editor_plugin.h" #include "editor/plugins/collision_polygon_2d_editor_plugin.h" #include "editor/plugins/collision_shape_2d_editor_plugin.h" #include "editor/plugins/control_editor_plugin.h" @@ -174,7 +175,6 @@ #include "editor/plugins/physical_bone_3d_editor_plugin.h" #include "editor/plugins/polygon_2d_editor_plugin.h" #include "editor/plugins/polygon_3d_editor_plugin.h" -#include "editor/plugins/ray_cast_2d_editor_plugin.h" #include "editor/plugins/resource_preloader_editor_plugin.h" #include "editor/plugins/root_motion_editor_plugin.h" #include "editor/plugins/script_editor_plugin.h" @@ -7190,7 +7190,7 @@ EditorNode::EditorNode() { add_editor_plugin(memnew(NavigationPolygonEditorPlugin)); add_editor_plugin(memnew(Path2DEditorPlugin)); add_editor_plugin(memnew(Polygon2DEditorPlugin)); - add_editor_plugin(memnew(RayCast2DEditorPlugin)); + add_editor_plugin(memnew(Cast2DEditorPlugin)); add_editor_plugin(memnew(Skeleton2DEditorPlugin)); add_editor_plugin(memnew(Sprite2DEditorPlugin)); add_editor_plugin(memnew(TilesEditorPlugin)); diff --git a/editor/plugins/cast_2d_editor_plugin.cpp b/editor/plugins/cast_2d_editor_plugin.cpp new file mode 100644 index 0000000000..18c38e7ab8 --- /dev/null +++ b/editor/plugins/cast_2d_editor_plugin.cpp @@ -0,0 +1,155 @@ +/*************************************************************************/ +/* cast_2d_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 "cast_2d_editor_plugin.h" + +#include "canvas_item_editor_plugin.h" +#include "editor/editor_node.h" +#include "scene/2d/ray_cast_2d.h" +#include "scene/2d/shape_cast_2d.h" + +void Cast2DEditor::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + get_tree()->connect("node_removed", callable_mp(this, &Cast2DEditor::_node_removed)); + } break; + + case NOTIFICATION_EXIT_TREE: { + get_tree()->disconnect("node_removed", callable_mp(this, &Cast2DEditor::_node_removed)); + } break; + } +} + +void Cast2DEditor::_node_removed(Node *p_node) { + if (p_node == node) { + node = nullptr; + } +} + +bool Cast2DEditor::forward_canvas_gui_input(const Ref &p_event) { + if (!node || !node->is_visible_in_tree()) { + return false; + } + + Transform2D xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform(); + + Ref mb = p_event; + if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT) { + Vector2 target_position = node->get("target_position"); + + if (mb->is_pressed()) { + if (xform.xform(target_position).distance_to(mb->get_position()) < 8) { + pressed = true; + original_target_position = target_position; + + return true; + } else { + pressed = false; + + return false; + } + } else if (pressed) { + undo_redo->create_action(TTR("Set target_position")); + undo_redo->add_do_property(node, "target_position", target_position); + undo_redo->add_do_method(canvas_item_editor, "update_viewport"); + undo_redo->add_undo_property(node, "target_position", original_target_position); + undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); + undo_redo->commit_action(); + + pressed = false; + + return true; + } + } + + Ref mm = p_event; + if (mm.is_valid() && pressed) { + Vector2 point = canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(mm->get_position())); + point = node->get_global_transform().affine_inverse().xform(point); + + node->set("target_position", point); + canvas_item_editor->update_viewport(); + node->notify_property_list_changed(); + + return true; + } + + return false; +} + +void Cast2DEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { + if (!node || !node->is_visible_in_tree()) { + return; + } + + Transform2D gt = canvas_item_editor->get_canvas_transform() * node->get_global_transform(); + + const Ref handle = get_theme_icon(SNAME("EditorHandle"), SNAME("EditorIcons")); + p_overlay->draw_texture(handle, gt.xform((Vector2)node->get("target_position")) - handle->get_size() / 2); +} + +void Cast2DEditor::edit(Node2D *p_node) { + if (!canvas_item_editor) { + canvas_item_editor = CanvasItemEditor::get_singleton(); + } + + if (Object::cast_to(p_node) || Object::cast_to(p_node)) { + node = p_node; + } else { + node = nullptr; + } + + canvas_item_editor->update_viewport(); +} + +Cast2DEditor::Cast2DEditor() { + undo_redo = EditorNode::get_singleton()->get_undo_redo(); +} + +/////////////////////// + +void Cast2DEditorPlugin::edit(Object *p_object) { + cast_2d_editor->edit(Object::cast_to(p_object)); +} + +bool Cast2DEditorPlugin::handles(Object *p_object) const { + return Object::cast_to(p_object) != nullptr || Object::cast_to(p_object) != nullptr; +} + +void Cast2DEditorPlugin::make_visible(bool p_visible) { + if (!p_visible) { + edit(nullptr); + } +} + +Cast2DEditorPlugin::Cast2DEditorPlugin() { + cast_2d_editor = memnew(Cast2DEditor); + EditorNode::get_singleton()->get_gui_base()->add_child(cast_2d_editor); +} diff --git a/editor/plugins/cast_2d_editor_plugin.h b/editor/plugins/cast_2d_editor_plugin.h new file mode 100644 index 0000000000..d9c0cc4a06 --- /dev/null +++ b/editor/plugins/cast_2d_editor_plugin.h @@ -0,0 +1,79 @@ +/*************************************************************************/ +/* cast_2d_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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. */ +/*************************************************************************/ + +#ifndef CAST_2D_EDITOR_PLUGIN_H +#define CAST_2D_EDITOR_PLUGIN_H + +#include "editor/editor_plugin.h" +#include "scene/2d/node_2d.h" + +class CanvasItemEditor; + +class Cast2DEditor : public Control { + GDCLASS(Cast2DEditor, Control); + + UndoRedo *undo_redo = nullptr; + CanvasItemEditor *canvas_item_editor = nullptr; + Node2D *node; + + bool pressed = false; + Point2 original_target_position; + +protected: + void _notification(int p_what); + void _node_removed(Node *p_node); + +public: + bool forward_canvas_gui_input(const Ref &p_event); + void forward_canvas_draw_over_viewport(Control *p_overlay); + void edit(Node2D *p_node); + + Cast2DEditor(); +}; + +class Cast2DEditorPlugin : public EditorPlugin { + GDCLASS(Cast2DEditorPlugin, EditorPlugin); + + Cast2DEditor *cast_2d_editor = nullptr; + +public: + virtual bool forward_canvas_gui_input(const Ref &p_event) override { return cast_2d_editor->forward_canvas_gui_input(p_event); } + virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override { cast_2d_editor->forward_canvas_draw_over_viewport(p_overlay); } + + virtual String get_name() const override { return "Cast2D"; } + bool has_main_screen() const override { return false; } + virtual void edit(Object *p_object) override; + virtual bool handles(Object *p_object) const override; + virtual void make_visible(bool visible) override; + + Cast2DEditorPlugin(); +}; + +#endif // CAST_2D_EDITOR_PLUGIN_H diff --git a/editor/plugins/ray_cast_2d_editor_plugin.cpp b/editor/plugins/ray_cast_2d_editor_plugin.cpp deleted file mode 100644 index 6f247a37ef..0000000000 --- a/editor/plugins/ray_cast_2d_editor_plugin.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/*************************************************************************/ -/* ray_cast_2d_editor_plugin.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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 "ray_cast_2d_editor_plugin.h" - -#include "canvas_item_editor_plugin.h" -#include "editor/editor_node.h" - -void RayCast2DEditor::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_ENTER_TREE: { - get_tree()->connect("node_removed", callable_mp(this, &RayCast2DEditor::_node_removed)); - } break; - - case NOTIFICATION_EXIT_TREE: { - get_tree()->disconnect("node_removed", callable_mp(this, &RayCast2DEditor::_node_removed)); - } break; - } -} - -void RayCast2DEditor::_node_removed(Node *p_node) { - if (p_node == node) { - node = nullptr; - } -} - -bool RayCast2DEditor::forward_canvas_gui_input(const Ref &p_event) { - if (!node || !node->is_visible_in_tree()) { - return false; - } - - Transform2D xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform(); - - Ref mb = p_event; - if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT) { - if (mb->is_pressed()) { - if (xform.xform(node->get_target_position()).distance_to(mb->get_position()) < 8) { - pressed = true; - original_target_position = node->get_target_position(); - - return true; - } else { - pressed = false; - - return false; - } - } else if (pressed) { - undo_redo->create_action(TTR("Set target_position")); - undo_redo->add_do_method(node, "set_target_position", node->get_target_position()); - undo_redo->add_do_method(canvas_item_editor, "update_viewport"); - undo_redo->add_undo_method(node, "set_target_position", original_target_position); - undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); - undo_redo->commit_action(); - - pressed = false; - - return true; - } - } - - Ref mm = p_event; - if (mm.is_valid() && pressed) { - Vector2 point = canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(mm->get_position())); - point = node->get_global_transform().affine_inverse().xform(point); - - node->set_target_position(point); - canvas_item_editor->update_viewport(); - node->notify_property_list_changed(); - - return true; - } - - return false; -} - -void RayCast2DEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { - if (!node || !node->is_visible_in_tree()) { - return; - } - - Transform2D gt = canvas_item_editor->get_canvas_transform() * node->get_global_transform(); - - const Ref handle = get_theme_icon(SNAME("EditorHandle"), SNAME("EditorIcons")); - p_overlay->draw_texture(handle, gt.xform(node->get_target_position()) - handle->get_size() / 2); -} - -void RayCast2DEditor::edit(Node *p_node) { - if (!canvas_item_editor) { - canvas_item_editor = CanvasItemEditor::get_singleton(); - } - - if (p_node) { - node = Object::cast_to(p_node); - } else { - node = nullptr; - } - - canvas_item_editor->update_viewport(); -} - -RayCast2DEditor::RayCast2DEditor() { - undo_redo = EditorNode::get_singleton()->get_undo_redo(); -} - -/////////////////////// - -void RayCast2DEditorPlugin::edit(Object *p_object) { - ray_cast_2d_editor->edit(Object::cast_to(p_object)); -} - -bool RayCast2DEditorPlugin::handles(Object *p_object) const { - return Object::cast_to(p_object) != nullptr; -} - -void RayCast2DEditorPlugin::make_visible(bool p_visible) { - if (!p_visible) { - edit(nullptr); - } -} - -RayCast2DEditorPlugin::RayCast2DEditorPlugin() { - ray_cast_2d_editor = memnew(RayCast2DEditor); - EditorNode::get_singleton()->get_gui_base()->add_child(ray_cast_2d_editor); -} diff --git a/editor/plugins/ray_cast_2d_editor_plugin.h b/editor/plugins/ray_cast_2d_editor_plugin.h deleted file mode 100644 index 74628da0e4..0000000000 --- a/editor/plugins/ray_cast_2d_editor_plugin.h +++ /dev/null @@ -1,79 +0,0 @@ -/*************************************************************************/ -/* ray_cast_2d_editor_plugin.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 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. */ -/*************************************************************************/ - -#ifndef RAY_CAST_2D_EDITOR_PLUGIN_H -#define RAY_CAST_2D_EDITOR_PLUGIN_H - -#include "editor/editor_plugin.h" -#include "scene/2d/ray_cast_2d.h" - -class CanvasItemEditor; - -class RayCast2DEditor : public Control { - GDCLASS(RayCast2DEditor, Control); - - UndoRedo *undo_redo = nullptr; - CanvasItemEditor *canvas_item_editor = nullptr; - RayCast2D *node; - - bool pressed = false; - Point2 original_target_position; - -protected: - void _notification(int p_what); - void _node_removed(Node *p_node); - -public: - bool forward_canvas_gui_input(const Ref &p_event); - void forward_canvas_draw_over_viewport(Control *p_overlay); - void edit(Node *p_node); - - RayCast2DEditor(); -}; - -class RayCast2DEditorPlugin : public EditorPlugin { - GDCLASS(RayCast2DEditorPlugin, EditorPlugin); - - RayCast2DEditor *ray_cast_2d_editor = nullptr; - -public: - virtual bool forward_canvas_gui_input(const Ref &p_event) override { return ray_cast_2d_editor->forward_canvas_gui_input(p_event); } - virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override { ray_cast_2d_editor->forward_canvas_draw_over_viewport(p_overlay); } - - virtual String get_name() const override { return "RayCast2D"; } - bool has_main_screen() const override { return false; } - virtual void edit(Object *p_object) override; - virtual bool handles(Object *p_object) const override; - virtual void make_visible(bool visible) override; - - RayCast2DEditorPlugin(); -}; - -#endif // RAY_CAST_2D_EDITOR_PLUGIN_H diff --git a/scene/2d/shape_cast_2d.cpp b/scene/2d/shape_cast_2d.cpp index 7589af0924..316988d298 100644 --- a/scene/2d/shape_cast_2d.cpp +++ b/scene/2d/shape_cast_2d.cpp @@ -217,7 +217,7 @@ void ShapeCast2D::_notification(int p_what) { if (shape.is_null()) { break; } - Color draw_col = get_tree()->get_debug_collisions_color(); + Color draw_col = collided ? Color(1.0, 0.01, 0) : get_tree()->get_debug_collisions_color(); if (!enabled) { float g = draw_col.get_v(); draw_col.r = g; @@ -235,18 +235,25 @@ void ShapeCast2D::_notification(int p_what) { // Draw an arrow indicating where the ShapeCast is pointing to. if (target_position != Vector2()) { - Transform2D xf; - xf.rotate(target_position.angle()); - xf.translate_local(Vector2(target_position.length(), 0)); + const real_t max_arrow_size = 6; + const real_t line_width = 1.4; + bool no_line = target_position.length() < line_width; + real_t arrow_size = CLAMP(target_position.length() * 2 / 3, line_width, max_arrow_size); - draw_line(Vector2(), target_position, draw_col, 2); + if (no_line) { + arrow_size = target_position.length(); + } else { + draw_line(Vector2(), target_position - target_position.normalized() * arrow_size, draw_col, line_width); + } - float tsize = 8; + Transform2D xf; + xf.rotate(target_position.angle()); + xf.translate_local(Vector2(no_line ? 0 : target_position.length() - arrow_size, 0)); Vector pts = { - xf.xform(Vector2(tsize, 0)), - xf.xform(Vector2(0, Math_SQRT12 * tsize)), - xf.xform(Vector2(0, -Math_SQRT12 * tsize)) + xf.xform(Vector2(arrow_size, 0)), + xf.xform(Vector2(0, 0.5 * arrow_size)), + xf.xform(Vector2(0, -0.5 * arrow_size)) }; Vector cols = { draw_col, draw_col, draw_col }; @@ -291,6 +298,8 @@ void ShapeCast2D::_update_shapecast_state() { collision_safe_fraction = 0.0; collision_unsafe_fraction = 0.0; + bool prev_collision_state = collided; + if (target_position != Vector2()) { dss->cast_motion(params, collision_safe_fraction, collision_unsafe_fraction); if (collision_unsafe_fraction < 1.0) { @@ -314,6 +323,10 @@ void ShapeCast2D::_update_shapecast_state() { } } collided = !result.is_empty(); + + if (prev_collision_state != collided) { + update(); + } } void ShapeCast2D::force_shapecast_update() { -- cgit v1.2.3