如何在Bevy中使用Tiled编辑器和Rapier进行多边形碰撞?

huangapple go评论62阅读模式
英文:

How to do polygon collisions in Bevy with Tiled editor and Rapier?

问题

Here is the translated code snippet:

我对游戏开发和Rust都非常陌生,所以可能完全走错了方向。我使用的是bevy 0.10.1bevy_ecs_tilemapbevy_rapier2d以及tiled,以创建一个包含在.tsx文件中指定碰撞的瓦片地图。我的一些瓦片使用多边形定义碰撞对象。我能够使矩形碰撞正常工作,但多边形碰撞的位置错误,有时形状也不对。

```rust
if let Some(object_layer) = layer_tile.collision.clone() {
    let shapes = object_layer
        .object_data()
        .iter()
        .filter_map(|object_data| {
            let pos = Vect::new(object_data.x / 2., -object_data.y / 2.);
            let rot = object_data.rotation;
            let shape = match &object_data.shape {
                tiled::ObjectShape::Rect { width, height } => {
                    Collider::cuboid(width / 2., height / 2.)
                }
                tiled::ObjectShape::Ellipse { width, height } => {
                    Collider::capsule_x(width / 2., height / 2.)
                }
                tiled::ObjectShape::Polyline { points } => {
                    Collider::polyline(
                        points
                            .iter()
                            .map(|(x, y)| Vect::new(*x, *y))
                            .collect(),
                        None,
                    )
                }
                tiled::ObjectShape::Polygon { points } => {
                    Collider::convex_hull(
                        &points
                            .iter()
                            .map(|(x, y)| Vect::new(*x, *y))
                            .collect::<Vec<_>>(),
                    )?
                }
                _ => {
                    return None;
                }
            };
            Some((pos, rot, shape))
        })
        .collect::<Vec<_>>();
    let world_pos = tile_pos.center_in_world(&grid_size, &map_type);
    let transform =
        get_tilemap_center_transform(&map_size, &grid_size, &map_type, 0.)
            * Transform::from_xyz(
                world_pos.x + offset_x,
                world_pos.y - offset_y,
                0.,
            );
    if shapes.len() == 1 {
        let (pos, rot, collider) = shapes[0].clone();
        let transform = transform
            * Transform {
                translation: Vec3::new(pos.x, pos.y, 0.),
                rotation: Quat::from_rotation_x(rot),
                ..Default::default()
            };
        commands
            .entity(tile_entity)
            .insert((collider, TransformBundle::from_transform(transform)));
    } else if shapes.len() > 1 {
        commands.entity(tile_entity).insert((
            Collider::compound(shapes),
            TransformBundle::from_transform(transform),
        ));
    }
}

如何将tiled的多边形碰撞数据转换为Rapier碰撞器?或者我是否在组合不适合在一起的工具?

英文:

I'm very new to game dev and to Rust, so I might be way on the wrong track. I'm using bevy 0.10.1, bevy_ecs_tilemap, bevy_rapier2d, and tiled to make a tilemap with collisions specified in the .tsx file. Some of my tiles use polygons to define the collision objects. I'm able to get the rectangular collisions to work, but the polygon collisions are in the wrong location and sometimes the wrong shape.
如何在Bevy中使用Tiled编辑器和Rapier进行多边形碰撞?

if let Some(object_layer) = layer_tile.collision.clone() {
let shapes = object_layer
.object_data()
.iter()
.filter_map(|object_data| {
let pos = Vect::new(object_data.x / 2., -object_data.y / 2.);
let rot = object_data.rotation;
let shape = match &amp;object_data.shape {
tiled::ObjectShape::Rect { width, height } =&gt; {
Collider::cuboid(width / 2., height / 2.)
}
tiled::ObjectShape::Ellipse { width, height } =&gt; {
Collider::capsule_x(width / 2., height / 2.)
}
tiled::ObjectShape::Polyline { points } =&gt; {
Collider::polyline(
points
.iter()
.map(|(x, y)| Vect::new(*x, *y))
.collect(),
None,
)
}
tiled::ObjectShape::Polygon { points } =&gt; {
Collider::convex_hull(
&amp;points
.iter()
.map(|(x, y)| Vect::new(*x, *y))
.collect::&lt;Vec&lt;_&gt;&gt;(),
)?
}
_ =&gt; {
return None;
}
};
Some((pos, rot, shape))
})
.collect::&lt;Vec&lt;_&gt;&gt;();
let world_pos = tile_pos.center_in_world(&amp;grid_size, &amp;map_type);
let transform =
get_tilemap_center_transform(&amp;map_size, &amp;grid_size, &amp;map_type, 0.)
* Transform::from_xyz(
world_pos.x + offset_x,
world_pos.y - offset_y,
0.,
);
if shapes.len() == 1 {
let (pos, rot, collider) = shapes[0].clone();
let transform = transform
* Transform {
translation: Vec3::new(pos.x, pos.y, 0.),
rotation: Quat::from_rotation_x(rot),
..default()
};
commands
.entity(tile_entity)
.insert((collider, TransformBundle::from_transform(transform)));
} else if shapes.len() &gt; 1 {
commands.entity(tile_entity).insert((
Collider::compound(shapes),
TransformBundle::from_transform(transform),
));
}
}

How do I convert tiled polygon collision data into Rapier colliders? Or am I combining tools that don't belong together?

答案1

得分: 0

Here is the translated code portion:

if let Some(object_layer) = layer_tile.collision.clone() {
    let shapes = object_layer
        .object_data()
        .iter()
        .filter_map(|object_data| {
            let pos = Vect::new(object_data.x, -object_data.y);
            let rot = object_data.rotation;
            match &object_data.shape {
                tiled::ObjectShape::Rect { width, height } => {
                    let shape = Collider::cuboid(width / 2., height / 2.);
                    Some((
                        pos + Vec2::new((-grid_size.x + width) / 2., (grid_size.y - height) / 2.),
                        rot,
                        shape,
                    ))
                }
                tiled::ObjectShape::Ellipse { width, height } => {
                    let shape = Collider::capsule_x(width / 2., height / 2.);
                    Some((
                        pos + Vec2::new((-grid_size.x + width) / 2., (grid_size.y - height) / 2.),
                        rot,
                        shape,
                    ))
                }
                tiled::ObjectShape::Polyline { points } => {
                    let shape = Collider::polyline(
                        points
                            .iter()
                            .map(|(x, y)| Vect::new(*x, -*y))
                            .collect(),
                        None,
                    );
                    Some((
                        pos + Vec2::new(-grid_size.x / 2., grid_size.y / 2.),
                        rot,
                        shape,
                    ))
                }
                tiled::ObjectShape::Polygon { points } => {
                    let shape = Collider::convex_hull(
                        &points
                            .iter()
                            .map(|(x, y)| Vect::new(*x, -*y))
                            .collect::<Vec<_>>(),
                    )?;
                    Some((
                        pos + Vec2::new(-grid_size.x / 2., grid_size.y / 2.),
                        rot,
                        shape,
                    ))
                }
                _ => {
                    return None;
                }
            }
        })
        .collect::<Vec<_>>();
    let world_pos = tile_pos.center_in_world(&grid_size, &map_type);
    let transform =
        get_tilemap_center_transform(&map_size, &grid_size, &map_type, 0.)
            * Transform::from_xyz(world_pos.x + offset_x, world_pos.y - offset_y, 0.);
    if shapes.len() == 1 {
        let (pos, rot, collider) = shapes[0].clone();
        let transform = transform
            * Transform {
                translation: Vec3::new(pos.x, pos.y, 0.),
                rotation: Quat::from_rotation_x(rot),
                ..default()
            };
        commands
            .entity(tile_entity)
            .insert((collider, TransformBundle::from_transform(transform)));
    } else if shapes.len() > 1 {
        commands.entity(tile_entity).insert((
            Collider::compound(shapes),
            TransformBundle::from_transform(transform),
        ));
    }
}

Please note that the translated code assumes the context of Rust programming and uses the same variable and function names as in the original code.

英文:

After much trial and error, I figured out how to do it. The polygons were upside-down and I needed to treat the rectangles different from the polygons. (My tilemap currently doesn't have ellipses or polylines, so I'm assuming they work the same as the other shapes.)

if let Some(object_layer) = layer_tile.collision.clone() {
let shapes = object_layer
.object_data()
.iter()
.filter_map(|object_data| {
let pos = Vect::new(object_data.x, -object_data.y);
let rot = object_data.rotation;
match &amp;object_data.shape {
tiled::ObjectShape::Rect { width, height } =&gt; {
let shape = Collider::cuboid(width / 2., height / 2.);
Some((
pos + Vec2::new(
(-grid_size.x + width) / 2.,
(grid_size.y - height) / 2.,
),
rot,
shape,
))
}
tiled::ObjectShape::Ellipse { width, height } =&gt; {
let shape =
Collider::capsule_x(width / 2., height / 2.);
Some((
pos + Vec2::new(
(-grid_size.x + width) / 2.,
(grid_size.y - height) / 2.,
),
rot,
shape,
))
}
tiled::ObjectShape::Polyline { points } =&gt; {
let shape = Collider::polyline(
points
.iter()
.map(|(x, y)| Vect::new(*x, -*y))
.collect(),
None,
);
Some((
pos + Vec2::new(
-grid_size.x / 2.,
grid_size.y / 2.,
),
rot,
shape,
))
}
tiled::ObjectShape::Polygon { points } =&gt; {
let shape = Collider::convex_hull(
&amp;points
.iter()
.map(|(x, y)| Vect::new(*x, -*y))
.collect::&lt;Vec&lt;_&gt;&gt;(),
)?;
Some((
pos + Vec2::new(
-grid_size.x / 2.,
grid_size.y / 2.,
),
rot,
shape,
))
}
_ =&gt; {
return None;
}
}
})
.collect::&lt;Vec&lt;_&gt;&gt;();
let world_pos = tile_pos.center_in_world(&amp;grid_size, &amp;map_type);
let transform =
get_tilemap_center_transform(&amp;map_size, &amp;grid_size, &amp;map_type, 0.)
* Transform::from_xyz(
world_pos.x + offset_x,
world_pos.y - offset_y,
0.,
);
if shapes.len() == 1 {
let (pos, rot, collider) = shapes[0].clone();
let transform = transform
* Transform {
translation: Vec3::new(pos.x, pos.y, 0.),
rotation: Quat::from_rotation_x(rot),
..default()
};
commands
.entity(tile_entity)
.insert((collider, TransformBundle::from_transform(transform)));
} else if shapes.len() &gt; 1 {
commands.entity(tile_entity).insert((
Collider::compound(shapes),
TransformBundle::from_transform(transform),
));
}
}

huangapple
  • 本文由 发表于 2023年5月25日 21:21:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/76332785.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定