血迹


关于本教程

本教程是免费且开源的,所有代码均使用 MIT 许可证 - 因此您可以随意使用它。 我希望您会喜欢本教程,并制作出色的游戏!

如果您喜欢这个教程并希望我继续写作,请考虑支持我的 Patreon

Hands-On Rust


我们的角色过着“杀人流浪汉 (murder-hobo)”的生活,随意掠夺和杀戮 - 因此,原始的地牢开始变得像停尸房 (charnel house) 也就很合理了。 这也为我们进入未来的章节搭建了桥梁,在未来的章节中,我们将开始向游戏中添加一些粒子和视觉效果(以 ASCII/CP437 格式)。

存储血液

瓦片 (Tile) 要么有血迹,要么没有,因此将它们作为“集合 (set)”附加到地图是有意义的。 因此,在 map.rs 的顶部,我们将包含一个新的存储类型 - HashSet

#![allow(unused)]
fn main() {
use std::collections::HashSet;
}

在地图定义中,我们将包含一个 usize(用于表示瓦片索引)类型的 HashSet 用于血液:

#![allow(unused)]
fn main() {
#[derive(Default, Serialize, Deserialize, Clone)]
pub struct Map {
    pub tiles : Vec<TileType>,
    pub rooms : Vec<Rect>,
    pub width : i32,
    pub height : i32,
    pub revealed_tiles : Vec<bool>,
    pub visible_tiles : Vec<bool>,
    pub blocked : Vec<bool>,
    pub depth : i32,
    pub bloodstains : HashSet<usize>,

    #[serde(skip_serializing)]
    #[serde(skip_deserializing)]
    pub tile_content : Vec<Vec<Entity>>
}
}

在新地图生成器中,我们将初始化它:

#![allow(unused)]
fn main() {
let mut map = Map{
    tiles : vec![TileType::Wall; MAPCOUNT],
    rooms : Vec::new(),
    width : MAPWIDTH as i32,
    height: MAPHEIGHT as i32,
    revealed_tiles : vec![false; MAPCOUNT],
    visible_tiles : vec![false; MAPCOUNT],
    blocked : vec![false; MAPCOUNT],
    tile_content : vec![Vec::new(); MAPCOUNT],
    depth: new_depth,
    bloodstains: HashSet::new()
};
}

渲染血液

我们将通过将瓦片背景更改为深红色来指示血迹。 我们不想让效果太“引人注目”,也不想隐藏瓦片内容 - 因此这应该足够了。 我们也不会显示视觉范围之外的血液,以保持低调。 在 map.rs 中,渲染部分现在看起来像这样:

#![allow(unused)]
fn main() {
if map.revealed_tiles[idx] {
    let glyph;
    let mut fg;
    let mut bg = RGB::from_f32(0., 0., 0.);
    match tile {
        TileType::Floor => {
            glyph = rltk::to_cp437('.');
            fg = RGB::from_f32(0.0, 0.5, 0.5);
        }
        TileType::Wall => {
            glyph = wall_glyph(&*map, x, y);
            fg = RGB::from_f32(0., 1.0, 0.);
        }
        TileType::DownStairs => {
            glyph = rltk::to_cp437('>');
            fg = RGB::from_f32(0., 1.0, 1.0);
        }
    }
    if map.bloodstains.contains(&idx) { bg = RGB::from_f32(0.75, 0., 0.); }
    if !map.visible_tiles[idx] {
        fg = fg.to_greyscale();
        bg = RGB::from_f32(0., 0., 0.); // 不显示视觉范围外的污渍
    }
    ctx.set(x, y, fg, bg, glyph);
}
}

为血腥之神献血 (Blood for the blood god)

现在我们需要向场景中添加血液! 每当有人在瓦片中受到伤害时,我们都会将该瓦片标记为血腥的。 我们将调整 damage_system.rs 中的 DamageSystem 以设置血迹:

#![allow(unused)]
fn main() {
impl<'a> System<'a> for DamageSystem {
    type SystemData = ( WriteStorage<'a, CombatStats>,
                        WriteStorage<'a, SufferDamage>,
                        ReadStorage<'a, Position>,
                        WriteExpect<'a, Map>,
                        Entities<'a> );

    fn run(&mut self, data : Self::SystemData) {
        let (mut stats, mut damage, positions, mut map, entities) = data;

        for (entity, mut stats, damage) in (&entities, &mut stats, &damage).join() {
            stats.hp -= damage.amount.iter().sum::<i32>();
            let pos = positions.get(entity);
            if let Some(pos) = pos {
                let idx = map.xy_idx(pos.x, pos.y);
                map.bloodstains.insert(idx);
            }
        }

        damage.clear();
    }
}
}

如果您 cargo run 运行您的项目,地图将开始显示战斗的迹象!

Screenshot

本章的源代码可以在这里找到

在您的浏览器中使用 WebAssembly 运行本章的示例(需要 WebGL2)


版权 (Copyright) (C) 2019, Herbert Wolverson。