camera_2d.cpp 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008
  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 (limit[SIDE_LEFT] > limit[SIDE_RIGHT] - screen_rect.size.x) {
  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 (limit[SIDE_TOP] > limit[SIDE_BOTTOM] - screen_rect.size.y) {
  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 (limit[SIDE_LEFT] > limit[SIDE_RIGHT] - (bottom_right_corner.x - screen_rect.position.x)) {
  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 (limit[SIDE_TOP] > limit[SIDE_BOTTOM] - (bottom_right_corner.y - screen_rect.position.y)) {
  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. if (_interpolation_data.accepting_resets) {
  265. // Force the limits etc. to update.
  266. _interpolation_data.xform_curr = get_camera_transform();
  267. _interpolation_data.xform_prev = _interpolation_data.xform_curr;
  268. _update_process_callback();
  269. }
  270. } break;
  271. case NOTIFICATION_SUSPENDED:
  272. case NOTIFICATION_PAUSED: {
  273. if (is_physics_interpolated_and_enabled()) {
  274. _update_scroll();
  275. }
  276. } break;
  277. case NOTIFICATION_TRANSFORM_CHANGED: {
  278. if ((!position_smoothing_enabled && !is_physics_interpolated_and_enabled()) || is_part_of_edited_scene()) {
  279. _update_scroll();
  280. }
  281. if (is_physics_interpolated_and_enabled()) {
  282. _ensure_update_interpolation_data();
  283. if (Engine::get_singleton()->is_in_physics_frame()) {
  284. _interpolation_data.xform_curr = get_camera_transform();
  285. }
  286. }
  287. } break;
  288. case NOTIFICATION_ENTER_TREE: {
  289. ERR_FAIL_COND(!is_inside_tree());
  290. if (custom_viewport && ObjectDB::get_instance(custom_viewport_id)) {
  291. viewport = custom_viewport;
  292. } else {
  293. viewport = get_viewport();
  294. }
  295. canvas = get_canvas();
  296. RID vp = viewport->get_viewport_rid();
  297. group_name = "__cameras_" + itos(vp.get_id());
  298. canvas_group_name = "__cameras_c" + itos(canvas.get_id());
  299. add_to_group(group_name);
  300. add_to_group(canvas_group_name);
  301. if (!is_part_of_edited_scene() && enabled && !viewport->get_camera_2d()) {
  302. make_current();
  303. }
  304. _update_process_callback();
  305. first = true;
  306. _update_scroll();
  307. // Note that NOTIFICATION_RESET_PHYSICS_INTERPOLATION
  308. // is automatically called before this because Camera2D is inherited
  309. // from CanvasItem. However, the camera transform is not up to date
  310. // until this point, so we do an extra manual reset.
  311. if (is_physics_interpolated_and_enabled()) {
  312. _interpolation_data.xform_curr = get_camera_transform();
  313. _interpolation_data.xform_prev = _interpolation_data.xform_curr;
  314. }
  315. _interpolation_data.accepting_resets = true;
  316. } break;
  317. case NOTIFICATION_EXIT_TREE: {
  318. remove_from_group(group_name);
  319. remove_from_group(canvas_group_name);
  320. if (is_current()) {
  321. clear_current();
  322. }
  323. viewport = nullptr;
  324. just_exited_tree = true;
  325. _interpolation_data.accepting_resets = false;
  326. callable_mp(this, &Camera2D::_reset_just_exited).call_deferred();
  327. } break;
  328. #ifdef TOOLS_ENABLED
  329. case NOTIFICATION_DRAW: {
  330. if (!is_inside_tree() || !is_part_of_edited_scene()) {
  331. break;
  332. }
  333. if (screen_drawing_enabled) {
  334. Color area_axis_color(1, 0.4, 1, 0.63);
  335. real_t area_axis_width = -1;
  336. if (is_current()) {
  337. area_axis_width = 3;
  338. }
  339. Transform2D inv_camera_transform = get_camera_transform().affine_inverse();
  340. Size2 screen_size = _get_camera_screen_size();
  341. Vector2 screen_endpoints[4] = {
  342. inv_camera_transform.xform(Vector2(0, 0)),
  343. inv_camera_transform.xform(Vector2(screen_size.width, 0)),
  344. inv_camera_transform.xform(Vector2(screen_size.width, screen_size.height)),
  345. inv_camera_transform.xform(Vector2(0, screen_size.height))
  346. };
  347. Transform2D inv_transform = get_global_transform().affine_inverse(); // Undo global space.
  348. for (int i = 0; i < 4; i++) {
  349. draw_line(inv_transform.xform(screen_endpoints[i]), inv_transform.xform(screen_endpoints[(i + 1) % 4]), area_axis_color, area_axis_width);
  350. }
  351. }
  352. if (limit_enabled && limit_drawing_enabled) {
  353. real_t limit_drawing_width = -1;
  354. if (is_current()) {
  355. limit_drawing_width = 3;
  356. }
  357. draw_set_transform_matrix(get_global_transform().affine_inverse());
  358. draw_rect(get_limit_rect(), Color(1, 1, 0.25, 0.63), false, limit_drawing_width);
  359. draw_set_transform_matrix(Transform2D());
  360. }
  361. if (margin_drawing_enabled) {
  362. Color margin_drawing_color(0.25, 1, 1, 0.63);
  363. real_t margin_drawing_width = -1;
  364. if (is_current()) {
  365. margin_drawing_width = 3;
  366. }
  367. Transform2D inv_camera_transform = get_camera_transform().affine_inverse();
  368. Size2 screen_size = _get_camera_screen_size();
  369. Vector2 margin_endpoints[4] = {
  370. 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]))),
  371. 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]))),
  372. 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]))),
  373. 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])))
  374. };
  375. Transform2D inv_transform = get_global_transform().affine_inverse(); // Undo global space.
  376. for (int i = 0; i < 4; i++) {
  377. draw_line(inv_transform.xform(margin_endpoints[i]), inv_transform.xform(margin_endpoints[(i + 1) % 4]), margin_drawing_color, margin_drawing_width);
  378. }
  379. }
  380. } break;
  381. #endif
  382. }
  383. }
  384. void Camera2D::set_offset(const Vector2 &p_offset) {
  385. if (offset == p_offset) {
  386. return;
  387. }
  388. offset = p_offset;
  389. Point2 old_smoothed_camera_pos = smoothed_camera_pos;
  390. _update_scroll();
  391. smoothed_camera_pos = old_smoothed_camera_pos;
  392. }
  393. Vector2 Camera2D::get_offset() const {
  394. return offset;
  395. }
  396. void Camera2D::set_anchor_mode(AnchorMode p_anchor_mode) {
  397. if (anchor_mode == p_anchor_mode) {
  398. return;
  399. }
  400. anchor_mode = p_anchor_mode;
  401. _update_scroll();
  402. }
  403. Camera2D::AnchorMode Camera2D::get_anchor_mode() const {
  404. return anchor_mode;
  405. }
  406. void Camera2D::set_ignore_rotation(bool p_ignore) {
  407. if (ignore_rotation == p_ignore) {
  408. return;
  409. }
  410. ignore_rotation = p_ignore;
  411. Point2 old_smoothed_camera_pos = smoothed_camera_pos;
  412. // Reset back to zero so it matches the camera rotation when ignore_rotation is enabled.
  413. if (ignore_rotation) {
  414. camera_angle = 0.0;
  415. }
  416. _update_scroll();
  417. smoothed_camera_pos = old_smoothed_camera_pos;
  418. }
  419. bool Camera2D::is_ignoring_rotation() const {
  420. return ignore_rotation;
  421. }
  422. void Camera2D::set_limit_enabled(bool p_limit_enabled) {
  423. if (limit_enabled == p_limit_enabled) {
  424. return;
  425. }
  426. limit_enabled = p_limit_enabled;
  427. _update_scroll();
  428. }
  429. bool Camera2D::is_limit_enabled() const {
  430. return limit_enabled;
  431. }
  432. void Camera2D::set_process_callback(Camera2DProcessCallback p_mode) {
  433. if (process_callback == p_mode) {
  434. return;
  435. }
  436. process_callback = p_mode;
  437. _update_process_callback();
  438. }
  439. void Camera2D::set_enabled(bool p_enabled) {
  440. if (enabled == p_enabled) {
  441. return;
  442. }
  443. enabled = p_enabled;
  444. if (!is_inside_tree()) {
  445. return;
  446. }
  447. if (enabled && !viewport->get_camera_2d()) {
  448. make_current();
  449. } else if (!enabled && is_current()) {
  450. clear_current();
  451. }
  452. }
  453. bool Camera2D::is_enabled() const {
  454. return enabled;
  455. }
  456. Camera2D::Camera2DProcessCallback Camera2D::get_process_callback() const {
  457. return process_callback;
  458. }
  459. void Camera2D::_make_current(Object *p_which) {
  460. if (!is_inside_tree() || !viewport) {
  461. return;
  462. }
  463. if (custom_viewport && !ObjectDB::get_instance(custom_viewport_id)) {
  464. return;
  465. }
  466. queue_redraw();
  467. bool was_current = viewport->get_camera_2d() == this;
  468. bool is_current = p_which == this;
  469. if (is_current) {
  470. viewport->_camera_2d_set(this);
  471. } else if (was_current) {
  472. viewport->_camera_2d_set(nullptr);
  473. }
  474. if (is_current != was_current) {
  475. _update_process_callback();
  476. }
  477. }
  478. void Camera2D::set_limit_rect(const Rect2i &p_limit_rect) {
  479. const Point2i limit_rect_end = p_limit_rect.get_end();
  480. set_limit(SIDE_LEFT, p_limit_rect.position.x);
  481. set_limit(SIDE_TOP, p_limit_rect.position.y);
  482. set_limit(SIDE_RIGHT, limit_rect_end.x);
  483. set_limit(SIDE_BOTTOM, limit_rect_end.y);
  484. }
  485. Rect2i Camera2D::get_limit_rect() const {
  486. return Rect2i(limit[SIDE_LEFT], limit[SIDE_TOP], limit[SIDE_RIGHT] - limit[SIDE_LEFT], limit[SIDE_BOTTOM] - limit[SIDE_TOP]);
  487. }
  488. void Camera2D::make_current() {
  489. ERR_FAIL_COND(!enabled || !is_inside_tree());
  490. get_tree()->call_group(group_name, "_make_current", this);
  491. if (just_exited_tree) {
  492. // If camera exited the scene tree in the same frame, group call will skip it, so this needs to be called manually.
  493. _make_current(this);
  494. }
  495. _update_scroll();
  496. _update_process_callback();
  497. }
  498. void Camera2D::clear_current() {
  499. ERR_FAIL_COND(!is_current());
  500. if (!viewport || !viewport->is_inside_tree()) {
  501. return;
  502. }
  503. if (!custom_viewport || ObjectDB::get_instance(custom_viewport_id)) {
  504. viewport->assign_next_enabled_camera_2d(group_name);
  505. }
  506. _update_process_callback();
  507. }
  508. bool Camera2D::is_current() const {
  509. if (!viewport) {
  510. return false;
  511. }
  512. if (!custom_viewport || ObjectDB::get_instance(custom_viewport_id)) {
  513. return viewport->get_camera_2d() == this;
  514. }
  515. return false;
  516. }
  517. void Camera2D::set_limit(Side p_side, int p_limit) {
  518. ERR_FAIL_INDEX((int)p_side, 4);
  519. if (limit[p_side] == p_limit) {
  520. return;
  521. }
  522. limit[p_side] = p_limit;
  523. Point2 old_smoothed_camera_pos = smoothed_camera_pos;
  524. _update_scroll();
  525. smoothed_camera_pos = old_smoothed_camera_pos;
  526. }
  527. int Camera2D::get_limit(Side p_side) const {
  528. ERR_FAIL_INDEX_V((int)p_side, 4, 0);
  529. return limit[p_side];
  530. }
  531. void Camera2D::set_limit_smoothing_enabled(bool p_enabled) {
  532. if (limit_smoothing_enabled == p_enabled) {
  533. return;
  534. }
  535. limit_smoothing_enabled = p_enabled;
  536. _update_scroll();
  537. }
  538. bool Camera2D::is_limit_smoothing_enabled() const {
  539. return limit_smoothing_enabled;
  540. }
  541. void Camera2D::set_drag_margin(Side p_side, real_t p_drag_margin) {
  542. ERR_FAIL_INDEX((int)p_side, 4);
  543. if (drag_margin[p_side] == p_drag_margin) {
  544. return;
  545. }
  546. drag_margin[p_side] = p_drag_margin;
  547. queue_redraw();
  548. }
  549. real_t Camera2D::get_drag_margin(Side p_side) const {
  550. ERR_FAIL_INDEX_V((int)p_side, 4, 0);
  551. return drag_margin[p_side];
  552. }
  553. Vector2 Camera2D::get_camera_position() const {
  554. return camera_pos;
  555. }
  556. void Camera2D::force_update_scroll() {
  557. _update_scroll();
  558. }
  559. void Camera2D::reset_smoothing() {
  560. _update_scroll();
  561. smoothed_camera_pos = camera_pos;
  562. }
  563. void Camera2D::align() {
  564. ERR_FAIL_COND(custom_viewport && !ObjectDB::get_instance(custom_viewport_id));
  565. Size2 screen_size = _get_camera_screen_size();
  566. Point2 current_camera_pos = get_global_position();
  567. if (anchor_mode == ANCHOR_MODE_DRAG_CENTER) {
  568. if (drag_horizontal_offset < 0) {
  569. camera_pos.x = current_camera_pos.x + screen_size.x * 0.5 * drag_margin[SIDE_RIGHT] * drag_horizontal_offset;
  570. } else {
  571. camera_pos.x = current_camera_pos.x + screen_size.x * 0.5 * drag_margin[SIDE_LEFT] * drag_horizontal_offset;
  572. }
  573. if (drag_vertical_offset < 0) {
  574. camera_pos.y = current_camera_pos.y + screen_size.y * 0.5 * drag_margin[SIDE_TOP] * drag_vertical_offset;
  575. } else {
  576. camera_pos.y = current_camera_pos.y + screen_size.y * 0.5 * drag_margin[SIDE_BOTTOM] * drag_vertical_offset;
  577. }
  578. } else if (anchor_mode == ANCHOR_MODE_FIXED_TOP_LEFT) {
  579. camera_pos = current_camera_pos;
  580. }
  581. _update_scroll();
  582. }
  583. void Camera2D::set_position_smoothing_speed(real_t p_speed) {
  584. if (position_smoothing_speed == p_speed) {
  585. return;
  586. }
  587. position_smoothing_speed = MAX(0, p_speed);
  588. _update_process_callback();
  589. }
  590. real_t Camera2D::get_position_smoothing_speed() const {
  591. return position_smoothing_speed;
  592. }
  593. void Camera2D::set_rotation_smoothing_speed(real_t p_speed) {
  594. if (rotation_smoothing_speed == p_speed) {
  595. return;
  596. }
  597. rotation_smoothing_speed = MAX(0, p_speed);
  598. _update_process_callback();
  599. }
  600. real_t Camera2D::get_rotation_smoothing_speed() const {
  601. return rotation_smoothing_speed;
  602. }
  603. void Camera2D::set_rotation_smoothing_enabled(bool p_enabled) {
  604. if (rotation_smoothing_enabled == p_enabled) {
  605. return;
  606. }
  607. rotation_smoothing_enabled = p_enabled;
  608. }
  609. bool Camera2D::is_rotation_smoothing_enabled() const {
  610. return rotation_smoothing_enabled;
  611. }
  612. Point2 Camera2D::get_camera_screen_center() const {
  613. return camera_screen_center;
  614. }
  615. real_t Camera2D::get_screen_rotation() const {
  616. return camera_angle;
  617. }
  618. Size2 Camera2D::_get_camera_screen_size() const {
  619. if (is_part_of_edited_scene()) {
  620. return Size2(GLOBAL_GET_CACHED(real_t, "display/window/size/viewport_width"), GLOBAL_GET_CACHED(real_t, "display/window/size/viewport_height"));
  621. }
  622. ERR_FAIL_NULL_V(viewport, Size2());
  623. return viewport->get_visible_rect().size;
  624. }
  625. void Camera2D::set_drag_horizontal_enabled(bool p_enabled) {
  626. drag_horizontal_enabled = p_enabled;
  627. }
  628. bool Camera2D::is_drag_horizontal_enabled() const {
  629. return drag_horizontal_enabled;
  630. }
  631. void Camera2D::set_drag_vertical_enabled(bool p_enabled) {
  632. drag_vertical_enabled = p_enabled;
  633. }
  634. bool Camera2D::is_drag_vertical_enabled() const {
  635. return drag_vertical_enabled;
  636. }
  637. void Camera2D::set_drag_vertical_offset(real_t p_offset) {
  638. if (drag_vertical_offset == p_offset) {
  639. return;
  640. }
  641. drag_vertical_offset = p_offset;
  642. drag_vertical_offset_changed = true;
  643. Point2 old_smoothed_camera_pos = smoothed_camera_pos;
  644. _update_scroll();
  645. smoothed_camera_pos = old_smoothed_camera_pos;
  646. }
  647. real_t Camera2D::get_drag_vertical_offset() const {
  648. return drag_vertical_offset;
  649. }
  650. void Camera2D::set_drag_horizontal_offset(real_t p_offset) {
  651. if (drag_horizontal_offset == p_offset) {
  652. return;
  653. }
  654. drag_horizontal_offset = p_offset;
  655. drag_horizontal_offset_changed = true;
  656. Point2 old_smoothed_camera_pos = smoothed_camera_pos;
  657. _update_scroll();
  658. smoothed_camera_pos = old_smoothed_camera_pos;
  659. }
  660. real_t Camera2D::get_drag_horizontal_offset() const {
  661. return drag_horizontal_offset;
  662. }
  663. void Camera2D::set_position_smoothing_enabled(bool p_enabled) {
  664. if (position_smoothing_enabled == p_enabled) {
  665. return;
  666. }
  667. position_smoothing_enabled = p_enabled;
  668. }
  669. bool Camera2D::is_position_smoothing_enabled() const {
  670. return position_smoothing_enabled;
  671. }
  672. void Camera2D::set_custom_viewport(Node *p_viewport) {
  673. ERR_FAIL_NULL(p_viewport);
  674. if (custom_viewport == p_viewport) {
  675. return;
  676. }
  677. if (is_inside_tree()) {
  678. remove_from_group(group_name);
  679. remove_from_group(canvas_group_name);
  680. }
  681. custom_viewport = Object::cast_to<Viewport>(p_viewport);
  682. if (custom_viewport) {
  683. custom_viewport_id = custom_viewport->get_instance_id();
  684. } else {
  685. custom_viewport_id = ObjectID();
  686. }
  687. if (is_inside_tree()) {
  688. if (custom_viewport) {
  689. viewport = custom_viewport;
  690. } else {
  691. viewport = get_viewport();
  692. }
  693. RID vp = viewport->get_viewport_rid();
  694. group_name = "__cameras_" + itos(vp.get_id());
  695. canvas_group_name = "__cameras_c" + itos(canvas.get_id());
  696. add_to_group(group_name);
  697. add_to_group(canvas_group_name);
  698. }
  699. }
  700. Node *Camera2D::get_custom_viewport() const {
  701. return custom_viewport;
  702. }
  703. void Camera2D::set_screen_drawing_enabled(bool p_enabled) {
  704. screen_drawing_enabled = p_enabled;
  705. #ifdef TOOLS_ENABLED
  706. queue_redraw();
  707. #endif
  708. }
  709. bool Camera2D::is_screen_drawing_enabled() const {
  710. return screen_drawing_enabled;
  711. }
  712. void Camera2D::set_limit_drawing_enabled(bool p_enabled) {
  713. limit_drawing_enabled = p_enabled;
  714. #ifdef TOOLS_ENABLED
  715. queue_redraw();
  716. #endif
  717. }
  718. bool Camera2D::is_limit_drawing_enabled() const {
  719. return limit_drawing_enabled;
  720. }
  721. void Camera2D::set_margin_drawing_enabled(bool p_enabled) {
  722. margin_drawing_enabled = p_enabled;
  723. #ifdef TOOLS_ENABLED
  724. queue_redraw();
  725. #endif
  726. }
  727. bool Camera2D::is_margin_drawing_enabled() const {
  728. return margin_drawing_enabled;
  729. }
  730. void Camera2D::_bind_methods() {
  731. ClassDB::bind_method(D_METHOD("set_offset", "offset"), &Camera2D::set_offset);
  732. ClassDB::bind_method(D_METHOD("get_offset"), &Camera2D::get_offset);
  733. ClassDB::bind_method(D_METHOD("set_anchor_mode", "anchor_mode"), &Camera2D::set_anchor_mode);
  734. ClassDB::bind_method(D_METHOD("get_anchor_mode"), &Camera2D::get_anchor_mode);
  735. ClassDB::bind_method(D_METHOD("set_ignore_rotation", "ignore"), &Camera2D::set_ignore_rotation);
  736. ClassDB::bind_method(D_METHOD("is_ignoring_rotation"), &Camera2D::is_ignoring_rotation);
  737. ClassDB::bind_method(D_METHOD("_update_scroll"), &Camera2D::_update_scroll);
  738. ClassDB::bind_method(D_METHOD("set_process_callback", "mode"), &Camera2D::set_process_callback);
  739. ClassDB::bind_method(D_METHOD("get_process_callback"), &Camera2D::get_process_callback);
  740. ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &Camera2D::set_enabled);
  741. ClassDB::bind_method(D_METHOD("is_enabled"), &Camera2D::is_enabled);
  742. ClassDB::bind_method(D_METHOD("make_current"), &Camera2D::make_current);
  743. ClassDB::bind_method(D_METHOD("is_current"), &Camera2D::is_current);
  744. ClassDB::bind_method(D_METHOD("_make_current"), &Camera2D::_make_current);
  745. ClassDB::bind_method(D_METHOD("set_limit_enabled", "limit_enabled"), &Camera2D::set_limit_enabled);
  746. ClassDB::bind_method(D_METHOD("is_limit_enabled"), &Camera2D::is_limit_enabled);
  747. ClassDB::bind_method(D_METHOD("set_limit", "margin", "limit"), &Camera2D::set_limit);
  748. ClassDB::bind_method(D_METHOD("get_limit", "margin"), &Camera2D::get_limit);
  749. ClassDB::bind_method(D_METHOD("_set_limit_rect", "rect"), &Camera2D::set_limit_rect);
  750. ClassDB::bind_method(D_METHOD("set_limit_smoothing_enabled", "limit_smoothing_enabled"), &Camera2D::set_limit_smoothing_enabled);
  751. ClassDB::bind_method(D_METHOD("is_limit_smoothing_enabled"), &Camera2D::is_limit_smoothing_enabled);
  752. ClassDB::bind_method(D_METHOD("set_drag_vertical_enabled", "enabled"), &Camera2D::set_drag_vertical_enabled);
  753. ClassDB::bind_method(D_METHOD("is_drag_vertical_enabled"), &Camera2D::is_drag_vertical_enabled);
  754. ClassDB::bind_method(D_METHOD("set_drag_horizontal_enabled", "enabled"), &Camera2D::set_drag_horizontal_enabled);
  755. ClassDB::bind_method(D_METHOD("is_drag_horizontal_enabled"), &Camera2D::is_drag_horizontal_enabled);
  756. ClassDB::bind_method(D_METHOD("set_drag_vertical_offset", "offset"), &Camera2D::set_drag_vertical_offset);
  757. ClassDB::bind_method(D_METHOD("get_drag_vertical_offset"), &Camera2D::get_drag_vertical_offset);
  758. ClassDB::bind_method(D_METHOD("set_drag_horizontal_offset", "offset"), &Camera2D::set_drag_horizontal_offset);
  759. ClassDB::bind_method(D_METHOD("get_drag_horizontal_offset"), &Camera2D::get_drag_horizontal_offset);
  760. ClassDB::bind_method(D_METHOD("set_drag_margin", "margin", "drag_margin"), &Camera2D::set_drag_margin);
  761. ClassDB::bind_method(D_METHOD("get_drag_margin", "margin"), &Camera2D::get_drag_margin);
  762. ClassDB::bind_method(D_METHOD("get_target_position"), &Camera2D::get_camera_position);
  763. ClassDB::bind_method(D_METHOD("get_screen_center_position"), &Camera2D::get_camera_screen_center);
  764. ClassDB::bind_method(D_METHOD("get_screen_rotation"), &Camera2D::get_screen_rotation);
  765. ClassDB::bind_method(D_METHOD("set_zoom", "zoom"), &Camera2D::set_zoom);
  766. ClassDB::bind_method(D_METHOD("get_zoom"), &Camera2D::get_zoom);
  767. ClassDB::bind_method(D_METHOD("set_custom_viewport", "viewport"), &Camera2D::set_custom_viewport);
  768. ClassDB::bind_method(D_METHOD("get_custom_viewport"), &Camera2D::get_custom_viewport);
  769. ClassDB::bind_method(D_METHOD("set_position_smoothing_speed", "position_smoothing_speed"), &Camera2D::set_position_smoothing_speed);
  770. ClassDB::bind_method(D_METHOD("get_position_smoothing_speed"), &Camera2D::get_position_smoothing_speed);
  771. ClassDB::bind_method(D_METHOD("set_position_smoothing_enabled", "enabled"), &Camera2D::set_position_smoothing_enabled);
  772. ClassDB::bind_method(D_METHOD("is_position_smoothing_enabled"), &Camera2D::is_position_smoothing_enabled);
  773. ClassDB::bind_method(D_METHOD("set_rotation_smoothing_enabled", "enabled"), &Camera2D::set_rotation_smoothing_enabled);
  774. ClassDB::bind_method(D_METHOD("is_rotation_smoothing_enabled"), &Camera2D::is_rotation_smoothing_enabled);
  775. ClassDB::bind_method(D_METHOD("set_rotation_smoothing_speed", "speed"), &Camera2D::set_rotation_smoothing_speed);
  776. ClassDB::bind_method(D_METHOD("get_rotation_smoothing_speed"), &Camera2D::get_rotation_smoothing_speed);
  777. ClassDB::bind_method(D_METHOD("force_update_scroll"), &Camera2D::force_update_scroll);
  778. ClassDB::bind_method(D_METHOD("reset_smoothing"), &Camera2D::reset_smoothing);
  779. ClassDB::bind_method(D_METHOD("align"), &Camera2D::align);
  780. ClassDB::bind_method(D_METHOD("set_screen_drawing_enabled", "screen_drawing_enabled"), &Camera2D::set_screen_drawing_enabled);
  781. ClassDB::bind_method(D_METHOD("is_screen_drawing_enabled"), &Camera2D::is_screen_drawing_enabled);
  782. ClassDB::bind_method(D_METHOD("set_limit_drawing_enabled", "limit_drawing_enabled"), &Camera2D::set_limit_drawing_enabled);
  783. ClassDB::bind_method(D_METHOD("is_limit_drawing_enabled"), &Camera2D::is_limit_drawing_enabled);
  784. ClassDB::bind_method(D_METHOD("set_margin_drawing_enabled", "margin_drawing_enabled"), &Camera2D::set_margin_drawing_enabled);
  785. ClassDB::bind_method(D_METHOD("is_margin_drawing_enabled"), &Camera2D::is_margin_drawing_enabled);
  786. ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset", PROPERTY_HINT_NONE, "suffix:px"), "set_offset", "get_offset");
  787. ADD_PROPERTY(PropertyInfo(Variant::INT, "anchor_mode", PROPERTY_HINT_ENUM, "Fixed Top Left,Drag Center"), "set_anchor_mode", "get_anchor_mode");
  788. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_rotation"), "set_ignore_rotation", "is_ignoring_rotation");
  789. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
  790. ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "zoom", PROPERTY_HINT_LINK), "set_zoom", "get_zoom");
  791. ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "custom_viewport", PROPERTY_HINT_RESOURCE_TYPE, "Viewport", PROPERTY_USAGE_NONE), "set_custom_viewport", "get_custom_viewport");
  792. ADD_PROPERTY(PropertyInfo(Variant::INT, "process_callback", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_process_callback", "get_process_callback");
  793. ADD_GROUP("Limit", "limit_");
  794. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "limit_enabled", PROPERTY_HINT_GROUP_ENABLE), "set_limit_enabled", "is_limit_enabled");
  795. ADD_PROPERTYI(PropertyInfo(Variant::INT, "limit_left", PROPERTY_HINT_NONE, "suffix:px"), "set_limit", "get_limit", SIDE_LEFT);
  796. ADD_PROPERTYI(PropertyInfo(Variant::INT, "limit_top", PROPERTY_HINT_NONE, "suffix:px"), "set_limit", "get_limit", SIDE_TOP);
  797. ADD_PROPERTYI(PropertyInfo(Variant::INT, "limit_right", PROPERTY_HINT_NONE, "suffix:px"), "set_limit", "get_limit", SIDE_RIGHT);
  798. ADD_PROPERTYI(PropertyInfo(Variant::INT, "limit_bottom", PROPERTY_HINT_NONE, "suffix:px"), "set_limit", "get_limit", SIDE_BOTTOM);
  799. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "limit_smoothed"), "set_limit_smoothing_enabled", "is_limit_smoothing_enabled");
  800. ADD_GROUP("Position Smoothing", "position_smoothing_");
  801. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "position_smoothing_enabled", PROPERTY_HINT_GROUP_ENABLE), "set_position_smoothing_enabled", "is_position_smoothing_enabled");
  802. ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "position_smoothing_speed", PROPERTY_HINT_NONE, "suffix:px/s"), "set_position_smoothing_speed", "get_position_smoothing_speed");
  803. ADD_GROUP("Rotation Smoothing", "rotation_smoothing_");
  804. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rotation_smoothing_enabled", PROPERTY_HINT_GROUP_ENABLE), "set_rotation_smoothing_enabled", "is_rotation_smoothing_enabled");
  805. ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation_smoothing_speed"), "set_rotation_smoothing_speed", "get_rotation_smoothing_speed");
  806. ADD_GROUP("Drag", "drag_");
  807. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_horizontal_enabled"), "set_drag_horizontal_enabled", "is_drag_horizontal_enabled");
  808. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_vertical_enabled"), "set_drag_vertical_enabled", "is_drag_vertical_enabled");
  809. ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "drag_horizontal_offset", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_drag_horizontal_offset", "get_drag_horizontal_offset");
  810. ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "drag_vertical_offset", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_drag_vertical_offset", "get_drag_vertical_offset");
  811. ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "drag_left_margin", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_margin", "get_drag_margin", SIDE_LEFT);
  812. ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "drag_top_margin", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_margin", "get_drag_margin", SIDE_TOP);
  813. ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "drag_right_margin", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_margin", "get_drag_margin", SIDE_RIGHT);
  814. ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "drag_bottom_margin", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_margin", "get_drag_margin", SIDE_BOTTOM);
  815. ADD_GROUP("Editor", "editor_");
  816. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_draw_screen"), "set_screen_drawing_enabled", "is_screen_drawing_enabled");
  817. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_draw_limits"), "set_limit_drawing_enabled", "is_limit_drawing_enabled");
  818. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_draw_drag_margin"), "set_margin_drawing_enabled", "is_margin_drawing_enabled");
  819. BIND_ENUM_CONSTANT(ANCHOR_MODE_FIXED_TOP_LEFT);
  820. BIND_ENUM_CONSTANT(ANCHOR_MODE_DRAG_CENTER);
  821. BIND_ENUM_CONSTANT(CAMERA2D_PROCESS_PHYSICS);
  822. BIND_ENUM_CONSTANT(CAMERA2D_PROCESS_IDLE);
  823. }
  824. Camera2D::Camera2D() {
  825. set_notify_transform(true);
  826. set_hide_clip_children(true);
  827. }