17 个版本 (8 个破坏性更新)
0.9.1 | 2022年5月26日 |
---|---|
0.8.0 | 2022年5月24日 |
#1344 in 文本处理
每月下载 48 次
2MB
4K SLoC
包含 (静态库, 630KB) glfw3.lib, (静态库, 630KB) glfw3_mt.lib, (静态库, 290KB) libglfw3.a, (Windows DLL, 215KB) glfw3.dll, (静态库, 84KB) libglfw3dll.a, (静态库, 31KB) glfw3dll.lib
Scoundrel
带有邪恶意图的roguelike引擎!
特性
- 简单的ASCII渲染(
print_char
、print_glyph
、print_string
函数) - 简单的形状印刷和光栅化(矩形、圆形、布尔运算等)
- 使用底层图操作简单生成地牢
- 数据驱动输入上下文和原始输入检查
- 易于使用带有错误处理的实体-组件-系统存储
- 游戏功能(视野、寻路等)
- 按需渲染(您控制是否发生
redraw
) - 复杂的玩家流程控制
- 文档需要大量更新
- 教程需要重写以与版本 >0.8.0 保持一致
简单示例
主循环
pub fn create_hero() -> Entity {
// our entities are described in terms of their components
World::spawn((
Positioned::at(10, 10), // these are some of the common components
Visible(Glyph::HERO), // that all rendered things will require
Sighted(16), // the next ones are needed only for things with sight
FieldOfView::empty(),
PlayerTag)) // this marks that this is a player entity
}
pub fn main() {
let mut engine = EngineInstance::default();
create_hero();
make_dungeon();
while !should_quit() {
poll_inputs(&mut engine);
if should_redraw() {
clear_screen();
engine.render();
}
}
}
地牢生成
pub fn make_dungeon() -> Entity {
let mut level = MapLevel::default();
// stamp large rooms
stamp_rooms(RoomStampDistribution{
width: 8..13, height: 8..13, max_count: 2, min_distance: 2
}, &mut level);
// stamp smaller rooms
stamp_rooms(RoomStampDistribution{
width: 5..8, height: 5..8, max_count: 20, min_distance: 1
}, &mut level);
// stamp filler rooms
stamp_rooms(RoomStampDistribution{
width: 3..5, height: 3..5, max_count: 300, min_distance: 1
}, &mut level);
// trunk connects all the rooms with no cycles
// shrub contains all the remaining non-intersecting edges between rooms
let (trunk, shrub) = construct_mesh(level.centers.as_slice());
// add all the corridors for the trunk, making sure to connect all the rooms
stamp_corridors(CorridorStampDistribution {
glyph: Glyph::FLOOR,
probability: Percentage::new(100),
filter_lengths: None,
corridor_style: &corridors::straight_edge,
ignore_already_connected: true,
}, &trunk, &mut level);
// add 50% of the rest of the corridors, to make the dungeon more fun
stamp_corridors(CorridorStampDistribution {
glyph: Glyph::FLOOR,
probability: Percentage::new(50),
filter_lengths: Some(1..8),
corridor_style: &corridors::straight_edge,
ignore_already_connected: true,
}, &shrub, &mut level);
// fetch the player entity
let (player_entity, _) = <(PlayerTag,)>::get().unwrap();
// select a walkable point in the level
let at = {
let points = level.walkable.points();
points[rand::usize(0..points.len())]
};
// position the player entity at that point
player_entity.update((Positioned(at),));
World::spawn((CurrentLevelTag, level))
}
游戏系统
pub fn update_fov(player_entity: Entity, level_entity: Entity,
mut level: MapLevel, from: &Point, range: u16) {
assert!(player_entity.has_component::<(FieldOfView,)>());
assert!(level_entity.has_component::<(MapLevel,)>());
let fov = level.fov(from, range);
level_entity.update((level,));
player_entity.update((fov,));
}
pub fn check_player_movement(dir: &Point, flow: &mut Flow) {
if !dir.is_zero() {
// grab these components from the first entity that has them all
let (player_entity, (Positioned(at), Sighted(range), _)) =
<(Positioned, Sighted, PlayerTag)>::get().unwrap();
let (level_entity, (level, _)) =
<(MapLevel, CurrentLevelTag)>::get().unwrap();
let next_pos = *dir + at;
// the player can walk to this location
if level.walkable.get(&next_pos) {
player_entity.update((Positioned(next_pos),));
// update the players field of view
update_fov(player_entity, level_entity, level, &at, range);
// we only redraw the screen once the player makes an action
force_redraw();
}
}
}
依赖项
~17–28MB
~380K SLoC