Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/scene/2d/navigation/navigation_link_2d.cpp
9904 views
1
/**************************************************************************/
2
/* navigation_link_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 "navigation_link_2d.h"
32
33
#include "core/math/geometry_2d.h"
34
#include "scene/resources/world_2d.h"
35
#include "servers/navigation_server_2d.h"
36
37
void NavigationLink2D::_bind_methods() {
38
ClassDB::bind_method(D_METHOD("get_rid"), &NavigationLink2D::get_rid);
39
40
ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationLink2D::set_enabled);
41
ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationLink2D::is_enabled);
42
43
ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationLink2D::set_navigation_map);
44
ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationLink2D::get_navigation_map);
45
46
ClassDB::bind_method(D_METHOD("set_bidirectional", "bidirectional"), &NavigationLink2D::set_bidirectional);
47
ClassDB::bind_method(D_METHOD("is_bidirectional"), &NavigationLink2D::is_bidirectional);
48
49
ClassDB::bind_method(D_METHOD("set_navigation_layers", "navigation_layers"), &NavigationLink2D::set_navigation_layers);
50
ClassDB::bind_method(D_METHOD("get_navigation_layers"), &NavigationLink2D::get_navigation_layers);
51
52
ClassDB::bind_method(D_METHOD("set_navigation_layer_value", "layer_number", "value"), &NavigationLink2D::set_navigation_layer_value);
53
ClassDB::bind_method(D_METHOD("get_navigation_layer_value", "layer_number"), &NavigationLink2D::get_navigation_layer_value);
54
55
ClassDB::bind_method(D_METHOD("set_start_position", "position"), &NavigationLink2D::set_start_position);
56
ClassDB::bind_method(D_METHOD("get_start_position"), &NavigationLink2D::get_start_position);
57
58
ClassDB::bind_method(D_METHOD("set_end_position", "position"), &NavigationLink2D::set_end_position);
59
ClassDB::bind_method(D_METHOD("get_end_position"), &NavigationLink2D::get_end_position);
60
61
ClassDB::bind_method(D_METHOD("set_global_start_position", "position"), &NavigationLink2D::set_global_start_position);
62
ClassDB::bind_method(D_METHOD("get_global_start_position"), &NavigationLink2D::get_global_start_position);
63
64
ClassDB::bind_method(D_METHOD("set_global_end_position", "position"), &NavigationLink2D::set_global_end_position);
65
ClassDB::bind_method(D_METHOD("get_global_end_position"), &NavigationLink2D::get_global_end_position);
66
67
ClassDB::bind_method(D_METHOD("set_enter_cost", "enter_cost"), &NavigationLink2D::set_enter_cost);
68
ClassDB::bind_method(D_METHOD("get_enter_cost"), &NavigationLink2D::get_enter_cost);
69
70
ClassDB::bind_method(D_METHOD("set_travel_cost", "travel_cost"), &NavigationLink2D::set_travel_cost);
71
ClassDB::bind_method(D_METHOD("get_travel_cost"), &NavigationLink2D::get_travel_cost);
72
73
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
74
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bidirectional"), "set_bidirectional", "is_bidirectional");
75
ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_2D_NAVIGATION), "set_navigation_layers", "get_navigation_layers");
76
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "start_position"), "set_start_position", "get_start_position");
77
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "end_position"), "set_end_position", "get_end_position");
78
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "enter_cost"), "set_enter_cost", "get_enter_cost");
79
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "travel_cost"), "set_travel_cost", "get_travel_cost");
80
}
81
82
#ifndef DISABLE_DEPRECATED
83
bool NavigationLink2D::_set(const StringName &p_name, const Variant &p_value) {
84
if (p_name == "start_location") {
85
set_start_position(p_value);
86
return true;
87
}
88
if (p_name == "end_location") {
89
set_end_position(p_value);
90
return true;
91
}
92
return false;
93
}
94
95
bool NavigationLink2D::_get(const StringName &p_name, Variant &r_ret) const {
96
if (p_name == "start_location") {
97
r_ret = get_start_position();
98
return true;
99
}
100
if (p_name == "end_location") {
101
r_ret = get_end_position();
102
return true;
103
}
104
return false;
105
}
106
#endif // DISABLE_DEPRECATED
107
108
void NavigationLink2D::_notification(int p_what) {
109
switch (p_what) {
110
case NOTIFICATION_ENTER_TREE: {
111
_link_enter_navigation_map();
112
} break;
113
114
case NOTIFICATION_TRANSFORM_CHANGED: {
115
set_physics_process_internal(true);
116
} break;
117
118
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
119
set_physics_process_internal(false);
120
_link_update_transform();
121
} break;
122
123
case NOTIFICATION_EXIT_TREE: {
124
_link_exit_navigation_map();
125
} break;
126
case NOTIFICATION_DRAW: {
127
#ifdef DEBUG_ENABLED
128
_update_debug_mesh();
129
#endif // DEBUG_ENABLED
130
} break;
131
}
132
}
133
134
#ifdef DEBUG_ENABLED
135
Rect2 NavigationLink2D::_edit_get_rect() const {
136
if (!is_inside_tree()) {
137
return Rect2();
138
}
139
140
real_t radius = NavigationServer2D::get_singleton()->map_get_link_connection_radius(get_world_2d()->get_navigation_map());
141
142
Rect2 rect(get_start_position(), Size2());
143
rect.expand_to(get_end_position());
144
rect.grow_by(radius);
145
return rect;
146
}
147
148
bool NavigationLink2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
149
Vector2 closest_point = Geometry2D::get_closest_point_to_segment(p_point, get_start_position(), get_end_position());
150
return p_point.distance_to(closest_point) < p_tolerance;
151
}
152
#endif // DEBUG_ENABLED
153
154
RID NavigationLink2D::get_rid() const {
155
return link;
156
}
157
158
void NavigationLink2D::set_enabled(bool p_enabled) {
159
if (enabled == p_enabled) {
160
return;
161
}
162
163
enabled = p_enabled;
164
165
NavigationServer2D::get_singleton()->link_set_enabled(link, enabled);
166
167
#ifdef DEBUG_ENABLED
168
queue_redraw();
169
#endif // DEBUG_ENABLED
170
}
171
172
void NavigationLink2D::set_navigation_map(RID p_navigation_map) {
173
if (map_override == p_navigation_map) {
174
return;
175
}
176
177
map_override = p_navigation_map;
178
179
NavigationServer2D::get_singleton()->link_set_map(link, map_override);
180
}
181
182
RID NavigationLink2D::get_navigation_map() const {
183
if (map_override.is_valid()) {
184
return map_override;
185
} else if (is_inside_tree()) {
186
return get_world_2d()->get_navigation_map();
187
}
188
return RID();
189
}
190
191
void NavigationLink2D::set_bidirectional(bool p_bidirectional) {
192
if (bidirectional == p_bidirectional) {
193
return;
194
}
195
196
bidirectional = p_bidirectional;
197
198
NavigationServer2D::get_singleton()->link_set_bidirectional(link, bidirectional);
199
200
#ifdef DEBUG_ENABLED
201
queue_redraw();
202
#endif // DEBUG_ENABLED
203
}
204
205
void NavigationLink2D::set_navigation_layers(uint32_t p_navigation_layers) {
206
if (navigation_layers == p_navigation_layers) {
207
return;
208
}
209
210
navigation_layers = p_navigation_layers;
211
212
NavigationServer2D::get_singleton()->link_set_navigation_layers(link, navigation_layers);
213
}
214
215
void NavigationLink2D::set_navigation_layer_value(int p_layer_number, bool p_value) {
216
ERR_FAIL_COND_MSG(p_layer_number < 1, "Navigation layer number must be between 1 and 32 inclusive.");
217
ERR_FAIL_COND_MSG(p_layer_number > 32, "Navigation layer number must be between 1 and 32 inclusive.");
218
219
uint32_t _navigation_layers = get_navigation_layers();
220
221
if (p_value) {
222
_navigation_layers |= 1 << (p_layer_number - 1);
223
} else {
224
_navigation_layers &= ~(1 << (p_layer_number - 1));
225
}
226
227
set_navigation_layers(_navigation_layers);
228
}
229
230
bool NavigationLink2D::get_navigation_layer_value(int p_layer_number) const {
231
ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Navigation layer number must be between 1 and 32 inclusive.");
232
ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Navigation layer number must be between 1 and 32 inclusive.");
233
234
return get_navigation_layers() & (1 << (p_layer_number - 1));
235
}
236
237
void NavigationLink2D::set_start_position(Vector2 p_position) {
238
if (start_position.is_equal_approx(p_position)) {
239
return;
240
}
241
242
start_position = p_position;
243
244
if (!is_inside_tree()) {
245
return;
246
}
247
248
NavigationServer2D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position));
249
250
update_configuration_warnings();
251
252
#ifdef DEBUG_ENABLED
253
queue_redraw();
254
#endif // DEBUG_ENABLED
255
}
256
257
void NavigationLink2D::set_end_position(Vector2 p_position) {
258
if (end_position.is_equal_approx(p_position)) {
259
return;
260
}
261
262
end_position = p_position;
263
264
if (!is_inside_tree()) {
265
return;
266
}
267
268
NavigationServer2D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position));
269
270
update_configuration_warnings();
271
272
#ifdef DEBUG_ENABLED
273
queue_redraw();
274
#endif // DEBUG_ENABLED
275
}
276
277
void NavigationLink2D::set_global_start_position(Vector2 p_position) {
278
if (is_inside_tree()) {
279
set_start_position(to_local(p_position));
280
} else {
281
set_start_position(p_position);
282
}
283
}
284
285
Vector2 NavigationLink2D::get_global_start_position() const {
286
if (is_inside_tree()) {
287
return to_global(start_position);
288
} else {
289
return start_position;
290
}
291
}
292
293
void NavigationLink2D::set_global_end_position(Vector2 p_position) {
294
if (is_inside_tree()) {
295
set_end_position(to_local(p_position));
296
} else {
297
set_end_position(p_position);
298
}
299
}
300
301
Vector2 NavigationLink2D::get_global_end_position() const {
302
if (is_inside_tree()) {
303
return to_global(end_position);
304
} else {
305
return end_position;
306
}
307
}
308
309
void NavigationLink2D::set_enter_cost(real_t p_enter_cost) {
310
ERR_FAIL_COND_MSG(p_enter_cost < 0.0, "The enter_cost must be positive.");
311
if (Math::is_equal_approx(enter_cost, p_enter_cost)) {
312
return;
313
}
314
315
enter_cost = p_enter_cost;
316
317
NavigationServer2D::get_singleton()->link_set_enter_cost(link, enter_cost);
318
}
319
320
void NavigationLink2D::set_travel_cost(real_t p_travel_cost) {
321
ERR_FAIL_COND_MSG(p_travel_cost < 0.0, "The travel_cost must be positive.");
322
if (Math::is_equal_approx(travel_cost, p_travel_cost)) {
323
return;
324
}
325
326
travel_cost = p_travel_cost;
327
328
NavigationServer2D::get_singleton()->link_set_travel_cost(link, travel_cost);
329
}
330
331
PackedStringArray NavigationLink2D::get_configuration_warnings() const {
332
PackedStringArray warnings = Node2D::get_configuration_warnings();
333
334
if (start_position.is_equal_approx(end_position)) {
335
warnings.push_back(RTR("NavigationLink2D start position should be different than the end position to be useful."));
336
}
337
338
return warnings;
339
}
340
341
void NavigationLink2D::_link_enter_navigation_map() {
342
if (!is_inside_tree()) {
343
return;
344
}
345
346
if (map_override.is_valid()) {
347
NavigationServer2D::get_singleton()->link_set_map(link, map_override);
348
} else {
349
NavigationServer2D::get_singleton()->link_set_map(link, get_world_2d()->get_navigation_map());
350
}
351
352
current_global_transform = get_global_transform();
353
354
NavigationServer2D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position));
355
NavigationServer2D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position));
356
NavigationServer2D::get_singleton()->link_set_enabled(link, enabled);
357
358
queue_redraw();
359
}
360
361
void NavigationLink2D::_link_exit_navigation_map() {
362
NavigationServer2D::get_singleton()->link_set_map(link, RID());
363
}
364
365
void NavigationLink2D::_link_update_transform() {
366
if (!is_inside_tree()) {
367
return;
368
}
369
370
Transform2D new_global_transform = get_global_transform();
371
if (current_global_transform != new_global_transform) {
372
current_global_transform = new_global_transform;
373
NavigationServer2D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position));
374
NavigationServer2D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position));
375
queue_redraw();
376
}
377
}
378
379
#ifdef DEBUG_ENABLED
380
void NavigationLink2D::_update_debug_mesh() {
381
if (!is_inside_tree()) {
382
return;
383
}
384
385
if (!Engine::get_singleton()->is_editor_hint() && !NavigationServer2D::get_singleton()->get_debug_enabled()) {
386
return;
387
}
388
389
Color color;
390
if (enabled) {
391
color = NavigationServer2D::get_singleton()->get_debug_navigation_link_connection_color();
392
} else {
393
color = NavigationServer2D::get_singleton()->get_debug_navigation_link_connection_disabled_color();
394
}
395
396
real_t radius = NavigationServer2D::get_singleton()->map_get_link_connection_radius(get_world_2d()->get_navigation_map());
397
398
draw_line(get_start_position(), get_end_position(), color);
399
draw_arc(get_start_position(), radius, 0, Math::TAU, 10, color);
400
draw_arc(get_end_position(), radius, 0, Math::TAU, 10, color);
401
402
const Vector2 link_segment = end_position - start_position;
403
const float arror_len = 5.0;
404
405
{
406
Vector2 anchor = start_position + (link_segment * 0.75);
407
Vector2 direction = start_position.direction_to(end_position);
408
Vector2 arrow_dir = -direction.orthogonal();
409
draw_line(anchor, anchor + (arrow_dir - direction) * arror_len, color);
410
411
arrow_dir = direction.orthogonal();
412
draw_line(anchor, anchor + (arrow_dir - direction) * arror_len, color);
413
}
414
415
if (is_bidirectional()) {
416
Vector2 anchor = start_position + (link_segment * 0.25);
417
Vector2 direction = end_position.direction_to(start_position);
418
Vector2 arrow_dir = -direction.orthogonal();
419
draw_line(anchor, anchor + (arrow_dir - direction) * arror_len, color);
420
421
arrow_dir = direction.orthogonal();
422
draw_line(anchor, anchor + (arrow_dir - direction) * arror_len, color);
423
}
424
}
425
#endif // DEBUG_ENABLED
426
427
NavigationLink2D::NavigationLink2D() {
428
link = NavigationServer2D::get_singleton()->link_create();
429
430
NavigationServer2D::get_singleton()->link_set_owner_id(link, get_instance_id());
431
NavigationServer2D::get_singleton()->link_set_enter_cost(link, enter_cost);
432
NavigationServer2D::get_singleton()->link_set_travel_cost(link, travel_cost);
433
NavigationServer2D::get_singleton()->link_set_navigation_layers(link, navigation_layers);
434
NavigationServer2D::get_singleton()->link_set_bidirectional(link, bidirectional);
435
NavigationServer2D::get_singleton()->link_set_enabled(link, enabled);
436
437
set_notify_transform(true);
438
set_hide_clip_children(true);
439
}
440
441
NavigationLink2D::~NavigationLink2D() {
442
ERR_FAIL_NULL(NavigationServer2D::get_singleton());
443
NavigationServer2D::get_singleton()->free(link);
444
link = RID();
445
}
446
447