Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add one-way collision to tile-set/tile-map (2.1) #8558

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions editor/plugins/tile_set_editor_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ void TileSetEditor::_import_node(Node *p_node, Ref<TileSet> p_library) {
Vector<Ref<Shape2D> > collisions;
Ref<NavigationPolygon> nav_poly;
Ref<OccluderPolygon2D> 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++) {

Expand All @@ -114,12 +117,30 @@ void TileSetEditor::_import_node(Node *p_node, Ref<TileSet> 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());
}
Expand Down
120 changes: 92 additions & 28 deletions scene/2d/tile_map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,9 @@ void TileMap::_update_quadrant_space(const RID &p_space) {
for (Map<PosKey, Quadrant>::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);
}
}
}

Expand All @@ -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<PosKey, Quadrant::NavPoly>::Element *E = q.navpoly_ids.front(); E; E = E->next()) {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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<PosKey, Quadrant::NavPoly>::Element *E = q.navpoly_ids.front(); E; E = E->next()) {
Expand Down Expand Up @@ -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<Ref<Shape2D> > 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<Shape2D> shape = shapes[i];
Expand All @@ -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);
}
}

Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -577,8 +646,6 @@ void TileMap::_recompute_rect_cache() {

Map<TileMap::PosKey, TileMap::Quadrant>::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();
Expand All @@ -587,22 +654,7 @@ Map<TileMap::PosKey, TileMap::Quadrant>::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;
Expand All @@ -612,7 +664,11 @@ Map<TileMap::PosKey, TileMap::Quadrant>::Element *TileMap::_create_quadrant(cons
void TileMap::_erase_quadrant(Map<PosKey, Quadrant>::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<RID>::Element *E = q.canvas_items.front(); E; E = E->next()) {

VisualServer::get_singleton()->free(E->get());
Expand Down Expand Up @@ -865,7 +921,9 @@ void TileMap::set_collision_layer(uint32_t p_layer) {
for (Map<PosKey, Quadrant>::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);
}
}
}

Expand All @@ -875,7 +933,9 @@ void TileMap::set_collision_mask(uint32_t p_mask) {
for (Map<PosKey, Quadrant>::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);
}
}
}

Expand Down Expand Up @@ -917,7 +977,9 @@ void TileMap::set_collision_friction(float p_friction) {
for (Map<PosKey, Quadrant>::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);
}
}
}

Expand All @@ -932,7 +994,9 @@ void TileMap::set_collision_bounce(float p_bounce) {
for (Map<PosKey, Quadrant>::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 {
Expand Down
8 changes: 4 additions & 4 deletions scene/2d/tile_map.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ class TileMap : public Node2D {

Vector2 pos;
List<RID> canvas_items;
RID body;
Vector<RID> bodies;

SelfList<Quadrant> dirty_list;

Expand All @@ -131,7 +131,7 @@ class TileMap : public Node2D {
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;
Expand All @@ -140,7 +140,7 @@ class TileMap : public Node2D {
: 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;
Expand Down Expand Up @@ -232,7 +232,7 @@ class TileMap : public Node2D {

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;

Expand Down
34 changes: 34 additions & 0 deletions scene/resources/tile_set.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -136,6 +144,8 @@ void TileSet::_get_property_list(List<PropertyInfo> *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"));
}
}

Expand Down Expand Up @@ -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;
Expand Down
11 changes: 9 additions & 2 deletions scene/resources/tile_set.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,17 @@ class TileSet : public Resource {
Vector2 shape_offset;
Rect2i region;
Vector<Ref<Shape2D> > shapes;
Vector2 one_way_collision_direction;
float one_way_collision_max_depth;
Vector2 occluder_offset;
Ref<OccluderPolygon2D> occluder;
Vector2 navigation_polygon_offset;
Ref<NavigationPolygon> navigation_polygon;
Ref<CanvasItemMaterial> 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<int, Data> tile_map;
Expand Down Expand Up @@ -114,6 +115,12 @@ class TileSet : public Resource {
void tile_set_shapes(int p_id, const Vector<Ref<Shape2D> > &p_shapes);
Vector<Ref<Shape2D> > 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;
Expand Down