Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/scene/2d/camera_2d.cpp
9896 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 (screen_rect.size.x > limit[SIDE_RIGHT] - limit[SIDE_LEFT]) {
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 (screen_rect.size.y > limit[SIDE_BOTTOM] - limit[SIDE_TOP]) {
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 (bottom_right_corner.x - screen_rect.position.x > limit[SIDE_RIGHT] - limit[SIDE_LEFT]) {
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 (bottom_right_corner.y - screen_rect.position.y > limit[SIDE_BOTTOM] - limit[SIDE_TOP]) {
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
// Force the limits etc. to update.
311
_interpolation_data.xform_curr = get_camera_transform();
312
_interpolation_data.xform_prev = _interpolation_data.xform_curr;
313
_update_process_callback();
314
} break;
315
316
case NOTIFICATION_SUSPENDED:
317
case NOTIFICATION_PAUSED: {
318
if (is_physics_interpolated_and_enabled()) {
319
_update_scroll();
320
}
321
} break;
322
323
case NOTIFICATION_TRANSFORM_CHANGED: {
324
if ((!position_smoothing_enabled && !is_physics_interpolated_and_enabled()) || is_part_of_edited_scene()) {
325
_update_scroll();
326
}
327
if (is_physics_interpolated_and_enabled()) {
328
_ensure_update_interpolation_data();
329
if (Engine::get_singleton()->is_in_physics_frame()) {
330
_interpolation_data.xform_curr = get_camera_transform();
331
}
332
}
333
} break;
334
335
case NOTIFICATION_ENTER_TREE: {
336
ERR_FAIL_COND(!is_inside_tree());
337
if (custom_viewport && ObjectDB::get_instance(custom_viewport_id)) {
338
viewport = custom_viewport;
339
} else {
340
viewport = get_viewport();
341
}
342
343
canvas = get_canvas();
344
345
RID vp = viewport->get_viewport_rid();
346
347
group_name = "__cameras_" + itos(vp.get_id());
348
canvas_group_name = "__cameras_c" + itos(canvas.get_id());
349
add_to_group(group_name);
350
add_to_group(canvas_group_name);
351
352
if (!is_part_of_edited_scene() && enabled && !viewport->get_camera_2d()) {
353
make_current();
354
}
355
356
_update_process_callback();
357
first = true;
358
_update_scroll();
359
360
// Note that NOTIFICATION_RESET_PHYSICS_INTERPOLATION
361
// is automatically called before this because Camera2D is inherited
362
// from CanvasItem. However, the camera transform is not up to date
363
// until this point, so we do an extra manual reset.
364
if (is_physics_interpolated_and_enabled()) {
365
_interpolation_data.xform_curr = get_camera_transform();
366
_interpolation_data.xform_prev = _interpolation_data.xform_curr;
367
}
368
} break;
369
370
case NOTIFICATION_EXIT_TREE: {
371
remove_from_group(group_name);
372
remove_from_group(canvas_group_name);
373
if (is_current()) {
374
clear_current();
375
}
376
viewport = nullptr;
377
just_exited_tree = true;
378
callable_mp(this, &Camera2D::_reset_just_exited).call_deferred();
379
} break;
380
381
#ifdef TOOLS_ENABLED
382
case NOTIFICATION_DRAW: {
383
if (!is_inside_tree() || !is_part_of_edited_scene()) {
384
break;
385
}
386
387
if (screen_drawing_enabled) {
388
Color area_axis_color(1, 0.4, 1, 0.63);
389
real_t area_axis_width = -1;
390
if (is_current()) {
391
area_axis_width = 3;
392
}
393
394
Transform2D inv_camera_transform = get_camera_transform().affine_inverse();
395
Size2 screen_size = _get_camera_screen_size();
396
397
Vector2 screen_endpoints[4] = {
398
inv_camera_transform.xform(Vector2(0, 0)),
399
inv_camera_transform.xform(Vector2(screen_size.width, 0)),
400
inv_camera_transform.xform(Vector2(screen_size.width, screen_size.height)),
401
inv_camera_transform.xform(Vector2(0, screen_size.height))
402
};
403
404
Transform2D inv_transform = get_global_transform().affine_inverse(); // Undo global space.
405
406
for (int i = 0; i < 4; i++) {
407
draw_line(inv_transform.xform(screen_endpoints[i]), inv_transform.xform(screen_endpoints[(i + 1) % 4]), area_axis_color, area_axis_width);
408
}
409
}
410
411
if (limit_enabled && limit_drawing_enabled) {
412
real_t limit_drawing_width = -1;
413
if (is_current()) {
414
limit_drawing_width = 3;
415
}
416
417
draw_set_transform_matrix(get_global_transform().affine_inverse());
418
draw_rect(get_limit_rect(), Color(1, 1, 0.25, 0.63), false, limit_drawing_width);
419
draw_set_transform_matrix(Transform2D());
420
}
421
422
if (margin_drawing_enabled) {
423
Color margin_drawing_color(0.25, 1, 1, 0.63);
424
real_t margin_drawing_width = -1;
425
if (is_current()) {
426
margin_drawing_width = 3;
427
}
428
429
Transform2D inv_camera_transform = get_camera_transform().affine_inverse();
430
Size2 screen_size = _get_camera_screen_size();
431
432
Vector2 margin_endpoints[4] = {
433
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]))),
434
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]))),
435
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]))),
436
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])))
437
};
438
439
Transform2D inv_transform = get_global_transform().affine_inverse(); // Undo global space.
440
441
for (int i = 0; i < 4; i++) {
442
draw_line(inv_transform.xform(margin_endpoints[i]), inv_transform.xform(margin_endpoints[(i + 1) % 4]), margin_drawing_color, margin_drawing_width);
443
}
444
}
445
} break;
446
#endif
447
}
448
}
449
450
void Camera2D::set_offset(const Vector2 &p_offset) {
451
if (offset == p_offset) {
452
return;
453
}
454
offset = p_offset;
455
Point2 old_smoothed_camera_pos = smoothed_camera_pos;
456
_update_scroll();
457
smoothed_camera_pos = old_smoothed_camera_pos;
458
}
459
460
Vector2 Camera2D::get_offset() const {
461
return offset;
462
}
463
464
void Camera2D::set_anchor_mode(AnchorMode p_anchor_mode) {
465
if (anchor_mode == p_anchor_mode) {
466
return;
467
}
468
anchor_mode = p_anchor_mode;
469
_update_scroll();
470
}
471
472
Camera2D::AnchorMode Camera2D::get_anchor_mode() const {
473
return anchor_mode;
474
}
475
476
void Camera2D::set_ignore_rotation(bool p_ignore) {
477
if (ignore_rotation == p_ignore) {
478
return;
479
}
480
ignore_rotation = p_ignore;
481
Point2 old_smoothed_camera_pos = smoothed_camera_pos;
482
483
// Reset back to zero so it matches the camera rotation when ignore_rotation is enabled.
484
if (ignore_rotation) {
485
camera_angle = 0.0;
486
}
487
488
_update_scroll();
489
smoothed_camera_pos = old_smoothed_camera_pos;
490
}
491
492
bool Camera2D::is_ignoring_rotation() const {
493
return ignore_rotation;
494
}
495
496
void Camera2D::set_limit_enabled(bool p_limit_enabled) {
497
if (limit_enabled == p_limit_enabled) {
498
return;
499
}
500
limit_enabled = p_limit_enabled;
501
_update_scroll();
502
}
503
504
bool Camera2D::is_limit_enabled() const {
505
return limit_enabled;
506
}
507
508
void Camera2D::set_process_callback(Camera2DProcessCallback p_mode) {
509
if (process_callback == p_mode) {
510
return;
511
}
512
513
process_callback = p_mode;
514
_update_process_callback();
515
}
516
517
void Camera2D::set_enabled(bool p_enabled) {
518
if (enabled == p_enabled) {
519
return;
520
}
521
enabled = p_enabled;
522
523
if (!is_inside_tree()) {
524
return;
525
}
526
527
if (enabled && !viewport->get_camera_2d()) {
528
make_current();
529
} else if (!enabled && is_current()) {
530
clear_current();
531
}
532
}
533
534
bool Camera2D::is_enabled() const {
535
return enabled;
536
}
537
538
Camera2D::Camera2DProcessCallback Camera2D::get_process_callback() const {
539
return process_callback;
540
}
541
542
void Camera2D::_make_current(Object *p_which) {
543
if (!is_inside_tree() || !viewport) {
544
return;
545
}
546
547
if (custom_viewport && !ObjectDB::get_instance(custom_viewport_id)) {
548
return;
549
}
550
551
queue_redraw();
552
553
bool was_current = viewport->get_camera_2d() == this;
554
bool is_current = p_which == this;
555
556
if (is_current) {
557
viewport->_camera_2d_set(this);
558
} else if (was_current) {
559
viewport->_camera_2d_set(nullptr);
560
}
561
562
if (is_current != was_current) {
563
_update_process_callback();
564
}
565
}
566
567
void Camera2D::set_limit_rect(const Rect2i &p_limit_rect) {
568
const Point2i limit_rect_end = p_limit_rect.get_end();
569
set_limit(SIDE_LEFT, p_limit_rect.position.x);
570
set_limit(SIDE_TOP, p_limit_rect.position.y);
571
set_limit(SIDE_RIGHT, limit_rect_end.x);
572
set_limit(SIDE_BOTTOM, limit_rect_end.y);
573
}
574
575
Rect2i Camera2D::get_limit_rect() const {
576
return Rect2i(limit[SIDE_LEFT], limit[SIDE_TOP], limit[SIDE_RIGHT] - limit[SIDE_LEFT], limit[SIDE_BOTTOM] - limit[SIDE_TOP]);
577
}
578
579
void Camera2D::make_current() {
580
ERR_FAIL_COND(!enabled || !is_inside_tree());
581
get_tree()->call_group(group_name, "_make_current", this);
582
if (just_exited_tree) {
583
// If camera exited the scene tree in the same frame, group call will skip it, so this needs to be called manually.
584
_make_current(this);
585
}
586
_update_scroll();
587
_update_process_callback();
588
}
589
590
void Camera2D::clear_current() {
591
ERR_FAIL_COND(!is_current());
592
593
if (!viewport || !viewport->is_inside_tree()) {
594
return;
595
}
596
597
if (!custom_viewport || ObjectDB::get_instance(custom_viewport_id)) {
598
viewport->assign_next_enabled_camera_2d(group_name);
599
}
600
601
_update_process_callback();
602
}
603
604
bool Camera2D::is_current() const {
605
if (!viewport) {
606
return false;
607
}
608
609
if (!custom_viewport || ObjectDB::get_instance(custom_viewport_id)) {
610
return viewport->get_camera_2d() == this;
611
}
612
return false;
613
}
614
615
void Camera2D::set_limit(Side p_side, int p_limit) {
616
ERR_FAIL_INDEX((int)p_side, 4);
617
if (limit[p_side] == p_limit) {
618
return;
619
}
620
limit[p_side] = p_limit;
621
Point2 old_smoothed_camera_pos = smoothed_camera_pos;
622
_update_scroll();
623
smoothed_camera_pos = old_smoothed_camera_pos;
624
}
625
626
int Camera2D::get_limit(Side p_side) const {
627
ERR_FAIL_INDEX_V((int)p_side, 4, 0);
628
return limit[p_side];
629
}
630
631
void Camera2D::set_limit_smoothing_enabled(bool p_enabled) {
632
if (limit_smoothing_enabled == p_enabled) {
633
return;
634
}
635
limit_smoothing_enabled = p_enabled;
636
_update_scroll();
637
}
638
639
bool Camera2D::is_limit_smoothing_enabled() const {
640
return limit_smoothing_enabled;
641
}
642
643
void Camera2D::set_drag_margin(Side p_side, real_t p_drag_margin) {
644
ERR_FAIL_INDEX((int)p_side, 4);
645
if (drag_margin[p_side] == p_drag_margin) {
646
return;
647
}
648
drag_margin[p_side] = p_drag_margin;
649
queue_redraw();
650
}
651
652
real_t Camera2D::get_drag_margin(Side p_side) const {
653
ERR_FAIL_INDEX_V((int)p_side, 4, 0);
654
return drag_margin[p_side];
655
}
656
657
Vector2 Camera2D::get_camera_position() const {
658
return camera_pos;
659
}
660
661
void Camera2D::force_update_scroll() {
662
_update_scroll();
663
}
664
665
void Camera2D::reset_smoothing() {
666
_update_scroll();
667
smoothed_camera_pos = camera_pos;
668
}
669
670
void Camera2D::align() {
671
ERR_FAIL_COND(custom_viewport && !ObjectDB::get_instance(custom_viewport_id));
672
673
Size2 screen_size = _get_camera_screen_size();
674
675
Point2 current_camera_pos = get_global_position();
676
if (anchor_mode == ANCHOR_MODE_DRAG_CENTER) {
677
if (drag_horizontal_offset < 0) {
678
camera_pos.x = current_camera_pos.x + screen_size.x * 0.5 * drag_margin[SIDE_RIGHT] * drag_horizontal_offset;
679
} else {
680
camera_pos.x = current_camera_pos.x + screen_size.x * 0.5 * drag_margin[SIDE_LEFT] * drag_horizontal_offset;
681
}
682
if (drag_vertical_offset < 0) {
683
camera_pos.y = current_camera_pos.y + screen_size.y * 0.5 * drag_margin[SIDE_TOP] * drag_vertical_offset;
684
} else {
685
camera_pos.y = current_camera_pos.y + screen_size.y * 0.5 * drag_margin[SIDE_BOTTOM] * drag_vertical_offset;
686
}
687
} else if (anchor_mode == ANCHOR_MODE_FIXED_TOP_LEFT) {
688
camera_pos = current_camera_pos;
689
}
690
691
_update_scroll();
692
}
693
694
void Camera2D::set_position_smoothing_speed(real_t p_speed) {
695
if (position_smoothing_speed == p_speed) {
696
return;
697
}
698
position_smoothing_speed = MAX(0, p_speed);
699
_update_process_callback();
700
}
701
702
real_t Camera2D::get_position_smoothing_speed() const {
703
return position_smoothing_speed;
704
}
705
706
void Camera2D::set_rotation_smoothing_speed(real_t p_speed) {
707
if (rotation_smoothing_speed == p_speed) {
708
return;
709
}
710
rotation_smoothing_speed = MAX(0, p_speed);
711
_update_process_callback();
712
}
713
714
real_t Camera2D::get_rotation_smoothing_speed() const {
715
return rotation_smoothing_speed;
716
}
717
718
void Camera2D::set_rotation_smoothing_enabled(bool p_enabled) {
719
if (rotation_smoothing_enabled == p_enabled) {
720
return;
721
}
722
rotation_smoothing_enabled = p_enabled;
723
}
724
725
bool Camera2D::is_rotation_smoothing_enabled() const {
726
return rotation_smoothing_enabled;
727
}
728
729
Point2 Camera2D::get_camera_screen_center() const {
730
return camera_screen_center;
731
}
732
733
real_t Camera2D::get_screen_rotation() const {
734
return camera_angle;
735
}
736
737
Size2 Camera2D::_get_camera_screen_size() const {
738
if (is_part_of_edited_scene()) {
739
return Size2(GLOBAL_GET_CACHED(real_t, "display/window/size/viewport_width"), GLOBAL_GET_CACHED(real_t, "display/window/size/viewport_height"));
740
}
741
return get_viewport_rect().size;
742
}
743
744
void Camera2D::set_drag_horizontal_enabled(bool p_enabled) {
745
drag_horizontal_enabled = p_enabled;
746
}
747
748
bool Camera2D::is_drag_horizontal_enabled() const {
749
return drag_horizontal_enabled;
750
}
751
752
void Camera2D::set_drag_vertical_enabled(bool p_enabled) {
753
drag_vertical_enabled = p_enabled;
754
}
755
756
bool Camera2D::is_drag_vertical_enabled() const {
757
return drag_vertical_enabled;
758
}
759
760
void Camera2D::set_drag_vertical_offset(real_t p_offset) {
761
if (drag_vertical_offset == p_offset) {
762
return;
763
}
764
drag_vertical_offset = p_offset;
765
drag_vertical_offset_changed = true;
766
Point2 old_smoothed_camera_pos = smoothed_camera_pos;
767
_update_scroll();
768
smoothed_camera_pos = old_smoothed_camera_pos;
769
}
770
771
real_t Camera2D::get_drag_vertical_offset() const {
772
return drag_vertical_offset;
773
}
774
775
void Camera2D::set_drag_horizontal_offset(real_t p_offset) {
776
if (drag_horizontal_offset == p_offset) {
777
return;
778
}
779
drag_horizontal_offset = p_offset;
780
drag_horizontal_offset_changed = true;
781
Point2 old_smoothed_camera_pos = smoothed_camera_pos;
782
_update_scroll();
783
smoothed_camera_pos = old_smoothed_camera_pos;
784
}
785
786
real_t Camera2D::get_drag_horizontal_offset() const {
787
return drag_horizontal_offset;
788
}
789
790
void Camera2D::set_position_smoothing_enabled(bool p_enabled) {
791
if (position_smoothing_enabled == p_enabled) {
792
return;
793
}
794
position_smoothing_enabled = p_enabled;
795
}
796
797
bool Camera2D::is_position_smoothing_enabled() const {
798
return position_smoothing_enabled;
799
}
800
801
void Camera2D::set_custom_viewport(Node *p_viewport) {
802
ERR_FAIL_NULL(p_viewport);
803
if (custom_viewport == p_viewport) {
804
return;
805
}
806
807
if (is_inside_tree()) {
808
remove_from_group(group_name);
809
remove_from_group(canvas_group_name);
810
}
811
812
custom_viewport = Object::cast_to<Viewport>(p_viewport);
813
814
if (custom_viewport) {
815
custom_viewport_id = custom_viewport->get_instance_id();
816
} else {
817
custom_viewport_id = ObjectID();
818
}
819
820
if (is_inside_tree()) {
821
if (custom_viewport) {
822
viewport = custom_viewport;
823
} else {
824
viewport = get_viewport();
825
}
826
827
RID vp = viewport->get_viewport_rid();
828
group_name = "__cameras_" + itos(vp.get_id());
829
canvas_group_name = "__cameras_c" + itos(canvas.get_id());
830
add_to_group(group_name);
831
add_to_group(canvas_group_name);
832
}
833
}
834
835
Node *Camera2D::get_custom_viewport() const {
836
return custom_viewport;
837
}
838
839
void Camera2D::set_screen_drawing_enabled(bool p_enabled) {
840
screen_drawing_enabled = p_enabled;
841
#ifdef TOOLS_ENABLED
842
queue_redraw();
843
#endif
844
}
845
846
bool Camera2D::is_screen_drawing_enabled() const {
847
return screen_drawing_enabled;
848
}
849
850
void Camera2D::set_limit_drawing_enabled(bool p_enabled) {
851
limit_drawing_enabled = p_enabled;
852
#ifdef TOOLS_ENABLED
853
queue_redraw();
854
#endif
855
}
856
857
bool Camera2D::is_limit_drawing_enabled() const {
858
return limit_drawing_enabled;
859
}
860
861
void Camera2D::set_margin_drawing_enabled(bool p_enabled) {
862
margin_drawing_enabled = p_enabled;
863
#ifdef TOOLS_ENABLED
864
queue_redraw();
865
#endif
866
}
867
868
bool Camera2D::is_margin_drawing_enabled() const {
869
return margin_drawing_enabled;
870
}
871
872
void Camera2D::_bind_methods() {
873
ClassDB::bind_method(D_METHOD("set_offset", "offset"), &Camera2D::set_offset);
874
ClassDB::bind_method(D_METHOD("get_offset"), &Camera2D::get_offset);
875
876
ClassDB::bind_method(D_METHOD("set_anchor_mode", "anchor_mode"), &Camera2D::set_anchor_mode);
877
ClassDB::bind_method(D_METHOD("get_anchor_mode"), &Camera2D::get_anchor_mode);
878
879
ClassDB::bind_method(D_METHOD("set_ignore_rotation", "ignore"), &Camera2D::set_ignore_rotation);
880
ClassDB::bind_method(D_METHOD("is_ignoring_rotation"), &Camera2D::is_ignoring_rotation);
881
882
ClassDB::bind_method(D_METHOD("_update_scroll"), &Camera2D::_update_scroll);
883
884
ClassDB::bind_method(D_METHOD("set_process_callback", "mode"), &Camera2D::set_process_callback);
885
ClassDB::bind_method(D_METHOD("get_process_callback"), &Camera2D::get_process_callback);
886
887
ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &Camera2D::set_enabled);
888
ClassDB::bind_method(D_METHOD("is_enabled"), &Camera2D::is_enabled);
889
890
ClassDB::bind_method(D_METHOD("make_current"), &Camera2D::make_current);
891
ClassDB::bind_method(D_METHOD("is_current"), &Camera2D::is_current);
892
ClassDB::bind_method(D_METHOD("_make_current"), &Camera2D::_make_current);
893
894
ClassDB::bind_method(D_METHOD("set_limit_enabled", "limit_enabled"), &Camera2D::set_limit_enabled);
895
ClassDB::bind_method(D_METHOD("is_limit_enabled"), &Camera2D::is_limit_enabled);
896
897
ClassDB::bind_method(D_METHOD("set_limit", "margin", "limit"), &Camera2D::set_limit);
898
ClassDB::bind_method(D_METHOD("get_limit", "margin"), &Camera2D::get_limit);
899
ClassDB::bind_method(D_METHOD("_set_limit_rect", "rect"), &Camera2D::set_limit_rect);
900
901
ClassDB::bind_method(D_METHOD("set_limit_smoothing_enabled", "limit_smoothing_enabled"), &Camera2D::set_limit_smoothing_enabled);
902
ClassDB::bind_method(D_METHOD("is_limit_smoothing_enabled"), &Camera2D::is_limit_smoothing_enabled);
903
904
ClassDB::bind_method(D_METHOD("set_drag_vertical_enabled", "enabled"), &Camera2D::set_drag_vertical_enabled);
905
ClassDB::bind_method(D_METHOD("is_drag_vertical_enabled"), &Camera2D::is_drag_vertical_enabled);
906
907
ClassDB::bind_method(D_METHOD("set_drag_horizontal_enabled", "enabled"), &Camera2D::set_drag_horizontal_enabled);
908
ClassDB::bind_method(D_METHOD("is_drag_horizontal_enabled"), &Camera2D::is_drag_horizontal_enabled);
909
910
ClassDB::bind_method(D_METHOD("set_drag_vertical_offset", "offset"), &Camera2D::set_drag_vertical_offset);
911
ClassDB::bind_method(D_METHOD("get_drag_vertical_offset"), &Camera2D::get_drag_vertical_offset);
912
913
ClassDB::bind_method(D_METHOD("set_drag_horizontal_offset", "offset"), &Camera2D::set_drag_horizontal_offset);
914
ClassDB::bind_method(D_METHOD("get_drag_horizontal_offset"), &Camera2D::get_drag_horizontal_offset);
915
916
ClassDB::bind_method(D_METHOD("set_drag_margin", "margin", "drag_margin"), &Camera2D::set_drag_margin);
917
ClassDB::bind_method(D_METHOD("get_drag_margin", "margin"), &Camera2D::get_drag_margin);
918
919
ClassDB::bind_method(D_METHOD("get_target_position"), &Camera2D::get_camera_position);
920
ClassDB::bind_method(D_METHOD("get_screen_center_position"), &Camera2D::get_camera_screen_center);
921
ClassDB::bind_method(D_METHOD("get_screen_rotation"), &Camera2D::get_screen_rotation);
922
923
ClassDB::bind_method(D_METHOD("set_zoom", "zoom"), &Camera2D::set_zoom);
924
ClassDB::bind_method(D_METHOD("get_zoom"), &Camera2D::get_zoom);
925
926
ClassDB::bind_method(D_METHOD("set_custom_viewport", "viewport"), &Camera2D::set_custom_viewport);
927
ClassDB::bind_method(D_METHOD("get_custom_viewport"), &Camera2D::get_custom_viewport);
928
929
ClassDB::bind_method(D_METHOD("set_position_smoothing_speed", "position_smoothing_speed"), &Camera2D::set_position_smoothing_speed);
930
ClassDB::bind_method(D_METHOD("get_position_smoothing_speed"), &Camera2D::get_position_smoothing_speed);
931
932
ClassDB::bind_method(D_METHOD("set_position_smoothing_enabled", "enabled"), &Camera2D::set_position_smoothing_enabled);
933
ClassDB::bind_method(D_METHOD("is_position_smoothing_enabled"), &Camera2D::is_position_smoothing_enabled);
934
935
ClassDB::bind_method(D_METHOD("set_rotation_smoothing_enabled", "enabled"), &Camera2D::set_rotation_smoothing_enabled);
936
ClassDB::bind_method(D_METHOD("is_rotation_smoothing_enabled"), &Camera2D::is_rotation_smoothing_enabled);
937
938
ClassDB::bind_method(D_METHOD("set_rotation_smoothing_speed", "speed"), &Camera2D::set_rotation_smoothing_speed);
939
ClassDB::bind_method(D_METHOD("get_rotation_smoothing_speed"), &Camera2D::get_rotation_smoothing_speed);
940
941
ClassDB::bind_method(D_METHOD("force_update_scroll"), &Camera2D::force_update_scroll);
942
ClassDB::bind_method(D_METHOD("reset_smoothing"), &Camera2D::reset_smoothing);
943
ClassDB::bind_method(D_METHOD("align"), &Camera2D::align);
944
945
ClassDB::bind_method(D_METHOD("set_screen_drawing_enabled", "screen_drawing_enabled"), &Camera2D::set_screen_drawing_enabled);
946
ClassDB::bind_method(D_METHOD("is_screen_drawing_enabled"), &Camera2D::is_screen_drawing_enabled);
947
948
ClassDB::bind_method(D_METHOD("set_limit_drawing_enabled", "limit_drawing_enabled"), &Camera2D::set_limit_drawing_enabled);
949
ClassDB::bind_method(D_METHOD("is_limit_drawing_enabled"), &Camera2D::is_limit_drawing_enabled);
950
951
ClassDB::bind_method(D_METHOD("set_margin_drawing_enabled", "margin_drawing_enabled"), &Camera2D::set_margin_drawing_enabled);
952
ClassDB::bind_method(D_METHOD("is_margin_drawing_enabled"), &Camera2D::is_margin_drawing_enabled);
953
954
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset", PROPERTY_HINT_NONE, "suffix:px"), "set_offset", "get_offset");
955
ADD_PROPERTY(PropertyInfo(Variant::INT, "anchor_mode", PROPERTY_HINT_ENUM, "Fixed Top Left,Drag Center"), "set_anchor_mode", "get_anchor_mode");
956
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_rotation"), "set_ignore_rotation", "is_ignoring_rotation");
957
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
958
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "zoom", PROPERTY_HINT_LINK), "set_zoom", "get_zoom");
959
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "custom_viewport", PROPERTY_HINT_RESOURCE_TYPE, "Viewport", PROPERTY_USAGE_NONE), "set_custom_viewport", "get_custom_viewport");
960
ADD_PROPERTY(PropertyInfo(Variant::INT, "process_callback", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_process_callback", "get_process_callback");
961
962
ADD_GROUP("Limit", "limit_");
963
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "limit_enabled", PROPERTY_HINT_GROUP_ENABLE), "set_limit_enabled", "is_limit_enabled");
964
ADD_PROPERTYI(PropertyInfo(Variant::INT, "limit_left", PROPERTY_HINT_NONE, "suffix:px"), "set_limit", "get_limit", SIDE_LEFT);
965
ADD_PROPERTYI(PropertyInfo(Variant::INT, "limit_top", PROPERTY_HINT_NONE, "suffix:px"), "set_limit", "get_limit", SIDE_TOP);
966
ADD_PROPERTYI(PropertyInfo(Variant::INT, "limit_right", PROPERTY_HINT_NONE, "suffix:px"), "set_limit", "get_limit", SIDE_RIGHT);
967
ADD_PROPERTYI(PropertyInfo(Variant::INT, "limit_bottom", PROPERTY_HINT_NONE, "suffix:px"), "set_limit", "get_limit", SIDE_BOTTOM);
968
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "limit_smoothed"), "set_limit_smoothing_enabled", "is_limit_smoothing_enabled");
969
970
ADD_GROUP("Position Smoothing", "position_smoothing_");
971
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "position_smoothing_enabled", PROPERTY_HINT_GROUP_ENABLE), "set_position_smoothing_enabled", "is_position_smoothing_enabled");
972
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "position_smoothing_speed", PROPERTY_HINT_NONE, "suffix:px/s"), "set_position_smoothing_speed", "get_position_smoothing_speed");
973
974
ADD_GROUP("Rotation Smoothing", "rotation_smoothing_");
975
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rotation_smoothing_enabled", PROPERTY_HINT_GROUP_ENABLE), "set_rotation_smoothing_enabled", "is_rotation_smoothing_enabled");
976
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation_smoothing_speed"), "set_rotation_smoothing_speed", "get_rotation_smoothing_speed");
977
978
ADD_GROUP("Drag", "drag_");
979
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_horizontal_enabled"), "set_drag_horizontal_enabled", "is_drag_horizontal_enabled");
980
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_vertical_enabled"), "set_drag_vertical_enabled", "is_drag_vertical_enabled");
981
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "drag_horizontal_offset", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_drag_horizontal_offset", "get_drag_horizontal_offset");
982
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "drag_vertical_offset", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_drag_vertical_offset", "get_drag_vertical_offset");
983
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "drag_left_margin", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_margin", "get_drag_margin", SIDE_LEFT);
984
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "drag_top_margin", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_margin", "get_drag_margin", SIDE_TOP);
985
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "drag_right_margin", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_margin", "get_drag_margin", SIDE_RIGHT);
986
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "drag_bottom_margin", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_margin", "get_drag_margin", SIDE_BOTTOM);
987
988
ADD_GROUP("Editor", "editor_");
989
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_draw_screen"), "set_screen_drawing_enabled", "is_screen_drawing_enabled");
990
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_draw_limits"), "set_limit_drawing_enabled", "is_limit_drawing_enabled");
991
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_draw_drag_margin"), "set_margin_drawing_enabled", "is_margin_drawing_enabled");
992
993
BIND_ENUM_CONSTANT(ANCHOR_MODE_FIXED_TOP_LEFT);
994
BIND_ENUM_CONSTANT(ANCHOR_MODE_DRAG_CENTER);
995
BIND_ENUM_CONSTANT(CAMERA2D_PROCESS_PHYSICS);
996
BIND_ENUM_CONSTANT(CAMERA2D_PROCESS_IDLE);
997
}
998
999
Camera2D::Camera2D() {
1000
set_notify_transform(true);
1001
set_hide_clip_children(true);
1002
}
1003
1004