diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp index 13515abed43..dab1861cf5f 100644 --- a/editor/plugins/tile_set_editor_plugin.cpp +++ b/editor/plugins/tile_set_editor_plugin.cpp @@ -89,6 +89,9 @@ void TileSetEditor::_import_node(Node *p_node, Ref p_library) { Vector > collisions; Ref nav_poly; Ref occluder; + bool one_way_ok = true; + Variant one_way_dir; + float one_way_max_depth = 0.0f; for (int j = 0; j < mi->get_child_count(); j++) { @@ -114,12 +117,30 @@ void TileSetEditor::_import_node(Node *p_node, Ref p_library) { } phys_offset -= sb->get_pos(); + + if (one_way_ok) { + Vector2 curr_dir = sb->get_one_way_collision_direction(); + float curr_max_depth = sb->get_one_way_collision_max_depth(); + if (one_way_dir == Variant()) { + one_way_dir = curr_dir; + one_way_max_depth = curr_max_depth; + } else { + if (curr_dir != one_way_dir || curr_max_depth != one_way_max_depth) { + one_way_ok = false; + WARN_PRINT(String("Mismatch in one-way collision parameters for " + child->get_name()).utf8().get_data()); + } + } + } } if (collisions.size()) { p_library->tile_set_shapes(id, collisions); p_library->tile_set_shape_offset(id, -phys_offset); + if (one_way_ok && one_way_dir != Variant()) { + p_library->tile_set_one_way_collision_direction(id, one_way_dir); + p_library->tile_set_one_way_collision_max_depth(id, one_way_max_depth); + } } else { p_library->tile_set_shape_offset(id, Vector2()); } diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index c7e0607a543..565ea89f39a 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -102,7 +102,9 @@ void TileMap::_update_quadrant_space(const RID &p_space) { for (Map::Element *E = quadrant_map.front(); E; E = E->next()) { Quadrant &q = E->get(); - Physics2DServer::get_singleton()->body_set_space(q.body, p_space); + for (int i = q.bodies.size() - 1; i >= 0; i--) { + Physics2DServer::get_singleton()->body_set_space(q.bodies[i], p_space); + } } } @@ -123,7 +125,9 @@ void TileMap::_update_quadrant_transform() { Matrix32 xform; xform.set_origin(q.pos); xform = global_transform * xform; - Physics2DServer::get_singleton()->body_set_state(q.body, Physics2DServer::BODY_STATE_TRANSFORM, xform); + for (int i = q.bodies.size() - 1; i >= 0; i--) { + Physics2DServer::get_singleton()->body_set_state(q.bodies[i], Physics2DServer::BODY_STATE_TRANSFORM, xform); + } if (navigation) { for (Map::Element *E = q.navpoly_ids.front(); E; E = E->next()) { @@ -252,6 +256,22 @@ void TileMap::_fix_cell_transform(Matrix32 &xform, const Cell &p_cell, const Vec xform.elements[2].y += offset.y; } +namespace { + +struct _BodyParams { + Vector2 one_way_direction; + float one_way_max_depth; + + bool operator<(const _BodyParams &p_rhs) const { + return p_rhs.one_way_direction < one_way_direction || p_rhs.one_way_max_depth < one_way_max_depth; + } + + bool operator==(const _BodyParams &p_rhs) const { + return p_rhs.one_way_direction == one_way_direction && p_rhs.one_way_max_depth == one_way_max_depth; + } +}; +} + void TileMap::_update_dirty_quadrants() { if (!pending_update) @@ -290,8 +310,8 @@ void TileMap::_update_dirty_quadrants() { q.canvas_items.clear(); - ps->body_clear_shapes(q.body); - int shape_idx = 0; + Map<_BodyParams, int> params_bodies; + int num_bodies_used = 0; if (navigation) { for (Map::Element *E = q.navpoly_ids.front(); E; E = E->next()) { @@ -448,8 +468,47 @@ void TileMap::_update_dirty_quadrants() { tex->draw_rect_region(canvas_item, rect, r, modulate, c.transpose); } + RID body; + _BodyParams params = { tile_set->tile_get_one_way_collision_direction(c.id), tile_set->tile_get_one_way_collision_max_depth(c.id) }; + Map<_BodyParams, int>::Element *B = params_bodies.find(params); + if (!B) { + if (q.bodies.size() > num_bodies_used) { + // recycle one already existent + body = q.bodies[num_bodies_used]; + // reset it + ps->body_clear_shapes(body); + } else { + // create a new one + body = Physics2DServer::get_singleton()->body_create(use_kinematic ? Physics2DServer::BODY_MODE_KINEMATIC : Physics2DServer::BODY_MODE_STATIC); + Physics2DServer::get_singleton()->body_attach_object_instance_ID(body, get_instance_ID()); + Physics2DServer::get_singleton()->body_set_layer_mask(body, collision_layer); + Physics2DServer::get_singleton()->body_set_collision_mask(body, collision_mask); + Physics2DServer::get_singleton()->body_set_param(body, Physics2DServer::BODY_PARAM_FRICTION, friction); + Physics2DServer::get_singleton()->body_set_param(body, Physics2DServer::BODY_PARAM_BOUNCE, bounce); + q.bodies.push_back(body); + } + + // initialize to match current quadrant + Matrix32 xform; + xform.set_origin(q.pos); + if (is_inside_tree()) { + xform = get_global_transform() * xform; + RID space = get_world_2d()->get_space(); + Physics2DServer::get_singleton()->body_set_space(body, space); + } + Physics2DServer::get_singleton()->body_set_state(body, Physics2DServer::BODY_STATE_TRANSFORM, xform); + + // bookkeep + params_bodies[params] = num_bodies_used; + num_bodies_used++; + } else { + // take the one already set up for this tile's parameters + body = q.bodies[B->get()]; + } + Vector > shapes = tile_set->tile_get_shapes(c.id); + int shape_idx = ps->body_get_shape_count(body); for (int i = 0; i < shapes.size(); i++) { Ref shape = shapes[i]; @@ -465,8 +524,10 @@ void TileMap::_update_dirty_quadrants() { vs->canvas_item_add_set_transform(debug_canvas_item, xform); shape->draw(debug_canvas_item, debug_collision_color); } - ps->body_add_shape(q.body, shape->get_rid(), xform); - ps->body_set_shape_metadata(q.body, shape_idx++, Vector2(E->key().x, E->key().y)); + ps->body_add_shape(body, shape->get_rid(), xform); + ps->body_set_shape_metadata(body, shape_idx++, Vector2(E->key().x, E->key().y)); + ps->body_set_one_way_collision_direction(body, params.one_way_direction); + ps->body_set_one_way_collision_max_depth(body, params.one_way_max_depth); } } @@ -511,6 +572,14 @@ void TileMap::_update_dirty_quadrants() { } } + // keep just as many bodies as needed + for (int i = num_bodies_used; i < q.bodies.size(); i++) { + ps->free(q.bodies[i]); + } + q.bodies.resize(params_bodies.size()); + + //OS::get_singleton()->print("body count: %d (%d)\n", q.bodies.size(), params_bodies.size()); + dirty_quadrant_list.remove(dirty_quadrant_list.first()); quadrant_order_dirty = true; } @@ -577,8 +646,6 @@ void TileMap::_recompute_rect_cache() { Map::Element *TileMap::_create_quadrant(const PosKey &p_qk) { - Matrix32 xform; - //xform.set_origin(Point2(p_qk.x,p_qk.y)*cell_size*quadrant_size); Quadrant q; q.pos = _map_to_world(p_qk.x * _get_quadrant_size(), p_qk.y * _get_quadrant_size()); q.pos += get_cell_draw_offset(); @@ -587,22 +654,7 @@ Map::Element *TileMap::_create_quadrant(cons else if (tile_origin == TILE_ORIGIN_BOTTOM_LEFT) q.pos.y += cell_size.y; - xform.set_origin(q.pos); // q.canvas_item = VisualServer::get_singleton()->canvas_item_create(); - q.body = Physics2DServer::get_singleton()->body_create(use_kinematic ? Physics2DServer::BODY_MODE_KINEMATIC : Physics2DServer::BODY_MODE_STATIC); - Physics2DServer::get_singleton()->body_attach_object_instance_ID(q.body, get_instance_ID()); - Physics2DServer::get_singleton()->body_set_layer_mask(q.body, collision_layer); - Physics2DServer::get_singleton()->body_set_collision_mask(q.body, collision_mask); - Physics2DServer::get_singleton()->body_set_param(q.body, Physics2DServer::BODY_PARAM_FRICTION, friction); - Physics2DServer::get_singleton()->body_set_param(q.body, Physics2DServer::BODY_PARAM_BOUNCE, bounce); - - if (is_inside_tree()) { - xform = get_global_transform() * xform; - RID space = get_world_2d()->get_space(); - Physics2DServer::get_singleton()->body_set_space(q.body, space); - } - - Physics2DServer::get_singleton()->body_set_state(q.body, Physics2DServer::BODY_STATE_TRANSFORM, xform); rect_cache_dirty = true; quadrant_order_dirty = true; @@ -612,7 +664,11 @@ Map::Element *TileMap::_create_quadrant(cons void TileMap::_erase_quadrant(Map::Element *Q) { Quadrant &q = Q->get(); - Physics2DServer::get_singleton()->free(q.body); + for (int i = 0; i < q.bodies.size(); i++) { + Physics2DServer::get_singleton()->free(q.bodies[i]); + } + q.bodies.clear(); + for (List::Element *E = q.canvas_items.front(); E; E = E->next()) { VisualServer::get_singleton()->free(E->get()); @@ -865,7 +921,9 @@ void TileMap::set_collision_layer(uint32_t p_layer) { for (Map::Element *E = quadrant_map.front(); E; E = E->next()) { Quadrant &q = E->get(); - Physics2DServer::get_singleton()->body_set_layer_mask(q.body, collision_layer); + for (int i = q.bodies.size() - 1; i >= 0; i--) { + Physics2DServer::get_singleton()->body_set_layer_mask(q.bodies[i], collision_layer); + } } } @@ -875,7 +933,9 @@ void TileMap::set_collision_mask(uint32_t p_mask) { for (Map::Element *E = quadrant_map.front(); E; E = E->next()) { Quadrant &q = E->get(); - Physics2DServer::get_singleton()->body_set_collision_mask(q.body, collision_mask); + for (int i = q.bodies.size() - 1; i >= 0; i--) { + Physics2DServer::get_singleton()->body_set_collision_mask(q.bodies[i], collision_mask); + } } } @@ -917,7 +977,9 @@ void TileMap::set_collision_friction(float p_friction) { for (Map::Element *E = quadrant_map.front(); E; E = E->next()) { Quadrant &q = E->get(); - Physics2DServer::get_singleton()->body_set_param(q.body, Physics2DServer::BODY_PARAM_FRICTION, p_friction); + for (int i = q.bodies.size() - 1; i >= 0; i--) { + Physics2DServer::get_singleton()->body_set_param(q.bodies[i], Physics2DServer::BODY_PARAM_FRICTION, p_friction); + } } } @@ -932,7 +994,9 @@ void TileMap::set_collision_bounce(float p_bounce) { for (Map::Element *E = quadrant_map.front(); E; E = E->next()) { Quadrant &q = E->get(); - Physics2DServer::get_singleton()->body_set_param(q.body, Physics2DServer::BODY_PARAM_BOUNCE, p_bounce); + for (int i = q.bodies.size() - 1; i >= 0; i--) { + Physics2DServer::get_singleton()->body_set_param(q.bodies[i], Physics2DServer::BODY_PARAM_BOUNCE, p_bounce); + } } } float TileMap::get_collision_bounce() const { diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index 7c47443088d..52823a29416 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -109,7 +109,7 @@ private: Vector2 pos; List canvas_items; - RID body; + Vector bodies; SelfList dirty_list; @@ -131,7 +131,7 @@ private: void operator=(const Quadrant &q) { pos = q.pos; canvas_items = q.canvas_items; - body = q.body; + bodies = q.bodies; cells = q.cells; navpoly_ids = q.navpoly_ids; occluder_instances = q.occluder_instances; @@ -140,7 +140,7 @@ private: : dirty_list(this) { pos = q.pos; canvas_items = q.canvas_items; - body = q.body; + bodies = q.bodies; cells = q.cells; occluder_instances = q.occluder_instances; navpoly_ids = q.navpoly_ids; @@ -232,7 +232,7 @@ public: void set_collision_mask(uint32_t p_mask); uint32_t get_collision_mask() const; - + void set_collision_layer_bit(int p_bit, bool p_value); bool get_collision_layer_bit(int p_bit) const; diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index 8049b9a87c1..50867adc33f 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -59,6 +59,10 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) { tile_set_shape(id, p_value); else if (what == "shapes") _tile_set_shapes(id, p_value); + else if (what == "one_way_collision_direction") + tile_set_one_way_collision_direction(id, p_value); + else if (what == "one_way_collision_max_depth") + tile_set_one_way_collision_max_depth(id, p_value); else if (what == "occluder") tile_set_light_occluder(id, p_value); else if (what == "occluder_offset") @@ -103,6 +107,10 @@ bool TileSet::_get(const StringName &p_name, Variant &r_ret) const { r_ret = tile_get_shape(id); else if (what == "shapes") r_ret = _tile_get_shapes(id); + else if (what == "one_way_collision_direction") + r_ret = tile_get_one_way_collision_direction(id); + else if (what == "one_way_collision_max_depth") + r_ret = tile_get_one_way_collision_max_depth(id); else if (what == "occluder") r_ret = tile_get_light_occluder(id); else if (what == "occluder_offset") @@ -136,6 +144,8 @@ void TileSet::_get_property_list(List *p_list) const { p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "shape_offset")); p_list->push_back(PropertyInfo(Variant::OBJECT, pre + "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape2D", PROPERTY_USAGE_EDITOR)); p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "shapes", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "one_way_collision_direction")); + p_list->push_back(PropertyInfo(Variant::REAL, pre + "one_way_collision_max_depth")); } } @@ -340,6 +350,30 @@ Array TileSet::_tile_get_shapes(int p_id) const { return arr; } +void TileSet::tile_set_one_way_collision_direction(int p_id, Vector2 p_direction) { + + ERR_FAIL_COND(!tile_map.has(p_id)); + tile_map[p_id].one_way_collision_direction = p_direction; +} + +Vector2 TileSet::tile_get_one_way_collision_direction(int p_id) const { + + ERR_FAIL_COND_V(!tile_map.has(p_id), Vector2()); + return tile_map[p_id].one_way_collision_direction; +} + +void TileSet::tile_set_one_way_collision_max_depth(int p_id, float p_max_depth) { + + ERR_FAIL_COND(!tile_map.has(p_id)); + tile_map[p_id].one_way_collision_max_depth = p_max_depth; +} + +float TileSet::tile_get_one_way_collision_max_depth(int p_id) const { + + ERR_FAIL_COND_V(!tile_map.has(p_id), 0.0f); + return tile_map[p_id].one_way_collision_max_depth; +} + Array TileSet::_get_tiles_ids() const { Array arr; diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index 440d567cda8..335161643ca 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -48,6 +48,8 @@ class TileSet : public Resource { Vector2 shape_offset; Rect2i region; Vector > shapes; + Vector2 one_way_collision_direction; + float one_way_collision_max_depth; Vector2 occluder_offset; Ref occluder; Vector2 navigation_polygon_offset; @@ -55,9 +57,8 @@ class TileSet : public Resource { Ref material; Color modulate; - // Default modulate for back-compat explicit Data() - : modulate(1, 1, 1) {} + : one_way_collision_max_depth(0.0f), modulate(1, 1, 1) {} }; Map tile_map; @@ -114,6 +115,12 @@ public: void tile_set_shapes(int p_id, const Vector > &p_shapes); Vector > tile_get_shapes(int p_id) const; + void tile_set_one_way_collision_direction(int p_id, Vector2 p_direction); + Vector2 tile_get_one_way_collision_direction(int p_id) const; + + void tile_set_one_way_collision_max_depth(int p_id, float p_max_depth); + float tile_get_one_way_collision_max_depth(int p_id) const; + void remove_tile(int p_id); bool has_tile(int p_id) const;