17 个版本 (8 个破坏性更新)

0.9.1 2022年5月26日
0.8.0 2022年5月24日

#1344 in 文本处理

每月下载 48 次

MIT/Apache

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引擎!

crates.io docs

特性

  • 简单的ASCII渲染(print_charprint_glyphprint_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