概述
- 学习一段时间Rust,使用Rust构建一些小游戏,加深对Rust的理解,此项目参看了Moeif博主的项目,对Rust的语言进行进一步的深入理解,在此项目中使用specs 和rtlk 两个包,实现ECS架构的小游戏
ECS: 实体与组件
在web开发或者其他开发中面向对象编程,对于游戏开发者使用ECS架构,原因在于当游戏超出原有设计来说,ECS架构有更好扩展性
组件:只承载数据,行为组件,渲染组件等(加载图形的Map)
实体:有多个组件构建
系统: 组件与实体之间的逻辑行为
BaseEntity
Monster
MeleeMob
OrcWarrior
ArcherMob
OrcArcher
比如在面向对象编程过程,如果增加一些功能,需要进行代码差分和抽取逻辑,将一些共同逻辑放入到基础类中,分离代码和抽象逻辑
在ECS架构中 使用组件描述一切东西, 一个兽人,一只狼,一瓶药水,组合要大于继承。
初始化工程
- 定于Position 和 Renderable 组件,在State 状态中定一个World, 在main 函数中现在World中注册所使用到组件,在组件中添加实体
use rltk::{GameState, Rltk, VirtualKeyCode, RGB};
use specs::prelude::*;
use specs_derive::Component;
use std::cmp::{max, min};
// 定义一个 Position Component
#[derive(Component, Debug)]
struct Position {
x: i32,
y: i32,
}
#[derive(Component, Debug)]
struct Renderable {
glyph: rltk::FontCharType,
fg: RGB,
bg: RGB,
}
#[derive(Component, Debug)]
struct Player {}
#[derive(Component)]
struct LeftMover {}
struct LeftWalker {}
impl<'a> System<'a> for LeftWalker {
type SystemData = (ReadStorage<'a, LeftMover>, WriteStorage<'a, Position>);
fn run(&mut self, (lefty, mut pos): Self::SystemData) {
for (_lefty, pos) in (&lefty, &mut pos).join() {
pos.x -= 1;
if pos.x < 0 {
pos.x = 79
};
}
}
}
struct State {
ecs: World,
}
impl State {
fn run_systems(&mut self) {
let mut lw = LeftWalker {};
lw.run_now(&self.ecs);
self.ecs.maintain();
}
}
// Compoenet 组件 有数据变更,需要进行 Apply
impl GameState for State {
fn tick(&mut self, ctx: &mut Rltk) {
ctx.cls();
self.run_systems();
let position = self.ecs.read_storage::<Position>();
let renderables = self.ecs.read_storage::<Renderable>();
// 对所有的组件渲染
for (pos, render) in (&position, &renderables).join() {
ctx.set(pos.x, pos.y, render.fg, render.bg, render.glyph);
}
}
}
fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) {
// 进行移动的位置
let mut positions = ecs.write_storage::<Position>();
let mut players = ecs.write_storage::<Player>();
for(_player, pos) in (&mut players, &mut positions).join(){
pos.x = min(79, max(0, pos.x + delta_x));
pos.y = min(49, max(0, pos.y + delta_y));
}
}
fn player_input(gs: &mut State, ctx: &mut Rltk) {
match ctx.key {
None => {}
Some(key) => match key {
VirtualKeyCode::Left => try_move_player(-1, 0, &mut gs.ecs),
VirtualKeyCode::Right => try_move_player(1, 0, &mut gs.ecs),
VirtualKeyCode::Up => try_move_player(0, -1, &mut gs.ecs),
VirtualKeyCode::Down => try_move_player(0, 1, &mut gs.ecs),
_ => {}
},
}
}
fn main() -> rltk::BError {
use rltk::RltkBuilder;
let context = RltkBuilder::simple80x50()
.with_title("Roguelike Toturial")
.build()?;
let mut gs = State { ecs: World::new() };
gs.ecs.register::<Position>();
gs.ecs.register::<Renderable>();
gs.ecs.register::<LeftMover>();
gs.ecs.register::<Player>();
gs.ecs
.create_entity()
.with(Position { x: 40, y: 25 })
.with(Renderable {
glyph: rltk::to_cp437('@'),
fg: RGB::named(rltk::YELLOW),
bg: RGB::named(rltk::BLACK),
})
.with(Player {})
.build();
for i in 0..10 {
gs.ecs
.create_entity()
.with(Position { x: i * 7, y: 20 })
.with(Renderable {
glyph: rltk::to_cp437('☺'),
fg: RGB::named(rltk::RED),
bg: RGB::named(rltk::BLACK),
})
.with(LeftMover {})
.build();
}
rltk::main_loop(context, gs)
}
生成地宫
- 进行模块化