collision_shape_2d_editor_plugin.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
  1. /**************************************************************************/
  2. /* collision_shape_2d_editor_plugin.cpp */
  3. /**************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /**************************************************************************/
  8. /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
  9. /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /**************************************************************************/
  30. #include "collision_shape_2d_editor_plugin.h"
  31. #include "canvas_item_editor_plugin.h"
  32. #include "core/os/keyboard.h"
  33. #include "editor/editor_node.h"
  34. #include "editor/editor_settings.h"
  35. #include "editor/editor_undo_redo_manager.h"
  36. #include "scene/main/viewport.h"
  37. #include "scene/resources/2d/capsule_shape_2d.h"
  38. #include "scene/resources/2d/circle_shape_2d.h"
  39. #include "scene/resources/2d/concave_polygon_shape_2d.h"
  40. #include "scene/resources/2d/convex_polygon_shape_2d.h"
  41. #include "scene/resources/2d/rectangle_shape_2d.h"
  42. #include "scene/resources/2d/segment_shape_2d.h"
  43. #include "scene/resources/2d/separation_ray_shape_2d.h"
  44. #include "scene/resources/2d/world_boundary_shape_2d.h"
  45. CollisionShape2DEditor::CollisionShape2DEditor() {
  46. grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");
  47. }
  48. void CollisionShape2DEditor::_node_removed(Node *p_node) {
  49. if (p_node == node) {
  50. node = nullptr;
  51. }
  52. }
  53. Variant CollisionShape2DEditor::get_handle_value(int idx) const {
  54. switch (shape_type) {
  55. case CAPSULE_SHAPE: {
  56. Ref<CapsuleShape2D> capsule = node->get_shape();
  57. return Vector2(capsule->get_radius(), capsule->get_height());
  58. } break;
  59. case CIRCLE_SHAPE: {
  60. Ref<CircleShape2D> circle = node->get_shape();
  61. if (idx == 0) {
  62. return circle->get_radius();
  63. }
  64. } break;
  65. case CONCAVE_POLYGON_SHAPE: {
  66. Ref<ConcavePolygonShape2D> shape = node->get_shape();
  67. const Vector<Vector2> &segments = shape->get_segments();
  68. return segments[idx];
  69. } break;
  70. case CONVEX_POLYGON_SHAPE: {
  71. Ref<ConvexPolygonShape2D> shape = node->get_shape();
  72. const Vector<Vector2> &points = shape->get_points();
  73. return points[idx];
  74. } break;
  75. case WORLD_BOUNDARY_SHAPE: {
  76. Ref<WorldBoundaryShape2D> world_boundary = node->get_shape();
  77. if (idx == 0) {
  78. return world_boundary->get_distance();
  79. } else {
  80. return world_boundary->get_normal();
  81. }
  82. } break;
  83. case SEPARATION_RAY_SHAPE: {
  84. Ref<SeparationRayShape2D> ray = node->get_shape();
  85. if (idx == 0) {
  86. return ray->get_length();
  87. }
  88. } break;
  89. case RECTANGLE_SHAPE: {
  90. Ref<RectangleShape2D> rect = node->get_shape();
  91. if (idx < 8) {
  92. return rect->get_size().abs();
  93. }
  94. } break;
  95. case SEGMENT_SHAPE: {
  96. Ref<SegmentShape2D> seg = node->get_shape();
  97. if (idx == 0) {
  98. return seg->get_a();
  99. } else if (idx == 1) {
  100. return seg->get_b();
  101. }
  102. } break;
  103. }
  104. return Variant();
  105. }
  106. void CollisionShape2DEditor::set_handle(int idx, Point2 &p_point) {
  107. switch (shape_type) {
  108. case CAPSULE_SHAPE: {
  109. if (idx < 2) {
  110. Ref<CapsuleShape2D> capsule = node->get_shape();
  111. real_t parameter = Math::abs(p_point[idx]);
  112. if (idx == 0) {
  113. capsule->set_radius(parameter);
  114. } else if (idx == 1) {
  115. capsule->set_height(parameter * 2);
  116. }
  117. }
  118. } break;
  119. case CIRCLE_SHAPE: {
  120. Ref<CircleShape2D> circle = node->get_shape();
  121. circle->set_radius(p_point.length());
  122. } break;
  123. case CONCAVE_POLYGON_SHAPE: {
  124. Ref<ConcavePolygonShape2D> concave_shape = node->get_shape();
  125. Vector<Vector2> segments = concave_shape->get_segments();
  126. ERR_FAIL_INDEX(idx, segments.size());
  127. segments.write[idx] = p_point;
  128. concave_shape->set_segments(segments);
  129. } break;
  130. case CONVEX_POLYGON_SHAPE: {
  131. Ref<ConvexPolygonShape2D> convex_shape = node->get_shape();
  132. Vector<Vector2> points = convex_shape->get_points();
  133. ERR_FAIL_INDEX(idx, points.size());
  134. points.write[idx] = p_point;
  135. convex_shape->set_points(points);
  136. } break;
  137. case WORLD_BOUNDARY_SHAPE: {
  138. if (idx < 2) {
  139. Ref<WorldBoundaryShape2D> world_boundary = node->get_shape();
  140. if (idx == 0) {
  141. Vector2 normal = world_boundary->get_normal();
  142. world_boundary->set_distance(p_point.dot(normal) / normal.length_squared());
  143. } else {
  144. world_boundary->set_normal(p_point.normalized());
  145. }
  146. }
  147. } break;
  148. case SEPARATION_RAY_SHAPE: {
  149. Ref<SeparationRayShape2D> ray = node->get_shape();
  150. ray->set_length(Math::abs(p_point.y));
  151. } break;
  152. case RECTANGLE_SHAPE: {
  153. if (idx < 8) {
  154. Ref<RectangleShape2D> rect = node->get_shape();
  155. Vector2 size = (Point2)original;
  156. if (RECT_HANDLES[idx].x != 0) {
  157. size.x = p_point.x * RECT_HANDLES[idx].x * 2;
  158. }
  159. if (RECT_HANDLES[idx].y != 0) {
  160. size.y = p_point.y * RECT_HANDLES[idx].y * 2;
  161. }
  162. if (Input::get_singleton()->is_key_pressed(Key::ALT)) {
  163. rect->set_size(size.abs());
  164. node->set_global_position(original_transform.get_origin());
  165. } else {
  166. rect->set_size(((Point2)original + (size - (Point2)original) * 0.5).abs());
  167. Point2 pos = original_transform.affine_inverse().xform(original_transform.get_origin());
  168. pos += (size - (Point2)original) * 0.5 * RECT_HANDLES[idx] * 0.5;
  169. node->set_global_position(original_transform.xform(pos));
  170. }
  171. }
  172. } break;
  173. case SEGMENT_SHAPE: {
  174. if (edit_handle < 2) {
  175. Ref<SegmentShape2D> seg = node->get_shape();
  176. if (idx == 0) {
  177. seg->set_a(p_point);
  178. } else if (idx == 1) {
  179. seg->set_b(p_point);
  180. }
  181. }
  182. } break;
  183. }
  184. }
  185. void CollisionShape2DEditor::commit_handle(int idx, Variant &p_org) {
  186. EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
  187. undo_redo->create_action(TTR("Set Handle"));
  188. switch (shape_type) {
  189. case CAPSULE_SHAPE: {
  190. Ref<CapsuleShape2D> capsule = node->get_shape();
  191. Vector2 values = p_org;
  192. if (idx == 0) {
  193. undo_redo->add_do_method(capsule.ptr(), "set_radius", capsule->get_radius());
  194. } else if (idx == 1) {
  195. undo_redo->add_do_method(capsule.ptr(), "set_height", capsule->get_height());
  196. }
  197. undo_redo->add_undo_method(capsule.ptr(), "set_radius", values[0]);
  198. undo_redo->add_undo_method(capsule.ptr(), "set_height", values[1]);
  199. } break;
  200. case CIRCLE_SHAPE: {
  201. Ref<CircleShape2D> circle = node->get_shape();
  202. undo_redo->add_do_method(circle.ptr(), "set_radius", circle->get_radius());
  203. undo_redo->add_undo_method(circle.ptr(), "set_radius", p_org);
  204. } break;
  205. case CONCAVE_POLYGON_SHAPE: {
  206. Ref<ConcavePolygonShape2D> concave_shape = node->get_shape();
  207. Vector2 values = p_org;
  208. Vector<Vector2> undo_segments = concave_shape->get_segments();
  209. ERR_FAIL_INDEX(idx, undo_segments.size());
  210. undo_segments.write[idx] = values;
  211. undo_redo->add_do_method(concave_shape.ptr(), "set_segments", concave_shape->get_segments());
  212. undo_redo->add_undo_method(concave_shape.ptr(), "set_segments", undo_segments);
  213. } break;
  214. case CONVEX_POLYGON_SHAPE: {
  215. Ref<ConvexPolygonShape2D> convex_shape = node->get_shape();
  216. Vector2 values = p_org;
  217. Vector<Vector2> undo_points = convex_shape->get_points();
  218. ERR_FAIL_INDEX(idx, undo_points.size());
  219. undo_points.write[idx] = values;
  220. undo_redo->add_do_method(convex_shape.ptr(), "set_points", convex_shape->get_points());
  221. undo_redo->add_undo_method(convex_shape.ptr(), "set_points", undo_points);
  222. } break;
  223. case WORLD_BOUNDARY_SHAPE: {
  224. Ref<WorldBoundaryShape2D> world_boundary = node->get_shape();
  225. if (idx == 0) {
  226. undo_redo->add_do_method(world_boundary.ptr(), "set_distance", world_boundary->get_distance());
  227. undo_redo->add_undo_method(world_boundary.ptr(), "set_distance", p_org);
  228. } else {
  229. undo_redo->add_do_method(world_boundary.ptr(), "set_normal", world_boundary->get_normal());
  230. undo_redo->add_undo_method(world_boundary.ptr(), "set_normal", p_org);
  231. }
  232. } break;
  233. case SEPARATION_RAY_SHAPE: {
  234. Ref<SeparationRayShape2D> ray = node->get_shape();
  235. undo_redo->add_do_method(ray.ptr(), "set_length", ray->get_length());
  236. undo_redo->add_undo_method(ray.ptr(), "set_length", p_org);
  237. } break;
  238. case RECTANGLE_SHAPE: {
  239. Ref<RectangleShape2D> rect = node->get_shape();
  240. undo_redo->add_do_method(rect.ptr(), "set_size", rect->get_size());
  241. undo_redo->add_do_method(node, "set_global_transform", node->get_global_transform());
  242. undo_redo->add_undo_method(rect.ptr(), "set_size", p_org);
  243. undo_redo->add_undo_method(node, "set_global_transform", original_transform);
  244. } break;
  245. case SEGMENT_SHAPE: {
  246. Ref<SegmentShape2D> seg = node->get_shape();
  247. if (idx == 0) {
  248. undo_redo->add_do_method(seg.ptr(), "set_a", seg->get_a());
  249. undo_redo->add_undo_method(seg.ptr(), "set_a", p_org);
  250. } else if (idx == 1) {
  251. undo_redo->add_do_method(seg.ptr(), "set_b", seg->get_b());
  252. undo_redo->add_undo_method(seg.ptr(), "set_b", p_org);
  253. }
  254. } break;
  255. }
  256. undo_redo->commit_action();
  257. }
  258. bool CollisionShape2DEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_event) {
  259. if (!node) {
  260. return false;
  261. }
  262. if (!node->is_visible_in_tree()) {
  263. return false;
  264. }
  265. Viewport *vp = node->get_viewport();
  266. if (vp && !vp->is_visible_subviewport()) {
  267. return false;
  268. }
  269. if (shape_type == -1) {
  270. return false;
  271. }
  272. Ref<InputEventMouseButton> mb = p_event;
  273. Transform2D xform = canvas_item_editor->get_canvas_transform() * node->get_screen_transform();
  274. if (mb.is_valid()) {
  275. Vector2 gpoint = mb->get_position();
  276. if (mb->get_button_index() == MouseButton::LEFT) {
  277. if (mb->is_pressed()) {
  278. for (int i = 0; i < handles.size(); i++) {
  279. if (xform.xform(handles[i]).distance_to(gpoint) < grab_threshold) {
  280. edit_handle = i;
  281. break;
  282. }
  283. }
  284. if (edit_handle == -1) {
  285. pressed = false;
  286. return false;
  287. }
  288. original_mouse_pos = gpoint;
  289. original_point = handles[edit_handle];
  290. original = get_handle_value(edit_handle);
  291. original_transform = node->get_global_transform();
  292. last_point = original;
  293. pressed = true;
  294. return true;
  295. } else {
  296. if (pressed) {
  297. if (original_mouse_pos != gpoint) {
  298. commit_handle(edit_handle, original);
  299. }
  300. edit_handle = -1;
  301. pressed = false;
  302. return true;
  303. }
  304. }
  305. }
  306. return false;
  307. }
  308. Ref<InputEventMouseMotion> mm = p_event;
  309. if (mm.is_valid()) {
  310. if (edit_handle == -1 || !pressed) {
  311. return false;
  312. }
  313. Vector2 cpoint = canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(mm->get_position()));
  314. cpoint = node->get_viewport()->get_popup_base_transform().affine_inverse().xform(cpoint);
  315. cpoint = original_transform.affine_inverse().xform(cpoint);
  316. last_point = cpoint;
  317. set_handle(edit_handle, cpoint);
  318. return true;
  319. }
  320. Ref<InputEventKey> k = p_event;
  321. if (k.is_valid()) {
  322. if (edit_handle == -1 || !pressed || k->is_echo()) {
  323. return false;
  324. }
  325. if (shape_type == RECTANGLE_SHAPE && k->get_keycode() == Key::ALT) {
  326. set_handle(edit_handle, last_point); // Update handle when Alt key is toggled.
  327. }
  328. }
  329. return false;
  330. }
  331. void CollisionShape2DEditor::_shape_changed() {
  332. canvas_item_editor->update_viewport();
  333. if (current_shape.is_valid()) {
  334. current_shape->disconnect_changed(callable_mp(canvas_item_editor, &CanvasItemEditor::update_viewport));
  335. current_shape = Ref<Shape2D>();
  336. shape_type = -1;
  337. }
  338. if (!node) {
  339. return;
  340. }
  341. current_shape = node->get_shape();
  342. if (current_shape.is_valid()) {
  343. current_shape->connect_changed(callable_mp(canvas_item_editor, &CanvasItemEditor::update_viewport));
  344. } else {
  345. return;
  346. }
  347. if (Object::cast_to<CapsuleShape2D>(*current_shape)) {
  348. shape_type = CAPSULE_SHAPE;
  349. } else if (Object::cast_to<CircleShape2D>(*current_shape)) {
  350. shape_type = CIRCLE_SHAPE;
  351. } else if (Object::cast_to<ConcavePolygonShape2D>(*current_shape)) {
  352. shape_type = CONCAVE_POLYGON_SHAPE;
  353. } else if (Object::cast_to<ConvexPolygonShape2D>(*current_shape)) {
  354. shape_type = CONVEX_POLYGON_SHAPE;
  355. } else if (Object::cast_to<WorldBoundaryShape2D>(*current_shape)) {
  356. shape_type = WORLD_BOUNDARY_SHAPE;
  357. } else if (Object::cast_to<SeparationRayShape2D>(*current_shape)) {
  358. shape_type = SEPARATION_RAY_SHAPE;
  359. } else if (Object::cast_to<RectangleShape2D>(*current_shape)) {
  360. shape_type = RECTANGLE_SHAPE;
  361. } else if (Object::cast_to<SegmentShape2D>(*current_shape)) {
  362. shape_type = SEGMENT_SHAPE;
  363. }
  364. }
  365. void CollisionShape2DEditor::forward_canvas_draw_over_viewport(Control *p_overlay) {
  366. if (!node) {
  367. return;
  368. }
  369. if (!node->is_visible_in_tree()) {
  370. return;
  371. }
  372. Viewport *vp = node->get_viewport();
  373. if (vp && !vp->is_visible_subviewport()) {
  374. return;
  375. }
  376. if (shape_type == -1) {
  377. return;
  378. }
  379. Transform2D gt = canvas_item_editor->get_canvas_transform() * node->get_screen_transform();
  380. Ref<Texture2D> h = get_editor_theme_icon(SNAME("EditorHandle"));
  381. Vector2 size = h->get_size() * 0.5;
  382. handles.clear();
  383. switch (shape_type) {
  384. case CAPSULE_SHAPE: {
  385. Ref<CapsuleShape2D> shape = current_shape;
  386. handles.resize(2);
  387. float radius = shape->get_radius();
  388. float height = shape->get_height() / 2;
  389. handles.write[0] = Point2(radius, 0);
  390. handles.write[1] = Point2(0, height);
  391. p_overlay->draw_texture(h, gt.xform(handles[0]) - size);
  392. p_overlay->draw_texture(h, gt.xform(handles[1]) - size);
  393. } break;
  394. case CIRCLE_SHAPE: {
  395. Ref<CircleShape2D> shape = current_shape;
  396. handles.resize(1);
  397. handles.write[0] = Point2(shape->get_radius(), 0);
  398. p_overlay->draw_texture(h, gt.xform(handles[0]) - size);
  399. } break;
  400. case CONCAVE_POLYGON_SHAPE: {
  401. Ref<ConcavePolygonShape2D> shape = current_shape;
  402. const Vector<Vector2> &segments = shape->get_segments();
  403. handles.resize(segments.size());
  404. for (int i = 0; i < handles.size(); i++) {
  405. handles.write[i] = segments[i];
  406. p_overlay->draw_texture(h, gt.xform(handles[i]) - size);
  407. }
  408. } break;
  409. case CONVEX_POLYGON_SHAPE: {
  410. Ref<ConvexPolygonShape2D> shape = current_shape;
  411. const Vector<Vector2> &points = shape->get_points();
  412. handles.resize(points.size());
  413. for (int i = 0; i < handles.size(); i++) {
  414. handles.write[i] = points[i];
  415. p_overlay->draw_texture(h, gt.xform(handles[i]) - size);
  416. }
  417. } break;
  418. case WORLD_BOUNDARY_SHAPE: {
  419. Ref<WorldBoundaryShape2D> shape = current_shape;
  420. handles.resize(2);
  421. handles.write[0] = shape->get_normal() * shape->get_distance();
  422. handles.write[1] = shape->get_normal() * (shape->get_distance() + 30.0);
  423. p_overlay->draw_texture(h, gt.xform(handles[0]) - size);
  424. p_overlay->draw_texture(h, gt.xform(handles[1]) - size);
  425. } break;
  426. case SEPARATION_RAY_SHAPE: {
  427. Ref<SeparationRayShape2D> shape = current_shape;
  428. handles.resize(1);
  429. handles.write[0] = Point2(0, shape->get_length());
  430. p_overlay->draw_texture(h, gt.xform(handles[0]) - size);
  431. } break;
  432. case RECTANGLE_SHAPE: {
  433. Ref<RectangleShape2D> shape = current_shape;
  434. handles.resize(8);
  435. Vector2 ext = shape->get_size() / 2;
  436. for (int i = 0; i < handles.size(); i++) {
  437. handles.write[i] = RECT_HANDLES[i] * ext;
  438. p_overlay->draw_texture(h, gt.xform(handles[i]) - size);
  439. }
  440. } break;
  441. case SEGMENT_SHAPE: {
  442. Ref<SegmentShape2D> shape = current_shape;
  443. handles.resize(2);
  444. handles.write[0] = shape->get_a();
  445. handles.write[1] = shape->get_b();
  446. p_overlay->draw_texture(h, gt.xform(handles[0]) - size);
  447. p_overlay->draw_texture(h, gt.xform(handles[1]) - size);
  448. } break;
  449. }
  450. }
  451. void CollisionShape2DEditor::_notification(int p_what) {
  452. switch (p_what) {
  453. case NOTIFICATION_ENTER_TREE: {
  454. get_tree()->connect("node_removed", callable_mp(this, &CollisionShape2DEditor::_node_removed));
  455. } break;
  456. case NOTIFICATION_EXIT_TREE: {
  457. get_tree()->disconnect("node_removed", callable_mp(this, &CollisionShape2DEditor::_node_removed));
  458. } break;
  459. case NOTIFICATION_PROCESS: {
  460. if (node && node->get_shape() != current_shape) {
  461. _shape_changed();
  462. }
  463. } break;
  464. case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
  465. if (EditorSettings::get_singleton()->check_changed_settings_in_group("editors/polygon_editor")) {
  466. grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");
  467. }
  468. } break;
  469. }
  470. }
  471. void CollisionShape2DEditor::edit(Node *p_node) {
  472. if (!canvas_item_editor) {
  473. canvas_item_editor = CanvasItemEditor::get_singleton();
  474. }
  475. if (p_node) {
  476. node = Object::cast_to<CollisionShape2D>(p_node);
  477. set_process(true);
  478. } else {
  479. if (pressed) {
  480. set_handle(edit_handle, original_point);
  481. pressed = false;
  482. }
  483. edit_handle = -1;
  484. node = nullptr;
  485. set_process(false);
  486. }
  487. _shape_changed();
  488. }
  489. void CollisionShape2DEditorPlugin::edit(Object *p_obj) {
  490. collision_shape_2d_editor->edit(Object::cast_to<Node>(p_obj));
  491. }
  492. bool CollisionShape2DEditorPlugin::handles(Object *p_obj) const {
  493. return p_obj->is_class("CollisionShape2D");
  494. }
  495. void CollisionShape2DEditorPlugin::make_visible(bool visible) {
  496. if (!visible) {
  497. edit(nullptr);
  498. }
  499. }
  500. CollisionShape2DEditorPlugin::CollisionShape2DEditorPlugin() {
  501. collision_shape_2d_editor = memnew(CollisionShape2DEditor);
  502. EditorNode::get_singleton()->get_gui_base()->add_child(collision_shape_2d_editor);
  503. }
  504. CollisionShape2DEditorPlugin::~CollisionShape2DEditorPlugin() {
  505. }