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