1 /*************************************************************************/
2 /* collision_object_2d.cpp */
3 /*************************************************************************/
4 /* This file is part of: */
5 /* GODOT ENGINE */
6 /* https://godotengine.org */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
9 /* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
10 /* */
11 /* Permission is hereby granted, free of charge, to any person obtaining */
12 /* a copy of this software and associated documentation files (the */
13 /* "Software"), to deal in the Software without restriction, including */
14 /* without limitation the rights to use, copy, modify, merge, publish, */
15 /* distribute, sublicense, and/or sell copies of the Software, and to */
16 /* permit persons to whom the Software is furnished to do so, subject to */
17 /* the following conditions: */
18 /* */
19 /* The above copyright notice and this permission notice shall be */
20 /* included in all copies or substantial portions of the Software. */
21 /* */
22 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
25 /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26 /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27 /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29 /*************************************************************************/
30 #include "collision_object_2d.h"
31 #include "scene/scene_string_names.h"
32 #include "servers/physics_2d_server.h"
33
_update_shapes_from_children()34 void CollisionObject2D::_update_shapes_from_children() {
35
36 shapes.clear();
37 for (int i = 0; i < get_child_count(); i++) {
38
39 Node *n = get_child(i);
40 n->call("_add_to_collision_object", this);
41 }
42
43 _update_shapes();
44 }
45
_notification(int p_what)46 void CollisionObject2D::_notification(int p_what) {
47
48 switch (p_what) {
49
50 case NOTIFICATION_ENTER_TREE: {
51
52 if (area)
53 Physics2DServer::get_singleton()->area_set_transform(rid, get_global_transform());
54 else
55 Physics2DServer::get_singleton()->body_set_state(rid, Physics2DServer::BODY_STATE_TRANSFORM, get_global_transform());
56
57 RID space = get_world_2d()->get_space();
58 if (area) {
59 Physics2DServer::get_singleton()->area_set_space(rid, space);
60 } else
61 Physics2DServer::get_singleton()->body_set_space(rid, space);
62
63 _update_pickable();
64
65 //get space
66 }
67
68 case NOTIFICATION_VISIBILITY_CHANGED: {
69
70 _update_pickable();
71 } break;
72 case NOTIFICATION_TRANSFORM_CHANGED: {
73
74 if (area)
75 Physics2DServer::get_singleton()->area_set_transform(rid, get_global_transform());
76 else
77 Physics2DServer::get_singleton()->body_set_state(rid, Physics2DServer::BODY_STATE_TRANSFORM, get_global_transform());
78
79 } break;
80 case NOTIFICATION_EXIT_TREE: {
81
82 if (area) {
83 Physics2DServer::get_singleton()->area_set_space(rid, RID());
84 } else
85 Physics2DServer::get_singleton()->body_set_space(rid, RID());
86
87 } break;
88 }
89 }
90
_update_shapes()91 void CollisionObject2D::_update_shapes() {
92
93 if (!rid.is_valid())
94 return;
95
96 if (area)
97 Physics2DServer::get_singleton()->area_clear_shapes(rid);
98 else
99 Physics2DServer::get_singleton()->body_clear_shapes(rid);
100
101 for (int i = 0; i < shapes.size(); i++) {
102
103 if (shapes[i].shape.is_null())
104 continue;
105 if (area)
106 Physics2DServer::get_singleton()->area_add_shape(rid, shapes[i].shape->get_rid(), shapes[i].xform);
107 else {
108 Physics2DServer::get_singleton()->body_add_shape(rid, shapes[i].shape->get_rid(), shapes[i].xform);
109 if (shapes[i].trigger)
110 Physics2DServer::get_singleton()->body_set_shape_as_trigger(rid, i, shapes[i].trigger);
111 }
112 }
113 }
114
_set(const StringName & p_name,const Variant & p_value)115 bool CollisionObject2D::_set(const StringName &p_name, const Variant &p_value) {
116 String name = p_name;
117
118 if (name.begins_with("shapes/")) {
119
120 int idx = name.get_slicec('/', 1).to_int();
121 String what = name.get_slicec('/', 2);
122 if (what == "shape") {
123 if (idx >= shapes.size())
124 add_shape(RefPtr(p_value));
125 else
126 set_shape(idx, RefPtr(p_value));
127 } else if (what == "transform")
128 set_shape_transform(idx, p_value);
129 else if (what == "trigger")
130 set_shape_as_trigger(idx, p_value);
131 } else
132 return false;
133
134 return true;
135 }
136
_get(const StringName & p_name,Variant & r_ret) const137 bool CollisionObject2D::_get(const StringName &p_name, Variant &r_ret) const {
138
139 String name = p_name;
140
141 if (name.begins_with("shapes/")) {
142
143 int idx = name.get_slicec('/', 1).to_int();
144 String what = name.get_slicec('/', 2);
145 if (what == "shape")
146 r_ret = get_shape(idx);
147 else if (what == "transform")
148 r_ret = get_shape_transform(idx);
149 else if (what == "trigger")
150 r_ret = is_shape_set_as_trigger(idx);
151 } else
152 return false;
153
154 return true;
155 }
156
_get_property_list(List<PropertyInfo> * p_list) const157 void CollisionObject2D::_get_property_list(List<PropertyInfo> *p_list) const {
158
159 //p_list->push_back( PropertyInfo(Variant::INT,"shape_count",PROPERTY_HINT_RANGE,"0,256,1",PROPERTY_USAGE_NOEDITOR|PROPERTY_USAGE_NO_INSTANCE_STATE) );
160
161 for (int i = 0; i < shapes.size(); i++) {
162 String path = "shapes/" + itos(i) + "/";
163 p_list->push_back(PropertyInfo(Variant::OBJECT, path + "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape2D", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_NO_INSTANCE_STATE));
164 p_list->push_back(PropertyInfo(Variant::TRANSFORM, path + "transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_NO_INSTANCE_STATE));
165 p_list->push_back(PropertyInfo(Variant::BOOL, path + "trigger", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_NO_INSTANCE_STATE));
166 }
167 }
168
set_pickable(bool p_enabled)169 void CollisionObject2D::set_pickable(bool p_enabled) {
170
171 if (pickable == p_enabled)
172 return;
173
174 pickable = p_enabled;
175 _update_pickable();
176 }
177
is_pickable() const178 bool CollisionObject2D::is_pickable() const {
179
180 return pickable;
181 }
182
_input_event(Node * p_viewport,const InputEvent & p_input_event,int p_shape)183 void CollisionObject2D::_input_event(Node *p_viewport, const InputEvent &p_input_event, int p_shape) {
184
185 if (get_script_instance()) {
186 get_script_instance()->call(SceneStringNames::get_singleton()->_input_event, p_viewport, p_input_event, p_shape);
187 }
188 emit_signal(SceneStringNames::get_singleton()->input_event, p_viewport, p_input_event, p_shape);
189 }
190
_mouse_enter()191 void CollisionObject2D::_mouse_enter() {
192
193 if (get_script_instance()) {
194 get_script_instance()->call(SceneStringNames::get_singleton()->_mouse_enter);
195 }
196 emit_signal(SceneStringNames::get_singleton()->mouse_enter);
197 }
198
_mouse_exit()199 void CollisionObject2D::_mouse_exit() {
200
201 if (get_script_instance()) {
202 get_script_instance()->call(SceneStringNames::get_singleton()->_mouse_exit);
203 }
204 emit_signal(SceneStringNames::get_singleton()->mouse_exit);
205 }
206
_update_pickable()207 void CollisionObject2D::_update_pickable() {
208 if (!is_inside_tree())
209 return;
210 bool pickable = this->pickable && is_inside_tree() && is_visible();
211 if (area)
212 Physics2DServer::get_singleton()->area_set_pickable(rid, pickable);
213 else
214 Physics2DServer::get_singleton()->body_set_pickable(rid, pickable);
215 }
216
_bind_methods()217 void CollisionObject2D::_bind_methods() {
218
219 ObjectTypeDB::bind_method(_MD("add_shape", "shape:Shape2D", "transform"), &CollisionObject2D::add_shape, DEFVAL(Matrix32()));
220 ObjectTypeDB::bind_method(_MD("get_shape_count"), &CollisionObject2D::get_shape_count);
221 ObjectTypeDB::bind_method(_MD("set_shape", "shape_idx", "shape:Shape"), &CollisionObject2D::set_shape);
222 ObjectTypeDB::bind_method(_MD("set_shape_transform", "shape_idx", "transform"), &CollisionObject2D::set_shape_transform);
223 ObjectTypeDB::bind_method(_MD("set_shape_as_trigger", "shape_idx", "enable"), &CollisionObject2D::set_shape_as_trigger);
224 ObjectTypeDB::bind_method(_MD("get_shape:Shape2D", "shape_idx"), &CollisionObject2D::get_shape);
225 ObjectTypeDB::bind_method(_MD("get_shape_transform", "shape_idx"), &CollisionObject2D::get_shape_transform);
226 ObjectTypeDB::bind_method(_MD("is_shape_set_as_trigger", "shape_idx"), &CollisionObject2D::is_shape_set_as_trigger);
227 ObjectTypeDB::bind_method(_MD("remove_shape", "shape_idx"), &CollisionObject2D::remove_shape);
228 ObjectTypeDB::bind_method(_MD("clear_shapes"), &CollisionObject2D::clear_shapes);
229 ObjectTypeDB::bind_method(_MD("get_rid"), &CollisionObject2D::get_rid);
230
231 ObjectTypeDB::bind_method(_MD("set_pickable", "enabled"), &CollisionObject2D::set_pickable);
232 ObjectTypeDB::bind_method(_MD("is_pickable"), &CollisionObject2D::is_pickable);
233
234 BIND_VMETHOD(MethodInfo("_input_event", PropertyInfo(Variant::OBJECT, "viewport"), PropertyInfo(Variant::INPUT_EVENT, "event"), PropertyInfo(Variant::INT, "shape_idx")));
235
236 ADD_SIGNAL(MethodInfo("input_event", PropertyInfo(Variant::OBJECT, "viewport"), PropertyInfo(Variant::INPUT_EVENT, "event"), PropertyInfo(Variant::INT, "shape_idx")));
237 ADD_SIGNAL(MethodInfo("mouse_enter"));
238 ADD_SIGNAL(MethodInfo("mouse_exit"));
239
240 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "input/pickable"), _SCS("set_pickable"), _SCS("is_pickable"));
241 }
242
add_shape(const Ref<Shape2D> & p_shape,const Matrix32 & p_transform)243 void CollisionObject2D::add_shape(const Ref<Shape2D> &p_shape, const Matrix32 &p_transform) {
244
245 ERR_FAIL_COND(p_shape.is_null());
246
247 ShapeData sdata;
248 sdata.shape = p_shape;
249 sdata.xform = p_transform;
250 sdata.trigger = false;
251
252 if (area)
253 Physics2DServer::get_singleton()->area_add_shape(get_rid(), p_shape->get_rid(), p_transform);
254 else
255 Physics2DServer::get_singleton()->body_add_shape(get_rid(), p_shape->get_rid(), p_transform);
256
257 shapes.push_back(sdata);
258 }
get_shape_count() const259 int CollisionObject2D::get_shape_count() const {
260
261 return shapes.size();
262 }
set_shape(int p_shape_idx,const Ref<Shape2D> & p_shape)263 void CollisionObject2D::set_shape(int p_shape_idx, const Ref<Shape2D> &p_shape) {
264
265 ERR_FAIL_INDEX(p_shape_idx, shapes.size());
266 ERR_FAIL_COND(p_shape.is_null());
267
268 shapes[p_shape_idx].shape = p_shape;
269 if (area)
270 Physics2DServer::get_singleton()->area_set_shape(get_rid(), p_shape_idx, p_shape->get_rid());
271 else
272 Physics2DServer::get_singleton()->body_set_shape(get_rid(), p_shape_idx, p_shape->get_rid());
273
274 // _update_shapes();
275 }
276
set_shape_transform(int p_shape_idx,const Matrix32 & p_transform)277 void CollisionObject2D::set_shape_transform(int p_shape_idx, const Matrix32 &p_transform) {
278
279 ERR_FAIL_INDEX(p_shape_idx, shapes.size());
280 shapes[p_shape_idx].xform = p_transform;
281
282 if (area)
283 Physics2DServer::get_singleton()->area_set_shape_transform(get_rid(), p_shape_idx, p_transform);
284 else
285 Physics2DServer::get_singleton()->body_set_shape_transform(get_rid(), p_shape_idx, p_transform);
286
287 // _update_shapes();
288 }
289
get_shape(int p_shape_idx) const290 Ref<Shape2D> CollisionObject2D::get_shape(int p_shape_idx) const {
291
292 ERR_FAIL_INDEX_V(p_shape_idx, shapes.size(), Ref<Shape2D>());
293 return shapes[p_shape_idx].shape;
294 }
get_shape_transform(int p_shape_idx) const295 Matrix32 CollisionObject2D::get_shape_transform(int p_shape_idx) const {
296
297 ERR_FAIL_INDEX_V(p_shape_idx, shapes.size(), Matrix32());
298 return shapes[p_shape_idx].xform;
299 }
remove_shape(int p_shape_idx)300 void CollisionObject2D::remove_shape(int p_shape_idx) {
301
302 ERR_FAIL_INDEX(p_shape_idx, shapes.size());
303 shapes.remove(p_shape_idx);
304
305 _update_shapes();
306 }
307
set_shape_as_trigger(int p_shape_idx,bool p_trigger)308 void CollisionObject2D::set_shape_as_trigger(int p_shape_idx, bool p_trigger) {
309
310 ERR_FAIL_INDEX(p_shape_idx, shapes.size());
311 shapes[p_shape_idx].trigger = p_trigger;
312 if (!area && rid.is_valid()) {
313
314 Physics2DServer::get_singleton()->body_set_shape_as_trigger(rid, p_shape_idx, p_trigger);
315 }
316 }
317
is_shape_set_as_trigger(int p_shape_idx) const318 bool CollisionObject2D::is_shape_set_as_trigger(int p_shape_idx) const {
319
320 ERR_FAIL_INDEX_V(p_shape_idx, shapes.size(), false);
321 return shapes[p_shape_idx].trigger;
322 }
323
clear_shapes()324 void CollisionObject2D::clear_shapes() {
325
326 shapes.clear();
327
328 _update_shapes();
329 }
330
CollisionObject2D(RID p_rid,bool p_area)331 CollisionObject2D::CollisionObject2D(RID p_rid, bool p_area) {
332
333 rid = p_rid;
334 area = p_area;
335 pickable = true;
336 if (p_area) {
337
338 Physics2DServer::get_singleton()->area_attach_object_instance_ID(rid, get_instance_ID());
339 } else {
340 Physics2DServer::get_singleton()->body_attach_object_instance_ID(rid, get_instance_ID());
341 }
342 }
343
CollisionObject2D()344 CollisionObject2D::CollisionObject2D() {
345
346 //owner=
347 }
348
~CollisionObject2D()349 CollisionObject2D::~CollisionObject2D() {
350
351 Physics2DServer::get_singleton()->free(rid);
352 }
353