Skip to main content

concinnity_core/components/
fps_counter.rs

1// FpsCounter component schema.
2
3use crate::ecs::asset_id::AssetId;
4use crate::ecs::asset_id::de_opt_asset_ref;
5
6/// Requests a frames-per-second counter; optionally writes it to a
7/// [TextLabel](#textlabel).
8///
9/// Declaring an `FpsCounter` updates the named [TextLabel](#textlabel) with the
10/// current rate once per second. Omit `label` to suppress on-screen display.
11///
12/// To display an FPS overlay, declare a [Font](#font), a
13/// [TextLabel](#textlabel), and an `FpsCounter` that references the label:
14#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
15#[serde(default)]
16#[derive(Default)]
17pub struct FpsCounter {
18    /// A [TextLabel](#textlabel) to update with the current FPS each second.
19    /// Leave unset to suppress on-screen display.
20    #[serde(deserialize_with = "de_opt_asset_ref")]
21    pub label: Option<AssetId>,
22}
23
24#[cfg(test)]
25mod tests {
26    use super::*;
27
28    #[test]
29    fn an_unset_label_suppresses_the_on_screen_readout() {
30        assert!(FpsCounter::default().label.is_none());
31        assert!(
32            serde_json::from_str::<FpsCounter>(r#"{"label":""}"#)
33                .unwrap()
34                .label
35                .is_none()
36        );
37    }
38
39    #[test]
40    fn a_named_label_parses_and_round_trips_through_postcard() {
41        crate::test_support::install_resolvers();
42        let c: FpsCounter = serde_json::from_str(r#"{"label":"fps_chip"}"#).unwrap();
43        assert_eq!(c.label, Some(AssetId(8)));
44
45        let bytes = postcard::to_allocvec(&c).unwrap();
46        let back: FpsCounter = postcard::from_bytes(&bytes).unwrap();
47        assert_eq!(back.label, Some(AssetId(8)));
48    }
49}