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