camera_2d.cpp 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002
  1. /**************************************************************************/
  2. /* camera_2d.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 "camera_2d.h"
  31. #include "core/config/project_settings.h"
  32. #include "core/input/input.h"
  33. #include "scene/main/viewport.h"
  34. void Camera2D::_update_scroll() {
  35. if (!is_inside_tree() || !viewport) {
  36. return;
  37. }
  38. if (is_part_of_edited_scene()) {
  39. queue_redraw();
  40. return;
  41. }
  42. if (is_current()) {
  43. ERR_FAIL_COND(custom_viewport && !ObjectDB::get_instance(custom_viewport_id));
  44. Size2 screen_size = _get_camera_screen_size();
  45. Transform2D xform;
  46. if (is_physics_interpolated_and_enabled()) {
  47. xform = _interpolation_data.xform_prev.interpolate_with(_interpolation_data.xform_curr, Engine::get_singleton()->get_physics_interpolation_fraction());
  48. camera_screen_center = xform.affine_inverse().xform(0.5 * screen_size);
  49. } else {
  50. xform = get_camera_transform();
  51. }
  52. viewport->set_canvas_transform(xform);
  53. Point2 screen_offset = (anchor_mode == ANCHOR_MODE_DRAG_CENTER ? (screen_size * 0.5) : Point2());
  54. Point2 adj_screen_pos = camera_screen_center - (screen_size * 0.5);
  55. // TODO: Remove xform and screen_offset when ParallaxBackground/ParallaxLayer is removed.
  56. get_tree()->call_group(group_name, SNAME("_camera_moved"), xform, screen_offset, adj_screen_pos);
  57. }
  58. }
  59. #ifdef TOOLS_ENABLED
  60. void Camera2D::_project_settings_changed() {
  61. if (screen_drawing_enabled) {
  62. queue_redraw();
  63. }
  64. }
  65. #endif
  66. void Camera2D::_update_process_callback() {
  67. if (is_physics_interpolated_and_enabled()) {
  68. set_process_internal(is_current());
  69. set_physics_process_internal(is_current());
  70. #ifdef TOOLS_ENABLED
  71. if (process_callback == CAMERA2D_PROCESS_IDLE) {
  72. WARN_PRINT_ONCE("Camera2D overridden to physics process mode due to use of physics interpolation.");
  73. }
  74. #endif
  75. } else if (is_part_of_edited_scene()) {
  76. set_process_internal(false);
  77. set_physics_process_internal(false);
  78. } else {
  79. if (process_callback == CAMERA2D_PROCESS_IDLE) {
  80. set_process_internal(true);
  81. set_physics_process_internal(false);
  82. } else {
  83. set_process_internal(false);
  84. set_physics_process_internal(true);
  85. }
  86. }
  87. }
  88. void Camera2D::set_zoom(const Vector2 &p_zoom) {
  89. // Setting zoom to zero causes 'affine_invert' issues.
  90. ERR_FAIL_COND_MSG(Math::is_zero_approx(p_zoom.x) || Math::is_zero_approx(p_zoom.y), "Zoom level must be different from 0 (can be negative).");
  91. zoom = p_zoom;
  92. zoom_scale = Vector2(1, 1) / zoom;
  93. Point2 old_smoothed_camera_pos = smoothed_camera_pos;
  94. _update_scroll();
  95. smoothed_camera_pos = old_smoothed_camera_pos;
  96. }
  97. Vector2 Camera2D::get_zoom() const {
  98. return zoom;
  99. }
  100. Transform2D Camera2D::get_camera_transform() {
  101. if (!get_tree()) {
  102. return Transform2D();
  103. }
  104. ERR_FAIL_COND_V(custom_viewport && !ObjectDB::get_instance(custom_viewport_id), Transform2D());
  105. Size2 screen_size = _get_camera_screen_size();
  106. Point2 new_camera_pos = get_global_position();
  107. Point2 ret_camera_pos;
  108. if (!first) {
  109. if (anchor_mode == ANCHOR_MODE_DRAG_CENTER) {
  110. if (drag_horizontal_enabled && !is_part_of_edited_scene() && !drag_horizontal_offset_changed) {
  111. camera_pos.x = MIN(camera_pos.x, (new_camera_pos.x + screen_size.x * 0.5 * zoom_scale.x * drag_margin[SIDE_LEFT]));
  112. camera_pos.x = MAX(camera_pos.x, (new_camera_pos.x - screen_size.x * 0.5 * zoom_scale.x * drag_margin[SIDE_RIGHT]));
  113. } else {
  114. if (drag_horizontal_offset < 0) {
  115. camera_pos.x = new_camera_pos.x + screen_size.x * 0.5 * drag_margin[SIDE_RIGHT] * drag_horizontal_offset;
  116. } else {
  117. camera_pos.x = new_camera_pos.x + screen_size.x * 0.5 * drag_margin[SIDE_LEFT] * drag_horizontal_offset;
  118. }
  119. drag_horizontal_offset_changed = false;
  120. }
  121. if (drag_vertical_enabled && !is_part_of_edited_scene() && !drag_vertical_offset_changed) {
  122. camera_pos.y = MIN(camera_pos.y, (new_camera_pos.y + screen_size.y * 0.5 * zoom_scale.y * drag_margin[SIDE_TOP]));
  123. camera_pos.y = MAX(camera_pos.y, (new_camera_pos.y - screen_size.y * 0.5 * zoom_scale.y * drag_margin[SIDE_BOTTOM]));
  124. } else {
  125. if (drag_vertical_offset < 0) {
  126. camera_pos.y = new_camera_pos.y + screen_size.y * 0.5 * drag_margin[SIDE_BOTTOM] * drag_vertical_offset;
  127. } else {
  128. camera_pos.y = new_camera_pos.y + screen_size.y * 0.5 * drag_margin[SIDE_TOP] * drag_vertical_offset;
  129. }
  130. drag_vertical_offset_changed = false;
  131. }
  132. } else if (anchor_mode == ANCHOR_MODE_FIXED_TOP_LEFT) {
  133. camera_pos = new_camera_pos;
  134. }
  135. Point2 screen_offset = (anchor_mode == ANCHOR_MODE_DRAG_CENTER ? (screen_size * 0.5 * zoom_scale) : Point2());
  136. Rect2 screen_rect(-screen_offset + camera_pos, screen_size * zoom_scale);
  137. if (limit_enabled && limit_smoothing_enabled) {
  138. // Apply horizontal limiting.
  139. if (screen_rect.size.x > limit[SIDE_RIGHT] - limit[SIDE_LEFT]) {
  140. // Split the limit difference horizontally.
  141. camera_pos.x -= screen_rect.position.x + (screen_rect.size.x - limit[SIDE_RIGHT] - limit[SIDE_LEFT]) / 2;
  142. } else if (screen_rect.position.x < limit[SIDE_LEFT]) {
  143. // Only apply left limit.
  144. camera_pos.x -= screen_rect.position.x - limit[SIDE_LEFT];
  145. } else if (screen_rect.position.x + screen_rect.size.x > limit[SIDE_RIGHT]) {
  146. // Only apply the right limit.
  147. camera_pos.x -= screen_rect.position.x + screen_rect.size.x - limit[SIDE_RIGHT];
  148. }
  149. // Apply vertical limiting.
  150. if (screen_rect.size.y > limit[SIDE_BOTTOM] - limit[SIDE_TOP]) {
  151. // Split the limit difference vertically.
  152. camera_pos.y -= screen_rect.position.y + (screen_rect.size.y - limit[SIDE_BOTTOM] - limit[SIDE_TOP]) / 2;
  153. } else if (screen_rect.position.y < limit[SIDE_TOP]) {
  154. // Only apply the top limit.
  155. camera_pos.y -= screen_rect.position.y - limit[SIDE_TOP];
  156. } else if (screen_rect.position.y + screen_rect.size.y > limit[SIDE_BOTTOM]) {
  157. // Only apply the bottom limit.
  158. camera_pos.y -= screen_rect.position.y + screen_rect.size.y - limit[SIDE_BOTTOM];
  159. }
  160. }
  161. // FIXME: There is a bug here, introduced before physics interpolation.
  162. // Smoothing occurs rather confusingly during the call to get_camera_transform().
  163. // It may be called MULTIPLE TIMES on certain frames,
  164. // therefore smoothing is not currently applied only once per frame / tick,
  165. // which will result in some haphazard results.
  166. if (position_smoothing_enabled && !is_part_of_edited_scene()) {
  167. bool physics_process = (process_callback == CAMERA2D_PROCESS_PHYSICS) || is_physics_interpolated_and_enabled();
  168. real_t delta = physics_process ? get_physics_process_delta_time() : get_process_delta_time();
  169. real_t c = position_smoothing_speed * delta;
  170. smoothed_camera_pos = ((camera_pos - smoothed_camera_pos) * c) + smoothed_camera_pos;
  171. ret_camera_pos = smoothed_camera_pos;
  172. //camera_pos=camera_pos*(1.0-position_smoothing_speed)+new_camera_pos*position_smoothing_speed;
  173. } else {
  174. ret_camera_pos = smoothed_camera_pos = camera_pos;
  175. }
  176. } else {
  177. ret_camera_pos = smoothed_camera_pos = camera_pos = new_camera_pos;
  178. first = false;
  179. }
  180. Point2 screen_offset = (anchor_mode == ANCHOR_MODE_DRAG_CENTER ? (screen_size * 0.5 * zoom_scale) : Point2());
  181. if (!ignore_rotation) {
  182. if (rotation_smoothing_enabled && !is_part_of_edited_scene()) {
  183. real_t step = rotation_smoothing_speed * (process_callback == CAMERA2D_PROCESS_PHYSICS ? get_physics_process_delta_time() : get_process_delta_time());
  184. camera_angle = Math::lerp_angle(camera_angle, get_global_rotation(), step);
  185. } else {
  186. camera_angle = get_global_rotation();
  187. }
  188. screen_offset = screen_offset.rotated(camera_angle);
  189. }
  190. Rect2 screen_rect(-screen_offset + ret_camera_pos, screen_size * zoom_scale);
  191. if (limit_enabled && (!position_smoothing_enabled || !limit_smoothing_enabled)) {
  192. Point2 bottom_right_corner = Point2(screen_rect.position + 2.0 * (ret_camera_pos - screen_rect.position));
  193. // Apply horizontal limiting.
  194. if (bottom_right_corner.x - screen_rect.position.x > limit[SIDE_RIGHT] - limit[SIDE_LEFT]) {
  195. // Split the difference horizontally (center it).
  196. screen_rect.position.x = (limit[SIDE_LEFT] + limit[SIDE_RIGHT] - (bottom_right_corner.x - screen_rect.position.x)) / 2;
  197. } else if (screen_rect.position.x < limit[SIDE_LEFT]) {
  198. // Only apply left limit.
  199. screen_rect.position.x = limit[SIDE_LEFT];
  200. } else if (bottom_right_corner.x > limit[SIDE_RIGHT]) {
  201. // Only apply right limit.
  202. screen_rect.position.x = limit[SIDE_RIGHT] - (bottom_right_corner.x - screen_rect.position.x);
  203. }
  204. // Apply vertical limiting.
  205. if (bottom_right_corner.y - screen_rect.position.y > limit[SIDE_BOTTOM] - limit[SIDE_TOP]) {
  206. // Split the limit difference vertically.
  207. screen_rect.position.y = (limit[SIDE_TOP] + limit[SIDE_BOTTOM] - (bottom_right_corner.y - screen_rect.position.y)) / 2;
  208. } else if (screen_rect.position.y < limit[SIDE_TOP]) {
  209. // Only apply the top limit.
  210. screen_rect.position.y = limit[SIDE_TOP];
  211. } else if (bottom_right_corner.y > limit[SIDE_BOTTOM]) {
  212. // Only apply the bottom limit.
  213. screen_rect.position.y = limit[SIDE_BOTTOM] - (bottom_right_corner.y - screen_rect.position.y);
  214. }
  215. }
  216. if (offset != Vector2()) {
  217. screen_rect.position += offset;
  218. }
  219. Transform2D xform;
  220. xform.scale_basis(zoom_scale);
  221. if (!ignore_rotation) {
  222. xform.set_rotation(camera_angle);
  223. }
  224. xform.set_origin(screen_rect.position);
  225. camera_screen_center = xform.xform(0.5 * screen_size);
  226. return xform.affine_inverse();
  227. }
  228. void Camera2D::_ensure_update_interpolation_data() {
  229. // The "curr -> previous" update can either occur
  230. // on NOTIFICATION_INTERNAL_PHYSICS_PROCESS, OR
  231. // on NOTIFICATION_TRANSFORM_CHANGED,
  232. // if NOTIFICATION_TRANSFORM_CHANGED takes place earlier than
  233. // NOTIFICATION_INTERNAL_PHYSICS_PROCESS on a tick.
  234. // This is to ensure that the data keeps flowing, but the new data
  235. // doesn't overwrite before prev has been set.
  236. // Keep the data flowing.
  237. uint64_t tick = Engine::get_singleton()->get_physics_frames();
  238. if (_interpolation_data.last_update_physics_tick != tick) {
  239. _interpolation_data.xform_prev = _interpolation_data.xform_curr;
  240. _interpolation_data.last_update_physics_tick = tick;
  241. }
  242. }
  243. void Camera2D::_notification(int p_what) {
  244. switch (p_what) {
  245. #ifdef TOOLS_ENABLED
  246. case NOTIFICATION_READY: {
  247. if (is_part_of_edited_scene()) {
  248. ProjectSettings::get_singleton()->connect(SNAME("settings_changed"), callable_mp(this, &Camera2D::_project_settings_changed));
  249. }
  250. } break;
  251. #endif
  252. case NOTIFICATION_INTERNAL_PROCESS: {
  253. _update_scroll();
  254. } break;
  255. case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
  256. if (is_physics_interpolated_and_enabled()) {
  257. _ensure_update_interpolation_data();
  258. _interpolation_data.xform_curr = get_camera_transform();
  259. } else {
  260. _update_scroll();
  261. }
  262. } break;
  263. case NOTIFICATION_RESET_PHYSICS_INTERPOLATION: {
  264. // Force the limits etc. to update.
  265. _interpolation_data.xform_curr = get_camera_transform();
  266. _interpolation_data.xform_prev = _interpolation_data.xform_curr;
  267. _update_process_callback();
  268. } break;
  269. case NOTIFICATION_SUSPENDED:
  270. case NOTIFICATION_PAUSED: {
  271. if (is_physics_interpolated_and_enabled()) {
  272. _update_scroll();
  273. }
  274. } break;
  275. case NOTIFICATION_TRANSFORM_CHANGED: {
  276. if ((!position_smoothing_enabled && !is_physics_interpolated_and_enabled()) || is_part_of_edited_scene()) {
  277. _update_scroll();
  278. }
  279. if (is_physics_interpolated_and_enabled()) {
  280. _ensure_update_interpolation_data();
  281. if (Engine::get_singleton()->is_in_physics_frame()) {
  282. _interpolation_data.xform_curr = get_camera_transform();
  283. }
  284. }
  285. } break;
  286. case NOTIFICATION_ENTER_TREE: {
  287. ERR_FAIL_COND(!is_inside_tree());
  288. if (custom_viewport && ObjectDB::get_instance(custom_viewport_id)) {
  289. viewport = custom_viewport;
  290. } else {
  291. viewport = get_viewport();
  292. }
  293. canvas = get_canvas();
  294. RID vp = viewport->get_viewport_rid();
  295. group_name = "__cameras_" + itos(vp.get_id());
  296. canvas_group_name = "__cameras_c" + itos(canvas.get_id());
  297. add_to_group(group_name);
  298. add_to_group(canvas_group_name);
  299. if (!is_part_of_edited_scene() && enabled && !viewport->get_camera_2d()) {
  300. make_current();
  301. }
  302. _update_process_callback();
  303. first = true;
  304. _update_scroll();
  305. // Note that NOTIFICATION_RESET_PHYSICS_INTERPOLATION
  306. // is automatically called before this because Camera2D is inherited
  307. // from CanvasItem. However, the camera transform is not up to date
  308. // until this point, so we do an extra manual reset.
  309. if (is_physics_interpolated_and_enabled()) {
  310. _interpolation_data.xform_curr = get_camera_transform();
  311. _interpolation_data.xform_prev = _interpolation_data.xform_curr;
  312. }
  313. } break;
  314. case NOTIFICATION_EXIT_TREE: {
  315. remove_from_group(group_name);
  316. remove_from_group(canvas_group_name);
  317. if (is_current()) {
  318. clear_current();
  319. }
  320. viewport = nullptr;
  321. just_exited_tree = true;
  322. callable_mp(this, &Camera2D::_reset_just_exited).call_deferred();
  323. } break;
  324. #ifdef TOOLS_ENABLED
  325. case NOTIFICATION_DRAW: {
  326. if (!is_inside_tree() || !is_part_of_edited_scene()) {
  327. break;
  328. }
  329. if (screen_drawing_enabled) {
  330. Color area_axis_color(1, 0.4, 1, 0.63);
  331. real_t area_axis_width = -1;
  332. if (is_current()) {
  333. area_axis_width = 3;
  334. }
  335. Transform2D inv_camera_transform = get_camera_transform().affine_inverse();
  336. Size2 screen_size = _get_camera_screen_size();
  337. Vector2 screen_endpoints[4] = {
  338. inv_camera_transform.xform(Vector2(0, 0)),
  339. inv_camera_transform.xform(Vector2(screen_size.width, 0)),
  340. inv_camera_transform.xform(Vector2(screen_size.width, screen_size.height)),
  341. inv_camera_transform.xform(Vector2(0, screen_size.height))
  342. };
  343. Transform2D inv_transform = get_global_transform().affine_inverse(); // Undo global space.
  344. for (int i = 0; i < 4; i++) {
  345. draw_line(inv_transform.xform(screen_endpoints[i]), inv_transform.xform(screen_endpoints[(i + 1) % 4]), area_axis_color, area_axis_width);
  346. }
  347. }
  348. if (limit_enabled && limit_drawing_enabled) {
  349. real_t limit_drawing_width = -1;
  350. if (is_current()) {
  351. limit_drawing_width = 3;
  352. }
  353. draw_set_transform_matrix(get_global_transform().affine_inverse());
  354. draw_rect(get_limit_rect(), Color(1, 1, 0.25, 0.63), false, limit_drawing_width);
  355. draw_set_transform_matrix(Transform2D());
  356. }
  357. if (margin_drawing_enabled) {
  358. Color margin_drawing_color(0.25, 1, 1, 0.63);
  359. real_t margin_drawing_width = -1;
  360. if (is_current()) {
  361. margin_drawing_width = 3;
  362. }
  363. Transform2D inv_camera_transform = get_camera_transform().affine_inverse();
  364. Size2 screen_size = _get_camera_screen_size();
  365. Vector2 margin_endpoints[4] = {
  366. inv_camera_transform.xform(Vector2((screen_size.width / 2) - ((screen_size.width / 2) * drag_margin[SIDE_LEFT]), (screen_size.height / 2) - ((screen_size.height / 2) * drag_margin[SIDE_TOP]))),
  367. inv_camera_transform.xform(Vector2((screen_size.width / 2) + ((screen_size.width / 2) * drag_margin[SIDE_RIGHT]), (screen_size.height / 2) - ((screen_size.height / 2) * drag_margin[SIDE_TOP]))),
  368. inv_camera_transform.xform(Vector2((screen_size.width / 2) + ((screen_size.width / 2) * drag_margin[SIDE_RIGHT]), (screen_size.height / 2) + ((screen_size.height / 2) * drag_margin[SIDE_BOTTOM]))),
  369. inv_camera_transform.xform(Vector2((screen_size.width / 2) - ((screen_size.width / 2) * drag_margin[SIDE_LEFT]), (screen_size.height / 2) + ((screen_size.height / 2) * drag_margin[SIDE_BOTTOM])))
  370. };
  371. Transform2D inv_transform = get_global_transform().affine_inverse(); // Undo global space.
  372. for (int i = 0; i < 4; i++) {
  373. draw_line(inv_transform.xform(margin_endpoints[i]), inv_transform.xform(margin_endpoints[(i + 1) % 4]), margin_drawing_color, margin_drawing_width);
  374. }
  375. }
  376. } break;
  377. #endif
  378. }
  379. }
  380. void Camera2D::set_offset(const Vector2 &p_offset) {
  381. if (offset == p_offset) {
  382. return;
  383. }
  384. offset = p_offset;
  385. Point2 old_smoothed_camera_pos = smoothed_camera_pos;
  386. _update_scroll();
  387. smoothed_camera_pos = old_smoothed_camera_pos;
  388. }
  389. Vector2 Camera2D::get_offset() const {
  390. return offset;
  391. }
  392. void Camera2D::set_anchor_mode(AnchorMode p_anchor_mode) {
  393. if (anchor_mode == p_anchor_mode) {
  394. return;
  395. }
  396. anchor_mode = p_anchor_mode;
  397. _update_scroll();
  398. }
  399. Camera2D::AnchorMode Camera2D::get_anchor_mode() const {
  400. return anchor_mode;
  401. }
  402. void Camera2D::set_ignore_rotation(bool p_ignore) {
  403. if (ignore_rotation == p_ignore) {
  404. return;
  405. }
  406. ignore_rotation = p_ignore;
  407. Point2 old_smoothed_camera_pos = smoothed_camera_pos;
  408. // Reset back to zero so it matches the camera rotation when ignore_rotation is enabled.
  409. if (ignore_rotation) {
  410. camera_angle = 0.0;
  411. }
  412. _update_scroll();
  413. smoothed_camera_pos = old_smoothed_camera_pos;
  414. }
  415. bool Camera2D::is_ignoring_rotation() const {
  416. return ignore_rotation;
  417. }
  418. void Camera2D::set_limit_enabled(bool p_limit_enabled) {
  419. if (limit_enabled == p_limit_enabled) {
  420. return;
  421. }
  422. limit_enabled = p_limit_enabled;
  423. _update_scroll();
  424. }
  425. bool Camera2D::is_limit_enabled() const {
  426. return limit_enabled;
  427. }
  428. void Camera2D::set_process_callback(Camera2DProcessCallback p_mode) {
  429. if (process_callback == p_mode) {
  430. return;
  431. }
  432. process_callback = p_mode;
  433. _update_process_callback();
  434. }
  435. void Camera2D::set_enabled(bool p_enabled) {
  436. if (enabled == p_enabled) {
  437. return;
  438. }
  439. enabled = p_enabled;
  440. if (!is_inside_tree()) {
  441. return;
  442. }
  443. if (enabled && !viewport->get_camera_2d()) {
  444. make_current();
  445. } else if (!enabled && is_current()) {
  446. clear_current();
  447. }
  448. }
  449. bool Camera2D::is_enabled() const {
  450. return enabled;
  451. }
  452. Camera2D::Camera2DProcessCallback Camera2D::get_process_callback() const {
  453. return process_callback;
  454. }
  455. void Camera2D::_make_current(Object *p_which) {
  456. if (!is_inside_tree() || !viewport) {
  457. return;
  458. }
  459. if (custom_viewport && !ObjectDB::get_instance(custom_viewport_id)) {
  460. return;
  461. }
  462. queue_redraw();
  463. bool was_current = viewport->get_camera_2d() == this;
  464. bool is_current = p_which == this;
  465. if (is_current) {
  466. viewport->_camera_2d_set(this);
  467. } else if (was_current) {
  468. viewport->_camera_2d_set(nullptr);
  469. }
  470. if (is_current != was_current) {
  471. _update_process_callback();
  472. }
  473. }
  474. void Camera2D::set_limit_rect(const Rect2i &p_limit_rect) {
  475. const Point2i limit_rect_end = p_limit_rect.get_end();
  476. set_limit(SIDE_LEFT, p_limit_rect.position.x);
  477. set_limit(SIDE_TOP, p_limit_rect.position.y);
  478. set_limit(SIDE_RIGHT, limit_rect_end.x);
  479. set_limit(SIDE_BOTTOM, limit_rect_end.y);
  480. }
  481. Rect2i Camera2D::get_limit_rect() const {
  482. return Rect2i(limit[SIDE_LEFT], limit[SIDE_TOP], limit[SIDE_RIGHT] - limit[SIDE_LEFT], limit[SIDE_BOTTOM] - limit[SIDE_TOP]);
  483. }
  484. void Camera2D::make_current() {
  485. ERR_FAIL_COND(!enabled || !is_inside_tree());
  486. get_tree()->call_group(group_name, "_make_current", this);
  487. if (just_exited_tree) {
  488. // If camera exited the scene tree in the same frame, group call will skip it, so this needs to be called manually.
  489. _make_current(this);
  490. }
  491. _update_scroll();
  492. _update_process_callback();
  493. }
  494. void Camera2D::clear_current() {
  495. ERR_FAIL_COND(!is_current());
  496. if (!viewport || !viewport->is_inside_tree()) {
  497. return;
  498. }
  499. if (!custom_viewport || ObjectDB::get_instance(custom_viewport_id)) {
  500. viewport->assign_next_enabled_camera_2d(group_name);
  501. }
  502. _update_process_callback();
  503. }
  504. bool Camera2D::is_current() const {
  505. if (!viewport) {
  506. return false;
  507. }
  508. if (!custom_viewport || ObjectDB::get_instance(custom_viewport_id)) {
  509. return viewport->get_camera_2d() == this;
  510. }
  511. return false;
  512. }
  513. void Camera2D::set_limit(Side p_side, int p_limit) {
  514. ERR_FAIL_INDEX((int)p_side, 4);
  515. if (limit[p_side] == p_limit) {
  516. return;
  517. }
  518. limit[p_side] = p_limit;
  519. Point2 old_smoothed_camera_pos = smoothed_camera_pos;
  520. _update_scroll();
  521. smoothed_camera_pos = old_smoothed_camera_pos;
  522. }
  523. int Camera2D::get_limit(Side p_side) const {
  524. ERR_FAIL_INDEX_V((int)p_side, 4, 0);
  525. return limit[p_side];
  526. }
  527. void Camera2D::set_limit_smoothing_enabled(bool p_enabled) {
  528. if (limit_smoothing_enabled == p_enabled) {
  529. return;
  530. }
  531. limit_smoothing_enabled = p_enabled;
  532. _update_scroll();
  533. }
  534. bool Camera2D::is_limit_smoothing_enabled() const {
  535. return limit_smoothing_enabled;
  536. }
  537. void Camera2D::set_drag_margin(Side p_side, real_t p_drag_margin) {
  538. ERR_FAIL_INDEX((int)p_side, 4);
  539. if (drag_margin[p_side] == p_drag_margin) {
  540. return;
  541. }
  542. drag_margin[p_side] = p_drag_margin;
  543. queue_redraw();
  544. }
  545. real_t Camera2D::get_drag_margin(Side p_side) const {
  546. ERR_FAIL_INDEX_V((int)p_side, 4, 0);
  547. return drag_margin[p_side];
  548. }
  549. Vector2 Camera2D::get_camera_position() const {
  550. return camera_pos;
  551. }
  552. void Camera2D::force_update_scroll() {
  553. _update_scroll();
  554. }
  555. void Camera2D::reset_smoothing() {
  556. _update_scroll();
  557. smoothed_camera_pos = camera_pos;
  558. }
  559. void Camera2D::align() {
  560. ERR_FAIL_COND(custom_viewport && !ObjectDB::get_instance(custom_viewport_id));
  561. Size2 screen_size = _get_camera_screen_size();
  562. Point2 current_camera_pos = get_global_position();
  563. if (anchor_mode == ANCHOR_MODE_DRAG_CENTER) {
  564. if (drag_horizontal_offset < 0) {
  565. camera_pos.x = current_camera_pos.x + screen_size.x * 0.5 * drag_margin[SIDE_RIGHT] * drag_horizontal_offset;
  566. } else {
  567. camera_pos.x = current_camera_pos.x + screen_size.x * 0.5 * drag_margin[SIDE_LEFT] * drag_horizontal_offset;
  568. }
  569. if (drag_vertical_offset < 0) {
  570. camera_pos.y = current_camera_pos.y + screen_size.y * 0.5 * drag_margin[SIDE_TOP] * drag_vertical_offset;
  571. } else {
  572. camera_pos.y = current_camera_pos.y + screen_size.y * 0.5 * drag_margin[SIDE_BOTTOM] * drag_vertical_offset;
  573. }
  574. } else if (anchor_mode == ANCHOR_MODE_FIXED_TOP_LEFT) {
  575. camera_pos = current_camera_pos;
  576. }
  577. _update_scroll();
  578. }
  579. void Camera2D::set_position_smoothing_speed(real_t p_speed) {
  580. if (position_smoothing_speed == p_speed) {
  581. return;
  582. }
  583. position_smoothing_speed = MAX(0, p_speed);
  584. _update_process_callback();
  585. }
  586. real_t Camera2D::get_position_smoothing_speed() const {
  587. return position_smoothing_speed;
  588. }
  589. void Camera2D::set_rotation_smoothing_speed(real_t p_speed) {
  590. if (rotation_smoothing_speed == p_speed) {
  591. return;
  592. }
  593. rotation_smoothing_speed = MAX(0, p_speed);
  594. _update_process_callback();
  595. }
  596. real_t Camera2D::get_rotation_smoothing_speed() const {
  597. return rotation_smoothing_speed;
  598. }
  599. void Camera2D::set_rotation_smoothing_enabled(bool p_enabled) {
  600. if (rotation_smoothing_enabled == p_enabled) {
  601. return;
  602. }
  603. rotation_smoothing_enabled = p_enabled;
  604. }
  605. bool Camera2D::is_rotation_smoothing_enabled() const {
  606. return rotation_smoothing_enabled;
  607. }
  608. Point2 Camera2D::get_camera_screen_center() const {
  609. return camera_screen_center;
  610. }
  611. real_t Camera2D::get_screen_rotation() const {
  612. return camera_angle;
  613. }
  614. Size2 Camera2D::_get_camera_screen_size() const {
  615. if (is_part_of_edited_scene()) {
  616. return Size2(GLOBAL_GET_CACHED(real_t, "display/window/size/viewport_width"), GLOBAL_GET_CACHED(real_t, "display/window/size/viewport_height"));
  617. }
  618. return get_viewport_rect().size;
  619. }
  620. void Camera2D::set_drag_horizontal_enabled(bool p_enabled) {
  621. drag_horizontal_enabled = p_enabled;
  622. }
  623. bool Camera2D::is_drag_horizontal_enabled() const {
  624. return drag_horizontal_enabled;
  625. }
  626. void Camera2D::set_drag_vertical_enabled(bool p_enabled) {
  627. drag_vertical_enabled = p_enabled;
  628. }
  629. bool Camera2D::is_drag_vertical_enabled() const {
  630. return drag_vertical_enabled;
  631. }
  632. void Camera2D::set_drag_vertical_offset(real_t p_offset) {
  633. if (drag_vertical_offset == p_offset) {
  634. return;
  635. }
  636. drag_vertical_offset = p_offset;
  637. drag_vertical_offset_changed = true;
  638. Point2 old_smoothed_camera_pos = smoothed_camera_pos;
  639. _update_scroll();
  640. smoothed_camera_pos = old_smoothed_camera_pos;
  641. }
  642. real_t Camera2D::get_drag_vertical_offset() const {
  643. return drag_vertical_offset;
  644. }
  645. void Camera2D::set_drag_horizontal_offset(real_t p_offset) {
  646. if (drag_horizontal_offset == p_offset) {
  647. return;
  648. }
  649. drag_horizontal_offset = p_offset;
  650. drag_horizontal_offset_changed = true;
  651. Point2 old_smoothed_camera_pos = smoothed_camera_pos;
  652. _update_scroll();
  653. smoothed_camera_pos = old_smoothed_camera_pos;
  654. }
  655. real_t Camera2D::get_drag_horizontal_offset() const {
  656. return drag_horizontal_offset;
  657. }
  658. void Camera2D::set_position_smoothing_enabled(bool p_enabled) {
  659. if (position_smoothing_enabled == p_enabled) {
  660. return;
  661. }
  662. position_smoothing_enabled = p_enabled;
  663. }
  664. bool Camera2D::is_position_smoothing_enabled() const {
  665. return position_smoothing_enabled;
  666. }
  667. void Camera2D::set_custom_viewport(Node *p_viewport) {
  668. ERR_FAIL_NULL(p_viewport);
  669. if (custom_viewport == p_viewport) {
  670. return;
  671. }
  672. if (is_inside_tree()) {
  673. remove_from_group(group_name);
  674. remove_from_group(canvas_group_name);
  675. }
  676. custom_viewport = Object::cast_to<Viewport>(p_viewport);
  677. if (custom_viewport) {
  678. custom_viewport_id = custom_viewport->get_instance_id();
  679. } else {
  680. custom_viewport_id = ObjectID();
  681. }
  682. if (is_inside_tree()) {
  683. if (custom_viewport) {
  684. viewport = custom_viewport;
  685. } else {
  686. viewport = get_viewport();
  687. }
  688. RID vp = viewport->get_viewport_rid();
  689. group_name = "__cameras_" + itos(vp.get_id());
  690. canvas_group_name = "__cameras_c" + itos(canvas.get_id());
  691. add_to_group(group_name);
  692. add_to_group(canvas_group_name);
  693. }
  694. }
  695. Node *Camera2D::get_custom_viewport() const {
  696. return custom_viewport;
  697. }
  698. void Camera2D::set_screen_drawing_enabled(bool p_enabled) {
  699. screen_drawing_enabled = p_enabled;
  700. #ifdef TOOLS_ENABLED
  701. queue_redraw();
  702. #endif
  703. }
  704. bool Camera2D::is_screen_drawing_enabled() const {
  705. return screen_drawing_enabled;
  706. }
  707. void Camera2D::set_limit_drawing_enabled(bool p_enabled) {
  708. limit_drawing_enabled = p_enabled;
  709. #ifdef TOOLS_ENABLED
  710. queue_redraw();
  711. #endif
  712. }
  713. bool Camera2D::is_limit_drawing_enabled() const {
  714. return limit_drawing_enabled;
  715. }
  716. void Camera2D::set_margin_drawing_enabled(bool p_enabled) {
  717. margin_drawing_enabled = p_enabled;
  718. #ifdef TOOLS_ENABLED
  719. queue_redraw();
  720. #endif
  721. }
  722. bool Camera2D::is_margin_drawing_enabled() const {
  723. return margin_drawing_enabled;
  724. }
  725. void Camera2D::_bind_methods() {
  726. ClassDB::bind_method(D_METHOD("set_offset", "offset"), &Camera2D::set_offset);
  727. ClassDB::bind_method(D_METHOD("get_offset"), &Camera2D::get_offset);
  728. ClassDB::bind_method(D_METHOD("set_anchor_mode", "anchor_mode"), &Camera2D::set_anchor_mode);
  729. ClassDB::bind_method(D_METHOD("get_anchor_mode"), &Camera2D::get_anchor_mode);
  730. ClassDB::bind_method(D_METHOD("set_ignore_rotation", "ignore"), &Camera2D::set_ignore_rotation);
  731. ClassDB::bind_method(D_METHOD("is_ignoring_rotation"), &Camera2D::is_ignoring_rotation);
  732. ClassDB::bind_method(D_METHOD("_update_scroll"), &Camera2D::_update_scroll);
  733. ClassDB::bind_method(D_METHOD("set_process_callback", "mode"), &Camera2D::set_process_callback);
  734. ClassDB::bind_method(D_METHOD("get_process_callback"), &Camera2D::get_process_callback);
  735. ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &Camera2D::set_enabled);
  736. ClassDB::bind_method(D_METHOD("is_enabled"), &Camera2D::is_enabled);
  737. ClassDB::bind_method(D_METHOD("make_current"), &Camera2D::make_current);
  738. ClassDB::bind_method(D_METHOD("is_current"), &Camera2D::is_current);
  739. ClassDB::bind_method(D_METHOD("_make_current"), &Camera2D::_make_current);
  740. ClassDB::bind_method(D_METHOD("set_limit_enabled", "limit_enabled"), &Camera2D::set_limit_enabled);
  741. ClassDB::bind_method(D_METHOD("is_limit_enabled"), &Camera2D::is_limit_enabled);
  742. ClassDB::bind_method(D_METHOD("set_limit", "margin", "limit"), &Camera2D::set_limit);
  743. ClassDB::bind_method(D_METHOD("get_limit", "margin"), &Camera2D::get_limit);
  744. ClassDB::bind_method(D_METHOD("_set_limit_rect", "rect"), &Camera2D::set_limit_rect);
  745. ClassDB::bind_method(D_METHOD("set_limit_smoothing_enabled", "limit_smoothing_enabled"), &Camera2D::set_limit_smoothing_enabled);
  746. ClassDB::bind_method(D_METHOD("is_limit_smoothing_enabled"), &Camera2D::is_limit_smoothing_enabled);
  747. ClassDB::bind_method(D_METHOD("set_drag_vertical_enabled", "enabled"), &Camera2D::set_drag_vertical_enabled);
  748. ClassDB::bind_method(D_METHOD("is_drag_vertical_enabled"), &Camera2D::is_drag_vertical_enabled);
  749. ClassDB::bind_method(D_METHOD("set_drag_horizontal_enabled", "enabled"), &Camera2D::set_drag_horizontal_enabled);
  750. ClassDB::bind_method(D_METHOD("is_drag_horizontal_enabled"), &Camera2D::is_drag_horizontal_enabled);
  751. ClassDB::bind_method(D_METHOD("set_drag_vertical_offset", "offset"), &Camera2D::set_drag_vertical_offset);
  752. ClassDB::bind_method(D_METHOD("get_drag_vertical_offset"), &Camera2D::get_drag_vertical_offset);
  753. ClassDB::bind_method(D_METHOD("set_drag_horizontal_offset", "offset"), &Camera2D::set_drag_horizontal_offset);
  754. ClassDB::bind_method(D_METHOD("get_drag_horizontal_offset"), &Camera2D::get_drag_horizontal_offset);
  755. ClassDB::bind_method(D_METHOD("set_drag_margin", "margin", "drag_margin"), &Camera2D::set_drag_margin);
  756. ClassDB::bind_method(D_METHOD("get_drag_margin", "margin"), &Camera2D::get_drag_margin);
  757. ClassDB::bind_method(D_METHOD("get_target_position"), &Camera2D::get_camera_position);
  758. ClassDB::bind_method(D_METHOD("get_screen_center_position"), &Camera2D::get_camera_screen_center);
  759. ClassDB::bind_method(D_METHOD("get_screen_rotation"), &Camera2D::get_screen_rotation);
  760. ClassDB::bind_method(D_METHOD("set_zoom", "zoom"), &Camera2D::set_zoom);
  761. ClassDB::bind_method(D_METHOD("get_zoom"), &Camera2D::get_zoom);
  762. ClassDB::bind_method(D_METHOD("set_custom_viewport", "viewport"), &Camera2D::set_custom_viewport);
  763. ClassDB::bind_method(D_METHOD("get_custom_viewport"), &Camera2D::get_custom_viewport);
  764. ClassDB::bind_method(D_METHOD("set_position_smoothing_speed", "position_smoothing_speed"), &Camera2D::set_position_smoothing_speed);
  765. ClassDB::bind_method(D_METHOD("get_position_smoothing_speed"), &Camera2D::get_position_smoothing_speed);
  766. ClassDB::bind_method(D_METHOD("set_position_smoothing_enabled", "position_smoothing_speed"), &Camera2D::set_position_smoothing_enabled);
  767. ClassDB::bind_method(D_METHOD("is_position_smoothing_enabled"), &Camera2D::is_position_smoothing_enabled);
  768. ClassDB::bind_method(D_METHOD("set_rotation_smoothing_enabled", "enabled"), &Camera2D::set_rotation_smoothing_enabled);
  769. ClassDB::bind_method(D_METHOD("is_rotation_smoothing_enabled"), &Camera2D::is_rotation_smoothing_enabled);
  770. ClassDB::bind_method(D_METHOD("set_rotation_smoothing_speed", "speed"), &Camera2D::set_rotation_smoothing_speed);
  771. ClassDB::bind_method(D_METHOD("get_rotation_smoothing_speed"), &Camera2D::get_rotation_smoothing_speed);
  772. ClassDB::bind_method(D_METHOD("force_update_scroll"), &Camera2D::force_update_scroll);
  773. ClassDB::bind_method(D_METHOD("reset_smoothing"), &Camera2D::reset_smoothing);
  774. ClassDB::bind_method(D_METHOD("align"), &Camera2D::align);
  775. ClassDB::bind_method(D_METHOD("set_screen_drawing_enabled", "screen_drawing_enabled"), &Camera2D::set_screen_drawing_enabled);
  776. ClassDB::bind_method(D_METHOD("is_screen_drawing_enabled"), &Camera2D::is_screen_drawing_enabled);
  777. ClassDB::bind_method(D_METHOD("set_limit_drawing_enabled", "limit_drawing_enabled"), &Camera2D::set_limit_drawing_enabled);
  778. ClassDB::bind_method(D_METHOD("is_limit_drawing_enabled"), &Camera2D::is_limit_drawing_enabled);
  779. ClassDB::bind_method(D_METHOD("set_margin_drawing_enabled", "margin_drawing_enabled"), &Camera2D::set_margin_drawing_enabled);
  780. ClassDB::bind_method(D_METHOD("is_margin_drawing_enabled"), &Camera2D::is_margin_drawing_enabled);
  781. ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset", PROPERTY_HINT_NONE, "suffix:px"), "set_offset", "get_offset");
  782. ADD_PROPERTY(PropertyInfo(Variant::INT, "anchor_mode", PROPERTY_HINT_ENUM, "Fixed Top Left,Drag Center"), "set_anchor_mode", "get_anchor_mode");
  783. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_rotation"), "set_ignore_rotation", "is_ignoring_rotation");
  784. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
  785. ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "zoom", PROPERTY_HINT_LINK), "set_zoom", "get_zoom");
  786. ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "custom_viewport", PROPERTY_HINT_RESOURCE_TYPE, "Viewport", PROPERTY_USAGE_NONE), "set_custom_viewport", "get_custom_viewport");
  787. ADD_PROPERTY(PropertyInfo(Variant::INT, "process_callback", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_process_callback", "get_process_callback");
  788. ADD_GROUP("Limit", "limit_");
  789. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "limit_enabled", PROPERTY_HINT_GROUP_ENABLE), "set_limit_enabled", "is_limit_enabled");
  790. ADD_PROPERTYI(PropertyInfo(Variant::INT, "limit_left", PROPERTY_HINT_NONE, "suffix:px"), "set_limit", "get_limit", SIDE_LEFT);
  791. ADD_PROPERTYI(PropertyInfo(Variant::INT, "limit_top", PROPERTY_HINT_NONE, "suffix:px"), "set_limit", "get_limit", SIDE_TOP);
  792. ADD_PROPERTYI(PropertyInfo(Variant::INT, "limit_right", PROPERTY_HINT_NONE, "suffix:px"), "set_limit", "get_limit", SIDE_RIGHT);
  793. ADD_PROPERTYI(PropertyInfo(Variant::INT, "limit_bottom", PROPERTY_HINT_NONE, "suffix:px"), "set_limit", "get_limit", SIDE_BOTTOM);
  794. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "limit_smoothed"), "set_limit_smoothing_enabled", "is_limit_smoothing_enabled");
  795. ADD_GROUP("Position Smoothing", "position_smoothing_");
  796. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "position_smoothing_enabled", PROPERTY_HINT_GROUP_ENABLE), "set_position_smoothing_enabled", "is_position_smoothing_enabled");
  797. ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "position_smoothing_speed", PROPERTY_HINT_NONE, "suffix:px/s"), "set_position_smoothing_speed", "get_position_smoothing_speed");
  798. ADD_GROUP("Rotation Smoothing", "rotation_smoothing_");
  799. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rotation_smoothing_enabled", PROPERTY_HINT_GROUP_ENABLE), "set_rotation_smoothing_enabled", "is_rotation_smoothing_enabled");
  800. ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation_smoothing_speed"), "set_rotation_smoothing_speed", "get_rotation_smoothing_speed");
  801. ADD_GROUP("Drag", "drag_");
  802. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_horizontal_enabled"), "set_drag_horizontal_enabled", "is_drag_horizontal_enabled");
  803. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_vertical_enabled"), "set_drag_vertical_enabled", "is_drag_vertical_enabled");
  804. ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "drag_horizontal_offset", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_drag_horizontal_offset", "get_drag_horizontal_offset");
  805. ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "drag_vertical_offset", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_drag_vertical_offset", "get_drag_vertical_offset");
  806. ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "drag_left_margin", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_margin", "get_drag_margin", SIDE_LEFT);
  807. ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "drag_top_margin", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_margin", "get_drag_margin", SIDE_TOP);
  808. ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "drag_right_margin", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_margin", "get_drag_margin", SIDE_RIGHT);
  809. ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "drag_bottom_margin", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_margin", "get_drag_margin", SIDE_BOTTOM);
  810. ADD_GROUP("Editor", "editor_");
  811. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_draw_screen"), "set_screen_drawing_enabled", "is_screen_drawing_enabled");
  812. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_draw_limits"), "set_limit_drawing_enabled", "is_limit_drawing_enabled");
  813. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_draw_drag_margin"), "set_margin_drawing_enabled", "is_margin_drawing_enabled");
  814. BIND_ENUM_CONSTANT(ANCHOR_MODE_FIXED_TOP_LEFT);
  815. BIND_ENUM_CONSTANT(ANCHOR_MODE_DRAG_CENTER);
  816. BIND_ENUM_CONSTANT(CAMERA2D_PROCESS_PHYSICS);
  817. BIND_ENUM_CONSTANT(CAMERA2D_PROCESS_IDLE);
  818. }
  819. Camera2D::Camera2D() {
  820. set_notify_transform(true);
  821. set_hide_clip_children(true);
  822. }