#stats #command #events #bevy #component #automatic #resources

bevy_easy_stats

Simple and easy tool to manage stats in Bevy

1 unstable release

new 0.2.0 Dec 1, 2024

#532 in Game dev

Download history 111/week @ 2024-11-27

111 downloads per month

MIT/Apache

42KB
950 lines

Bevy Easy Stats

A simple and easy way to keep track of stats in one place.

Bevy Version

BES Version Bevy Version
0.2 0.15
0.1 0.14

Usage

1. Create a new stat identifier

pub struct EnemiesKilled;
impl StatIdentifier for EnemiesKilled {
    fn identifier(&self) -> &'static str {
        "Enemies Killed"
    }
}

2. bevy_easy_stats natively supports components and resources as stat collections. These can be automatically updated using built in events and command extensions

#[derive(Resource, Default)]
pub struct ResourceStats {
    stats: Stats,
}
impl AsMut<Stats> for ResourceStats {
    fn as_mut(&mut self) -> &mut Stats {
        &mut self.stats
    }
}

#[derive(Component)]
pub struct EntityStats {
    stats: Stats,
}
impl AsMut<Stats> for EntityStats {
    fn as_mut(&mut self) -> &mut Stats {
        &mut self.stats
    }
}

// If you want to take advantage of built in event based modification functionality make sure to register the stat resource in the app
app.register_stat_resource::<ResourceStats>();

3. Modify stats using several different ways to match your needs

fn modify(mut event_writer: EventWriter<ModifyStat<ResourceStats>>, mut commands: Commands) {
  // entity commands ext to modify component based stats
  commands.entity(entity).modify_stat::<EntityStats>(EnemiesKilled, ModificationType::add(5u64));

  /// Gain acces to an object to queue multiple commands across different types for component based stats
  let mut stats = commands.entity_stats::<EntityStats>(entity);
  stats.add(EnemiesKilled, 5u64);
  stats.add(TimePlayed, Duration::new(5, 0));

  // Use events to modify resource based stats
  event_writer.send(ModifyStat::add(EnemiesKilled, 2u64));
  event_writer.send(ModifyStat::add(TimePlayed, Duration::new(5, 0)));
}

4. Create your own stat types to keep track of whatever you want

// new stat data type
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct CropsGrownStat {
    map: HashMap<String, u64>,
}

// Impl the StatData trait
#[typetag::serde]
impl StatData for CropsGrownStat {
  #[doc = " Creates a new instance of the same kind of stat data"]
  fn default(&self) -> Box<dyn StatData> {
    Box::new(CropsGrownStat {
      map: HashMap::default(),
    })
  }
  #[doc = " Adds the given other to this stat data"]
  fn add(&mut self, other: Box<dyn StatData>) {
    if let Some(other) = other.downcast_ref::<CropsGrownStat>() {
      for (id, amount) in other.map.iter() {
        let entry = self.map.entry(id.clone()).or_default();
        *entry += amount;
      }
    }
  }

  #[doc = " Subtracts the given other from this stat data"]
  fn sub(&mut self, other: Box<dyn StatData>) {
    if let Some(other) = other.downcast_ref::<CropsGrownStat>() {
      for (id, amount) in other.map.iter() {
        let entry = self.map.entry(id.clone()).or_default();
          *entry -= amount;
      }
    }
  }
}

event_writer.send(ModifyStat::add(CropsGrown, CropsGrownStat::new(vec![("Potato".to_string(), 5)])));
event_writer.send(ModifyStat::add(CropsGrown, CropsGrownStat::new(vec![("Dandelion".to_string(), 100)])));
stats.get_stat_downcast::<CropsGrownStat>(&CropsGrown).unwrap() = CropsGrownStat::new(vec![("Dandelion".to_string(), 100), ("Potato".to_string(), 5)])

Future

Dependencies

~21–32MB
~518K SLoC